Observability:从零开始创建 Java 微服务并监控它 (二)

news2024/10/6 6:47:08

这篇文章是继上一篇文章 “Observability:从零开始创建 Java 微服务并监控它 (一)” 的续篇。在上一篇文章中,我们讲述了如何创建一个 Java web 应用,并使用 Filebeat 来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用 APM 来监控应用并监督 web 服务的在线情况。

源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。

摄入指标

指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有 1000 个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/最大值以获得更多指示。此外,这意味着你还需要考虑这些指标的持续时间。你需要每分钟一次还是每 10 秒一次?

为了从不同的角度查看你的应用程序,让我们获取一些指标。在此示例中,我们将使用 Metricbeat Prometheus 模块将数据发送到 Elasticsearch。

我们应用程序中使用的底层库是 micrometer.io,这是一个供应商中立的应用程序指标,结合其 Prometheus 支持来实现基于拉取的模型。你可以使用 Elastic 支持来实现基于推送的模型。这将要求用户在我们的应用程序中存储 Elasticsearch 集群的凭证数据。

向应用程序添加指标

1)将依赖项添加到我们的 build.gradle 文件中。

  // metrics via micrometer
  implementation 'io.micrometer:micrometer-core:1.5.4'
  implementation 'io.micrometer:micrometer-registry-prometheus:1.5.4'
  implementation 'org.apache.commons:commons-lang3:3.11'

2)将 micrometer 插件及其相应的导入添加到我们的 Javalin 应用程序中。

...
import io.javalin.plugin.metrics.MicrometerPlugin;
import io.javalin.core.security.BasicAuthCredentials;
...

Javalin app = Javalin.create(config -> {
   ...
   config.registerPlugin(new MicrometerPlugin());
);

App.java

package de.spinscale.javalin;

import io.javalin.Javalin;
import io.javalin.http.Handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.javalin.plugin.metrics.MicrometerPlugin;
import io.javalin.core.security.BasicAuthCredentials;

public class App {

    private static final Logger logger = LoggerFactory.getLogger(App.class);

    public static void main(String[] args) {
        Javalin app = Javalin.create(config -> {
            config.requestLogger((ctx, executionTimeMs) -> {
                String userAgent = ctx.userAgent() != null ? ctx.userAgent() : "-";
                logger.info("{} {} {} {} \"{}\" {}",
                        ctx.method(), ctx.req.getPathInfo(), ctx.res.getStatus(),
                        ctx.req.getRemoteHost(), userAgent, executionTimeMs.longValue());
            });

            config.registerPlugin(new MicrometerPlugin());
        });

        app.get("/exception", ctx -> {
            throw new IllegalArgumentException("not yet implemented");
        });
        
        app.exception(Exception.class, (e, ctx) -> {
            logger.error("Exception found", e);
            ctx.status(500).result(e.getMessage());
        });

        app.get("/", mainHandler());
        app.start(8000);
    }

    static Handler mainHandler() {
        return ctx -> {
            String userAgent = ctx.userAgent() != null ? ctx.userAgent() : "-";
            logger.info("This is an informative logging message, user agent [{}]", userAgent);
            ctx.result("Absolutely perfect");
        };
    }
}

3)添加一个新的 metrics 端点并确保 BasicAuthCredentials 类也被导入。

final Micrometer micrometer = new Micrometer();
app.get("/metrics", ctx -> {
  ctx.status(404);
  if (ctx.basicAuthCredentialsExist()) {
    final BasicAuthCredentials credentials = ctx.basicAuthCredentials();
    if ("metrics".equals(credentials.getUsername()) && "secret".equals(credentials.getPassword())) {
      ctx.status(200).result(micrometer.scrape());
    }
  }
});

在这里,Micrometer 类是一个名为 Micrometer.java 的自写类,它设置了几个指标监视器并为 Prometheus 创建了注册表,它提供了基于文本的 Prometheus 输出。

Micrometer.java

package de.spinscale.javalin;

import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.jvm.JvmCompilationMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics;
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics;
import io.micrometer.core.instrument.binder.logging.Log4j2Metrics;
import io.micrometer.core.instrument.binder.system.FileDescriptorMetrics;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.micrometer.core.instrument.binder.system.UptimeMetrics;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;

public class Micrometer {

    final PrometheusMeterRegistry registry = new PrometheusMeterRegistry(new PrometheusConfig() {
        @Override
        public String get(String key) {
            return null;
        }

        @Override
        public String prefix() {
            return "javalin";
        }
    });

    public Micrometer() {
        Metrics.addRegistry(registry);
        new JvmGcMetrics().bindTo(Metrics.globalRegistry);
        new JvmHeapPressureMetrics().bindTo(Metrics.globalRegistry);
        new JvmThreadMetrics().bindTo(Metrics.globalRegistry);
        new JvmCompilationMetrics().bindTo(Metrics.globalRegistry);
        new JvmMemoryMetrics().bindTo(Metrics.globalRegistry);
        new Log4j2Metrics().bindTo(Metrics.globalRegistry);
        new UptimeMetrics().bindTo(Metrics.globalRegistry);
        new FileDescriptorMetrics().bindTo(Metrics.globalRegistry);
        new ProcessorMetrics().bindTo(Metrics.globalRegistry);
    }

    public String scrape() {
        return registry.scrape();
    }
}

4)重新编译你的应用程序并轮询指标端点。

curl localhost:8000/metrics -u metrics:secret

 这将返回基于行的响应,每行一个指标。 这是标准的 Prometheus 格式。

安装和配置 Metricbeat

要将指标发送到 Elasticsearch,需要 Metricbeat。 要下载并安装 Metricbeat,请使用适用于你系统的命令。我们可以到 Elastic 的官方网站 Download Metricbeat • Ship Metrics to Elasticsearch | Elastic 下载和你的 Elasticsearch 同样版本的 Metricbeat。针对我的 macOS,我使用如下的命令来进行下载:

curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-8.5.2-darwin-x86_64.tar.gz
tar xzvf metricbeat-8.5.2-darwin-x86_64.tar.gz

在 Merticbeat 的安装根目录下,我们会发现一个叫做 metricbeat.yml 的配置文件。

$ pwd
/Users/liuxg/elastic/metricbeat-8.5.2-darwin-aarch64
$ ls
LICENSE.txt              kibana                   metricbeat_org.yml
NOTICE.txt               logs                     module
README.md                metricbeat               modules.d
data                     metricbeat.reference.yml prometheus.yml
fields.yml               metricbeat.yml

我们需要来配置这个文件。和之前的 Filebeat 的配置类似,我们采用 API key 来进行配置:

metricbeat.yml

metricbeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: false

name: javalin-metrics-shipper

output.elasticsearch:
  hosts: ["localhost:9200"]
  protocol: "https"

  # Authentication credentials - either API key or username/password.
  api_key: "xZ5uyIQBVVocAV-RwzpW:Szm88RvESbm88Bmk0SYFGQ"
  ssl.certificate_authorities: ["/Users/liuxg/elastic/elasticsearch-8.5.2/config/certs/http_ca.crt"]

processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - add_kubernetes_metadata: ~

在上面,我们使用和 Filbeat 一样的 API key。我们可以使用如下的命令来禁止 system 模块的启用,尽管不必要。

./metricbeat modules disable system
$ ./metricbeat modules disable system
Module system is already disabled

我们需要启动 prometheus 模块。打入如下的命令:

 ./metricbeat modules enable prometheus
$ ./metricbeat modules enable prometheus
Module prometheus is already enabled

我接下来需要多 prometheus 模块进行配置。它的配资文件位于 modules.d 目录下。

vi modules.d/prometheus.yml 

prometheus.yml 

$ pwd
/Users/liuxg/elastic/metricbeat-8.5.2-darwin-aarch64
$ cat  modules.d/prometheus.yml 
- module: prometheus
  period: 10s
  hosts: ["127.0.0.1:8000"]
  metrics_path: /metrics
  username: "metrics"
  password: "secret"
  use_types: true
  rate_counters: true

经过上面的配置后,我们可以使用如下的命令来检查我们的配置是否成功:

./metricbeat test config
$ ./metricbeat test config
Config OK
./metricbeat test output
$ ./metricbeat test output
elasticsearch: https://localhost:9200...
  parse url... OK
  connection...
    parse host... OK
    dns lookup... OK
    addresses: ::1, 127.0.0.1
    dial up... OK
  TLS...
    security: server's certificate chain verification is enabled
    handshake... OK
    TLS version: TLSv1.3
    dial up... OK
  talk to server... OK
  version: 8.5.2

从上面的输出中我们可以看到配置的格式以及连接到 Elasticsearch 都是成功的。我们使用如下的命令来测试 prometheus 模块:

./metricbeat test modules prometheus
$ ./metricbeat test modules prometheus
prometheus...
  collector...OK
    result: 
    {
     "@timestamp": "2022-12-01T06:29:14.806Z",
     "event": {
      "dataset": "prometheus.collector",
      "duration": 36580333,
      "module": "prometheus"
     },
     "metricset": {
      "name": "collector",
      "period": 10000
     },
     "prometheus": {
      "jvm_threads_states_threads": {
       "value": 10
      },
      "labels": {
       "instance": "127.0.0.1:8000",
       "job": "prometheus",
       "state": "runnable"
      }
     },
     "service": {
      "address": "http://127.0.0.1:8000/metrics",
      "type": "prometheus"
     }
    }

它表明我们的模块 prometheus 是成功的。

为了能在 Kibana 中能生产相应的 metricbeat-* index pattern,我们执行如下的命令:

./metricbeat setup
$ ./metricbeat setup
Overwriting ILM policy is disabled. Set `setup.ilm.overwrite: true` for enabling.

Index setup finished.
Loading dashboards (Kibana must be running and reachable)
Loaded dashboards

接下来我们运行 Metricbeat:

sudo chown root metricbeat.yml
sudo chown root modules.d/prometheus.yml 
sudo ./metricbeat -e

注意:你将以 root 身份运行 Metricbeat,因此你需要更改配置文件的所有权,或运行 Metricbeat 指定 --strict.perms=false 。 请参阅配置文件所有权和权限。

验证 Prometheus 事件是否流入 Elasticsearch。

GET metricbeat-*/_search?filter_path=**.prometheus,hits.total
{
  "query": {
    "term": {
      "event.module": "prometheus"
    }
  }
}

在 Kibana 中查看 Metrics

由于这是来自我们 Javalin 应用程序的自定义数据,因此没有用于显示此数据的预定义仪表板。

让我们检查每个 log level 的日志消息数。

GET metricbeat-*/_search
{
  "query": {
    "exists": {
      "field": "prometheus.log4j2_events_total.counter"
    }
  }
}

我们可以通过如下的命令得到文档的个数:

GET metricbeat-*/_search
{
  "query": {
    "range": {
      "prometheus.log4j2_events_total.counter": {
        "gte": 0
      }
    }
  }
}

可视化日志消息的数量随时间的变化,按 log level 划分。 从 Elastic Stack 7.7 开始,有一种创建可视化的新方法,称为 Lens。

 

 

 

随着时间的推移可视化打开的文件

第二个可视化是检查我们的应用程序中打开的文件数。

由于没有人能记住所有字段名称,让我们先再次查看指标输出。

curl -s localhost:8000/metrics -u metrics:secret | grep ^process
$ curl -s localhost:8000/metrics -u metrics:secret | grep ^process
process_files_max_files 10240.0
process_start_time_seconds 1.669871554172E9
process_files_open_files 30.0
process_uptime_seconds 6995.98
process_cpu_usage 6.685723040584351E-4

让我们看一下 process_files_open_files 指标。 这应该是一个很少改变的相当静态的值。 如果你运行在 JVM 中存储数据或打开和关闭网络套接字的应用程序,则此指标会根据负载增加或减少。 对于 Web 应用程序,这是相当静态的。 让我们弄清楚为什么在我们的小型 Web 应用程序中打开了 30 个文件。

运行将在进程列表中包含你的应用程序的 jps。

$ jps
22178 Elasticsearch
17443 GradleServer
80996 Jps
17543 GradleDaemon
7754 Launcher
17930 GradleServer
18029 GradleDaemon
21006 javalin-app-all.jar
7278 
17390 gradle-language-server.jar
22096 CliToolLauncher
17459 org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
19315 GradleDaemon
17907 gradle-language-server.jar
17556 GradleDaemon
51189 Logstash
17945 org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar
17467 GradleDaemon
17628 GradleDaemon

从上面的输出中我们可以看出来 javalin-app-all.jar 对应的 process id 值为 21006。我们使用如下的命令:

lsof -p 21006

你将看到比所有正在打开的文件更多的输出,因为一个文件也是一个正在发生的 TCP 连接。

添加一个端点以通过长时间运行的 HTTP 连接来增加打开文件的数量(每个连接也被视为一个打开的文件,因为它需要一个文件描述符),然后对其运行 wrk。我们在 App.java 中进行如下的修改:

App.java

...
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
...

public static void main(String[] args) {
...
    final Executor executor = CompletableFuture.delayedExecutor(20, TimeUnit.SECONDS);
    app.get("/wait", ctx -> {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "done", executor);
        ctx.result(future);
    });
...

每个 future 都会延迟 20 秒,这意味着单个 HTTP 请求保持打开状态 20 秒。

让我们运行一个 wrk 工作负载。

wrk -c 100 -t 20 -d 5m http://localhost:8000/wait
$ wrk -c 100 -t 20 -d 5m http://localhost:8000/wait
Running 5m test @ http://localhost:8000/wait
  20 threads and 100 connections

结果显示只发送了 20 个请求,考虑到处理时间,这是合理的。现在让我们在 Kibana 中使用 Lens 构建可视化。

 

使用 Infrastrucutre 应用

现在让我们看一下 Kibana 中的 Infrastructure 应用程序。 选择 Observability → Infrastructure

你只会看到来自单个 shipper 的数据。 尽管如此,当你运行多个服务时,并且能够按 Kubernetes pod 或主机对其进行分组的能力使你能够发现 CPU 或内存消耗增加的主机。

 

  

这是 Javalin 应用发出的总事件计数器的面积图。 它正在上升,因为有一个组件轮询一个端点,而该端点又会生成另一条日志消息。 更陡峭的窥探是由于发送了更多的请求。 但是,突然下降是从哪里来的呢? JVM 重启。 由于这些指标不会持久化,因此它们会在 JVM 重启时重置。 考虑到这一点,通常最好记录速率而不是计数器字段。 

检测应用程序

可观察性的第三个部分是应用程序性能管理 (APM)。 APM 设置包括一个接受数据的 APM 服务器(并且已经在我们的 Elastic Cloud 设置中运行)和一个将数据传送到服务器的代理。

代理有两个任务:检测 Java 应用程序以提取应用程序性能信息并将该数据发送到 APM 服务器。

APM 的核心思想之一是能够在整个堆栈中跟踪用户会话的流程,无论你是否有数十个微服务或单体应用来响应你的用户请求。这意味着能够在整个堆栈中标记请求。

要完全捕获用户活动,你需要在用户的浏览器中开始使用真实用户监控 (RUM),直至你的应用程序,该应用程序会向你的数据库发送 SQL 查询。

数据模型

尽管 APM 格局非常分散,但术语通常是相似的。两个最重要的术语是 Span 和 Transactions。更为详细的资料可以参考文章 “Observability:应用程序性能监控/管理(APM)实践”。

Transaction 封装了一系列 span,其中包含有关一段代码执行的信息。让我们看一下 Kibana APM 应用程序的屏幕截图。

这是一个 Spring Boot 应用程序。 UserProfileController.showProfile() 方法被调用,被标记为事务。 里面有两个跨度。 首先,使用 Elasticsearch REST 客户端向 Elasticsearch 发送请求,然后使用 Thymeleaf 呈现响应。 在这种情况下,对 Elasticsearch 的请求比渲染要快。

Java APM 代理可以自动检测特定框架。 Spring 和 Spring Boot 支持很好,上面的数据是在 Spring Boot 应用中添加 agent 创建的; 无需配置。

目前有适用于 Go、.NET、Node、Python、Ruby 和浏览器 (RUM) 的代理。 不断添加代理,因此您可能需要查看 APM 代理文档。

将 APM 代理添加到你的代码

你有两个选项可以将 Java 代理工具添加到您的应用程序中。

首先,你可以在调用 java 二进制文件时通过参数添加代理。 这样,它不会干扰应用程序的打包。 该机制在启动时检测应用程序。

首先下载代理,可以查看最新版本。

你也可以使用如下的命令来进行下载:

wget https://repo1.maven.org/maven2/co/elastic/apm/elastic-apm-agent/1.34.1/elastic-apm-agent-1.34.1.jar

指定启动时的代理以及将 APM 数据发送到的配置参数。 在启动 Java 应用程序之前,让我们为在 Elastic Cloud 中运行的 APM 服务器获取一个 API 密钥。

当你检查你在 Elastic Cloud 中的部署并单击左侧的 APM 时,您将看到可以使用的 APM Server Secret Token。 你也可以从那里复制 APM 端点 URL。

java -javaagent:/path/to/elastic-apm-agent-1.34.1.jar\
  -Delastic.apm.service_name=javalin-app \
  -Delastic.apm.application_packages=de.spinscale.javalin \
  -Delastic.apm.server_urls=$APM_ENDPOINT_URL \
  -Delastic.apm.secret_token=PqWTHGtHZS2i0ZuBol \
  -jar build/libs/javalin-app-all.jar

针对我们的本地部署,我们可以参考之前的文章 “Observability:使用 Elastic Agent 提取应用程序跟踪 - Elastic Stack 8.0” 来安装 Elastic Agent 及 APM 集成。

我们可以通过如下的方式来安装 Elastic Agent 及 Fleet Server:

$ sudo ./elastic-agent install \
>   --fleet-server-es=https://192.168.0.3:9200 \
>   --fleet-server-service-token=AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuLTE2Njk4ODAzNjU3MjA6b29rTGZPRExUSGVwTFpHR0RxNEZfZw \
>   --fleet-server-policy=fleet-server-policy \
>   --fleet-server-es-ca-trusted-fingerprint=07b5d9d9b686daacb996df14a25ef22d2385cfd1b66f1bc5dbfb8f4e1983c64a
Password:
Elastic Agent will be installed at /Library/Elastic/Agent and will run as a service. Do you want to continue? [Y/n]:Y
{"log.level":"info","@timestamp":"2022-12-01T15:41:37.693+0800","log.origin":{"file.name":"cmd/enroll_cmd.go","file.line":403},"message":"Generating self-signed certificate for Fleet Server","ecs.version":"1.6.0"}
{"log.level":"info","@timestamp":"2022-12-01T15:41:40.893+0800","log.origin":{"file.name":"cmd/enroll_cmd.go","file.line":792},"message":"Fleet Server - Starting","ecs.version":"1.6.0"}
{"log.level":"info","@timestamp":"2022-12-01T15:41:44.898+0800","log.origin":{"file.name":"cmd/enroll_cmd.go","file.line":773},"message":"Fleet Server - Running on policy with Fleet Server integration: fleet-server-policy; missing config fleet.agent.id (expected during bootstrap process)","ecs.version":"1.6.0"}
{"log.level":"info","@timestamp":"2022-12-01T15:41:45.187+0800","log.origin":{"file.name":"cmd/enroll_cmd.go","file.line":471},"message":"Starting enrollment to URL: https://liuxgm.local:8220/","ecs.version":"1.6.0"}
{"log.level":"info","@timestamp":"2022-12-01T15:41:46.607+0800","log.origin":{"file.name":"cmd/enroll_cmd.go","file.line":273},"message":"Successfully triggered restart on running Elastic Agent.","ecs.version":"1.6.0"}
Successfully enrolled the Elastic Agent.
Elastic Agent has been successfully installed.

 

 

 

 

 

 

由于我们的 Fleet Server 及 Agents 是在同一个机器上运行的,我们选择 Existing hosts。点击上面的 Save and continue 按钮:

 

 

 

这样我们为 Fleet Server Policy 添加了一个 apm-1 的集成。 

我们接下在 terminal 中运行如下命令:

 

  java -javaagent:./elastic-apm-agent-1.34.1.jar\
  -Delastic.apm.service_name=javalin-app \
  -Delastic.apm.application_packages=de.spinscale.javalin \
  -Delastic.apm.server_urls=http://localhost:8200 \
  -Delastic.apm.secret_token=PqWTHGtHZS2i0ZuBol \
  -jar build/libs/javalin-app-all.jar

你现在可以继续打开 APM UI,你应该会看到流入的数据。

 

 

自动依附

如果你不想更改应用程序的启动选项,独立代理允许您附加到主机上运行的 JVM。

这需要你下载独立的 jar。 你可以在官方文档上找到链接。

要列出本地运行的 Java 应用程序,你可以运行:

java -jar /path/to/apm-agent-attach-1.34.1-standalone.jar --list

由于我通常在我的系统上运行多个 Java 应用程序,因此我指定要附加到的应用程序。 此外,请确保你已经停止了已附加代理的 Javalin 应用程序,并在没有配置附加代理的情况下启动常规 Javalin 应用程序。

java -jar /tmp/apm-agent-attach-1.33.1-standalone.jar --pid 30730 \
  --config service_name=javalin-app \
  --config application_packages=de.spinscale.javalin \
  --config server_urls=$APM_ENDPOINT_URL \
  --config secret_token=PqWTHGtHZS2i0ZuBol

上面的消息将返回如下内容:

2022-12-01 15:04:48.144  INFO Attaching the Elastic {apm-agent} to 30730
2022-12-01 15:04:49.649  INFO Done

因此,现在代理已附加到具有特殊配置的正在运行的应用程序。更多阅读请参阅 “https://elasticstack.blog.csdn.net/article/details/118481955”。

虽然前两种可能性都有效,但你也可以使用第三种方法:将 APM 代理用作直接依赖项。 这允许你在我们的应用程序中编写自定义 span 和 transaction。

程序化设置

编程设置允许你通过源代码中的一行 java 附加代理。

1)添加 java 代理依赖项。

dependencies {
  ...
  implementation 'co.elastic.apm:apm-agent-attach:1.17.0'
  ...
}

2)在我们的 main() 方法开始时检测应用程序。

我们还没有配置任何端点或 API 令牌。 虽然文档建议使用 src/main/resources/elasticapm.properties 文件,但我更喜欢使用环境变量,因为这可以防止将 API 令牌提交到你的源或合并另一个存储库。 保险库等机制允许您以这种方式管理你的秘密。

3)我们在本地项目的根目录中创建一个叫做 .env 的文件。其内容如下:

ELASTIC_APM_SERVICE_NAME=javalin-app
ELASTIC_APM_SERVER_URLS=https://APM_ENDPOINT_URL
ELASTIC_APM_SECRET_TOKEN=PqWTHGtHZS2i0ZuBol

根据我们的配置,.env 的内容如下:

$ pwd
/Users/liuxg/demos/apm/java_monitor
$ cat .env
ELASTIC_APM_SERVICE_NAME=javalin-app
ELASTIC_APM_SERVER_URLS=http://localhost:8220
ELASTIC_APM_SECRET_TOKEN=PqWTHGtHZS2i0ZuBol

4)你现在可以像以前一样运行 java 应用程序。

java -jar build/libs/javalin-app-all.jar

如果你想在你的 IDE 中运行它,你可以手动设置环境变量或搜索支持 .env 文件的插件。

稍等几分钟,让我们最后看一下 APM 应用程序。

 如你所见,这与前面显示的 Spring Boot 应用程序有很大的不同。 未列出不同的端点; 尽管包括错误,但我们可以看到每分钟的请求数。

唯一的 transaction 来自单个 servlet,这不是很有帮助。 让我们尝试通过引入自定义程序化 transaction 来解决这个问题。

定制 transactions 

1)添加另一个依赖项。

dependencies {
  ...
  implementation 'co.elastic.apm:apm-agent-attach:1.17.0'
  implementation 'co.elastic.apm:apm-agent-api:1.17.0'
  ...
}

2)修复 transaction 的名称以包括 HTTP 方法和请求路径:

import co.elastic.apm.api.ElasticApm;

...

app.before(ctx -> ElasticApm.currentTransaction()
  .setName(ctx.method() + " " + ctx.path()));

重新启动你的应用程序并查看流入的数据。 测试几个不同的端点,尤其是抛出异常的端点和触发 404 的端点。

curl http://localhost:8000
curl http://localhost:8000/exception
curl http://localhost:8000/404

我们分别运行上面的命令几次。

 4)添加另一个端点以查看 transaction 的强大功能,该 transaction 轮询另一个 HTTP 服务。 你可能听说过 wttr.in,这是一种从中轮询天气信息的服务。 让我们实现一个将请求转发到该端点的 HTTP 代理方法。 让我们使用 Apache HTTP 客户端,这是最典型的 HTTP 客户端之一。

这是我们的新端点。

import org.apache.http.client.fluent.Request;

...

public static void main(String[] args) {
...

    app.get("/weather/:city", ctx -> {
        String city = ctx.pathParam("city");
        ctx.result(Request.Get("https://wttr.in/" + city + "?format=3").execute()
            .returnContent().asBytes())
            .contentType("text/plain; charset=utf-8");
    });

...

5)我们使用如下的命令来进行测试:

curl http://localhost:8000/weather/Beijing

$ curl http://localhost:8000/weather/Beijing
Beijing: ☀️   -2°C

让我们检查一下 APM UI。

/weather/Beijing 的事务现在包含一个 span,显示检索天气数据所花费的时间。 因为 HTTP 客户端是自动检测的,所以不需要做任何事情。 

如果该 URL 的 city 参数具有高基数,这将导致提及大量 URL 而不是通用端点。 如果你想防止这种情况发生,可以使用 ctx.matchedPath() 将每次对天气 API 的调用记录为 GET /weather/:city。 然而,这需要通过删除 app.before() 处理程序并将其替换为 app.after() 处理程序来进行一些重构。

app.after(ctx -> ElasticApm.currentTransaction().setName(ctx.method()
  + " " + ctx.endpointHandlerPath()));

我们再次运行上面的请求,并再次回到 APM UI 界面。我们这次看到的是:

 

显然这次我们只看到一个 transaction:/weather/:city,而不以每个城市分别命令的 transaction。

设置 Uptime

到目前为止,我们的应用程序中有一些基本的监控功能。 我们索引日志(带跟踪),我们索引指标,我们甚至可以在我们的应用程序中查看以找出单个性能瓶颈,这要归功于 APM。 但是,仍然存在一个弱点。 到目前为止所做的一切都在应用程序中,但所有用户都是通过互联网访问应用程序的。

如何检查我们的用户是否具有我们的 APM 数据向我们建议的相同体验。 想象一下,在你的应用程序前面有一个滞后的负载均衡器,这会使你为每个请求额外花费 50 毫秒。 那将是毁灭性的。 或者 TLS 协商代价高昂。 即使这些外部事件都不是你的错,你仍然会受到影响,应该尝试减轻这些影响。 这意味着您需要先了解它们。

Uptime 不仅可以让你监控服务的可用性,还可以绘制延迟随时间变化的图表,并在 TLS 证书到期时收到通知。

设置

要将正常运行时间数据发送到 Elasticsearch,需要 Heartbeat(轮询组件)。 要下载并安装 Heartbeat,请使用适用于你系统的命令。我们可以到地址 Download Heartbeat • Uptime Monitoring & Elasticsearch | Elastic 下载适合你 Elasticsearch 的版本。针对我的 macOS,我使用如下的命令:

curl -L -O https://artifacts.elastic.co/downloads/beats/heartbeat/heartbeat-8.5.2-darwin-aarch64.tar.gz
tar xzvf heartbeat-8.5.2-darwin-aarch64.tar.gz

和之前的 Metricbeat 及 Filebeat 一样,我们可以使用同样的 API key 来进行配置。当我们解压缩 heartbeat 后,我们需要配置 heartbeat.yml 文件。

heartbeat.yml

ame: heartbeat-shipper

output.elasticsearch:
  hosts: ["localhost:9200"]
  protocol: "https"

  # Authentication credentials - either API key or username/password.
  api_key: "xZ5uyIQBVVocAV-RwzpW:Szm88RvESbm88Bmk0SYFGQ"
  ssl.certificate_authorities: ["/Users/liuxg/elastic/elasticsearch-8.5.2/config/certs/http_ca.crt"]

heartbeat.monitors:
  - type: http
    id: javalin-http-app
    name: "Javalin Web Application"
    urls: ["http://localhost:8000"]
    check.response.status: [200]
    schedule: '@every 15s'

  - type: http
    id: httpbin-get
    name: "httpbin GET"
    urls: ["https://httpbin.org/get"]
    check.response.status: [200]
    schedule: '@every 15s'

  - type: tcp
    id: javalin-tcp
    name: "TCP Port 8000"
    hosts: ["localhost:8000"]
    schedule: '@every 15s'

processors:
  - add_observer_metadata:
      geo:
        name: asia-beijing
        location: "39.923423, 116.405654"

我们使用如下的命令来测试一下:

./heartbeat test config
$ ./heartbeat test config
Config OK
./heartbeat test output
$ ./heartbeat test output
elasticsearch: https://localhost:9200...
  parse url... OK
  connection...
    parse host... OK
    dns lookup... OK
    addresses: ::1, 127.0.0.1
    dial up... OK
  TLS...
    security: server's certificate chain verification is enabled
    handshake... OK
    TLS version: TLSv1.3
    dial up... OK
  talk to server... OK
  version: 8.5.2

我们使用如下的命令来进行配置:

./heartbeat setup
$ ./heartbeat setup
Overwriting ILM policy is disabled. Set `setup.ilm.overwrite: true` for enabling.

Index setup finished.

我们接下来使用如下的命令来启动 heartbeat:

./heartbeat -e

要查看 Uptime 应用程序,请选择 Observability → Uptime。 概述看起来像这样。

 

你可以看到监视器列表和全局概览。让我们看看其中一个警报的详细信息。单击 Javalin Web 应用程序。

您可以看到最后一次计划检查的执行情况,但每次检查的持续时间可能更有趣。你可以查看其中一项检查的延迟是否增加。

通过配置多个在全球范围内运行的 Heartbeats,你可以比较延迟并确定您需要哪个数据中心来运行你的应用程序以靠近你的用户。

监视器的持续时间在几毫秒内,因为它非常快。检查 httpbin.org 端点的监视器,你会看到更长的持续时间。在这种情况下,每个请求大约需要 1000 毫秒。这并不奇怪,因为端点不在附近,并且你需要为每个请求发起 TLS 连接,这是昂贵的。

不要低估这种监控的重要性。此外,考虑这只是一个开始,因为下一步是让合成器监视应用程序的正确行为,例如,以确保你的结帐过程始终有效。

接下我们停止运行 javalin web 应用。稍等一会儿,我们看到:

从图中,我们可以看到有两个服务的运行状态变为红色,也即它们是挂了。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/52450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习3判断机器算法的性能

文章目录一、判断机器算法的性能1基本使用1.目的2.使用pycharm函数封装3.sklearn中的train test split&#xff1a;4.完美调用&#xff1a;二、判断机器算法的性能2分类的准确度&#xff08;accuracy&#xff09;准确度初步计算&#xff1a;完善KNNpy程序如下&#xff1a;一、判…

[附源码]Python计算机毕业设计Django高校社团管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

GHost系统备份与还原

前期准备工作&#xff1a;U盘&#xff08;>8G&#xff09;,最好大一点&#xff0c;如果你U盘要放GHO或者ISO文件的话&#xff0c;可能就不够用了。 我这里使用的老白菜工具&#xff0c;然后制作一个启动U盘。附教程连接&#xff1a;http://laobaicai.bsllcmgs.cn/upqdzz.htm…

无线通信系统简述(学习笔记)

文章目录Evolution of Mobile Radio CommunicationsFirst GenerationSecond GenerationThird GenerationFourth GenerationFifth GenerationOther Wireless Communication SystemsWireless Local Area Networks (WLAN&#xff09;Satellite communication networkWireless Sens…

揭秘如今市场上最火爆的三大商业模式,点进来看看有没有什么收获

大家好&#xff0c;我是爱生活爱分享&#xff0c;无限输出干活内容的阿璋&#xff0c;今天和大家分享一下现在最实用最流行的三大商业模式&#xff0c;现在市面上都有成熟的案例&#xff0c;每个模式都有不同的效果&#xff0c;大家可以看一看&#xff0c;学一学&#xff0c;借…

高通导航器软件开发包使用指南(17)

高通导航器软件开发包使用指南&#xff08;17&#xff09;11 附加的功能11.1 螺旋桨障碍检测11.1.1 螺旋桨起转期间11.1.2 飞行中11.2 低电压警告和迫降11.3 GPS 模式下的 Geotether11.4 禁飞区功能11.5 不允许螺旋桨旋转的传感器检查11.6 仿真模式11.6.1 简介11.6.2 用法12 状…

一种用于入侵检测的自适应集成机器学习模型

一种用于入侵检测的自适应集成机器学习模型学习目标学习内容一&#xff0e;Decision Tree Based Intrusion Detection System for NSL-KDD Dataset二、一种用于入侵检测的自适应集成机器学习模型D. Multi-Tree算法E. 深度神经网络(Deep Neural Networks)算法/多层感知机&#x…

pikachu平台SQL注入

pikachu平台SQL注入 日常心累、速通pikachu注入相关 目录pikachu平台SQL注入使用到的名词解释1. 数字型注入 --使用bp处理数据包2. 字符型注入 --hackbar处理3. 搜索型注入4. xx型注入5. insert/update注入6. delete注入7. http头注入8. 布尔盲注9. 时间盲注10. 宽字节注入使用…

MYSQL 事务、事务隔离级别和MVCC,幻读

快照读和当前读 快照读&#xff1a;快照读就是读取的是快照数据&#xff0c;不加锁的普通 SELECT 都属于快照读。如下面语句&#xff1a; select * from table where ..... 当前读:当前读就是读的是最新数据&#xff0c;而不是历史的数据&#xff0c;加锁的 SELECT&#xff0c…

八、Nacos服务注册和配置中心

SpringCloud Alibaba Nacos服务注册和配置中心 Nacos简介 为什么叫Nacos 前四个字母分别为Naming和Configuration的前两个字母&#xff0c;最后的s为Service 是什么 一个更易于构建云原生应用的动态服务发现&#xff0c;配置管理和服务管理中心 Nacos&#xff1a;Dynamic…

微信

引言&#xff1a;微信必不可少&#xff0c;但用着用着 C 盘内存飙升&#xff0c;不知如何解决&#xff1f;这篇博文来帮你 文章目录一、安装微信二、微信文件默认保存位置的更改三、WeChat Files 有什么&#xff1f;一、安装微信 下载之前先在 D 盘&#xff08;除了 C 盘就行&…

Android Profiler入门与实践

1、内存分析 点击MEMORY&#xff0c;可以看到正在运行进程的各种内存使用情况 Tips&#xff1a;点击右上角的垃圾桶图标会触发强制gc 1.1、查看Java堆内存和Java实例 执行以下代码&#xff1a; 说明&#xff1a;list为MainActivity的全局变量&#xff0c;所以list的只有在M…

数据结构和算法——基于Java——4.1栈(数组实现栈、链表实现栈)

理论补充 先进后出 FILO&#xff08;First-Input-Last-Out&#xff09;的有序列表&#xff0c;限制线性表中元素的插入和删除只能在线性表的同一端进行 栈顶&#xff1a;变化的一端栈底:固定的一端 代码实现 2.1 数组模拟栈 package com.b0.stack;import java.util.Scanner…

[附源码]Python计算机毕业设计SSM考研信息共享博客系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

为ubuntu配置WiFi

1.前提条件 ubuntu不论什么版本&#xff0c;如果想要正常使用wifi&#xff0c;必须在电脑的bios中把secure boot设定为disable 2.快捷指令&#xff08;WiFi相关的常用命令行指令&#xff09; 2.1查看电脑连接的蓝牙和网卡设备 rfkill list all0: hci0: Bluetooth 这个是我电…

79. 单词搜索

79. 单词搜索 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水…

微服务单元测试策略

欢迎关注公众号:TestingStudio,学习更多测试开发必备技能 单元测试对应用程序中最小的可测试软件进行测试&#xff0c;以确定其行为是否如预期的那样。 被测试单元的大小没有严格定义&#xff0c;但是单元测试通常是在类级别或围绕一小组相关的类编写的。被测试的单元越小&…

STM32实战总结:HAL之IAP

我们学习单片机一般都是从51开始的&#xff0c;51单片机烧录程序通常是使用烧录软件如STC-ISP。这种方式&#xff0c;通过串口连接单片机&#xff0c;选择一个合适的波特率就可以烧录了。 后来学习STM32&#xff0c;编程时使用KEIL软件自带的下载按钮就能下载程序&#xff0c;方…

09-13-Hbase-shell入门操作

09-Hbase-shell入门操作&#xff1a; HBase Shell 操作 DDL基本操作 1&#xff0e;进入 HBase 客户端命令行 [roothadoop102 hbase-1.3.1]# bin/hbase shell 2&#xff0e;查看帮助命令 hbase(main):001:0> help 3&#xff0e;查看当前数据库中有哪些表 hbase(main):0…

大数据 | Hadoop、Hive、Spark的关系

文章总括图 数据存储 单机数据库时代 所有数据在单机都能存的下&#xff0c;数据处理的任务都是IO密集型&#xff0c;更谈不上分布式系统 一个典型的2U服务器可以插6块硬盘&#xff0c;每块硬盘4T&#xff0c;共24T原始容量&#xff0c;再加上一些数据包的可用冗余&#xf…