Vert.x,应用监控 - 基于Micrometer / Prometheus

对于企业级的应用程序来说,我们需要通过运行指标(metrics)的监控,来了解(监控)程序的运行状态。Vert.x的核心组件内置了大量的运行指标,并支持通过Micrometer来管理这些运行指标并向后端报告。

目前Vertx内置运行指标的核心组件包括: TCP/HTTP client and servers, DatagramSocket, EventBus and pools,Vertx也支持自定义指标。

Micrometer简介

Micrometer provides a simple facade over the instrumentation clients for a number of popular monitoring systems. Currently, it supports the following monitoring systems: Atlas, Datadog, Graphite, Ganglia, Influx, JMX, and Prometheus.

Micrometer为市场上主流的监控(子)系统提供了一个简单的"门面",目前Micrometer支持的监控系统包括:Atlas, Datadog, Graphite, Ganglia, Influx, JMX, and Prometheus. Micrometer实现了运行指标监控的一个门面设计模式(Facade Pattern),你可以用SLF4J来理解Micrometer,只不过slf4j用于管理程序日志,而micrometer用于管理程序运行指标。门面模式提供了一个统一的,与实现无关的接口,我们只使用接口进行编程,而不依赖具体实现。面模式来简化程序与主流监控系统的整合交互,降低了与他们的耦合。

如果不使用slf4j,我们的程序要同时支持JUL,Log4j,Logback等多种日志系统是很麻烦的,而通过slf4j,我们的程序只需要使用slf4j的标准日志接口(API)来记录日志,而日志记录的具体实现,则在运行时,由slf4j通过配置或者SPI机制适配到具体的日志框架完成。

Micrometer也是一个类似的门面框架,只不过用来采集程序指标的,Micrometer提供了标准的指标采集接口(门面),我们程序只使用这些稳定的标准的API来记录指标,而不用关系后端复杂的实现(如程序指标是上报到Influx? 还是Prometheus? 或者同时上报),通过Micrometer极大的简化了运行指标的采集编程,且我们的程序只依赖稳定的接口,而非变化的具体实现。

Micrometer中,使用Meter接口来抽象一个监控指标,监控指标则由MeterRegistry实例创建和持有。

  • 根据指标类型的不同,Meter实现类包括: Timer, Counter, Gauge, DistributionSummary, LongTaskTimer, FunctionCounter, FunctionTimer, and TimeGauge.
  • 根据指标上报(export)的后端不同,MeterRegistry的实现类包括: SimpleMeterRegistry, CompositeMeterRegistry, AtlasMeterRegistry, JmxMeterRegistry, PrometheusMeterRegistry, …

Micrometer还提供了大量内置针对JVM的监控指标,称之为Binders:

  • 可用于监控JVM类加载的指标(class loader metrics) : ClassLoaderMetrics;
  • 可用于监控JVM内存池的指标(JVM memory pool): JvmMemoryMetrics;
  • 用于监控JVM垃圾收集的指标(GC metrics): JvmGcMetrics;
  • 用于监控线程和CPU使用率的指标(thread and CPU utilization): JvmThreadMetrics, ProcessorMetrics;

Micrometer示例(JMX)

首先必须引入相关依赖:

<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-core</artifactId>
	<version>1.13.6</version>
</dependency>
<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-registry-jmx</artifactId>
	<version>1.13.6</version>
</dependency>

自定义监控指标的编程的大致逻辑是,先创建对应的MeterRegistry实例, 再创建对应的指标实例,并注册到MeterRegistry实例中; 而使用内置的指标这需要创建对应的Binder,并绑定到MeterRegistry实例中; MeterRegistry实例就可以将注册的监控值上报到对应的后端或者直接从MeterRegistry获取监控值。

MeterRegistry registry 
	//= new SimpleMeterRegistry(); // 无后端(保存在内存)
	= new JmxMeterRegistry(JmxConfig.DEFAULT, Clock.SYSTEM); // 后端为JMX

// 创建一个Counter类型指标, 并注册到registry中	
Counter counter1 = Counter.builder("my.counter.metric1")
				.description("indicates periodic timer fire count.")
				.tags("periodic", "Micrometer2")
				.register(registry);

Vertx vertx = Vertx.vertx();
vertx.setPeriodic(4000, timerId -> {
	// timer执行一次,指标值(+1)
	counter1.increment();
});

// 通过registry, 获取指标值
double count = registry.get("my.counter.metric1").counter().count();
LOGGER.info("my.counter.metric1: " + count);

 通过以下方式使用内建的binder实例,并绑定registry
JvmMemoryMetrics jvmMemoryMetrics = new JvmMemoryMetrics();
ProcessorMetrics processorMetrics = new ProcessorMetrics();
jvmMemoryMetrics.bindTo(registry);
processorMetrics.bindTo(registry);

double cpuUsage = registry.get("system.cpu.usage").gauge().value();
 name对应的指标有可能不止一个, 需要根据tag区分; 例如:
double gss = registry.get("jvm.memory.used").tag("area", "heap").tag("id", "G1 Survivor Space").gauge().value();
double geg = registry.get("jvm.memory.used").tag("area", "heap").tag("id", "G1 Eden Space").gauge().value();
double gog = registry.get("jvm.memory.used").tag("area", "heap").tag("id", "G1 Old Gen").gauge().value();
LOGGER.info("CPU Usage%: " + cpuUsage);
LOGGER.info("Memory Heap : \n" 
				+ "  G1 Survivor Space: " + gss 
				+ "\n  G1 Old Gen: " + gog
				+ "\n  G1 Eden Space: " + geg);

 可以通过以下方式遍历registry的所有指标
for (Meter meter : registry.getMeters()) {
	String meterInfo = "";
	Meter.Id meterId = meter.getId();
	//meterId.getDescription();
	meterInfo += (meterId.getName() + ", ");
	meterInfo += (meterId.getType() + ", ");
	meterInfo += (meterId.getTags() + ", ");
	if (meter instanceof Counter) {
		Counter c = (Counter) meter;
		meterInfo += c.count();
	} else if (meter instanceof Gauge) {
		Gauge g = (Gauge) meter;
		meterInfo += g.value();
	} else if (meter instanceof CumulativeFunctionCounter) {
		CumulativeFunctionCounter<?> cc = (CumulativeFunctionCounter<?>) meter;
		meterInfo += cc.count();
	} else {
		meterInfo += meter.toString();
	}
	LOGGER.info(meterInfo);
}

执行结果如下:

2024-11-07 15:51:25 [信息] my.counter.metric1: 30.0
2024-11-07 15:51:25 [信息] CPU Usage%: 0.01603586752462649
2024-11-07 15:51:25 [信息] Memory Heap : 
  G1 Survivor Space: 4194304.0
  G1 Old Gen: 368128.0
  G1 Eden Space: 1.2582912E7
2024-11-07 15:51:29 [信息] my.counter.metric1: 31.0
2024-11-07 15:51:29 [信息] CPU Usage%: 0.04454708016282827
2024-11-07 15:51:29 [信息] Memory Heap : 
  G1 Survivor Space: 4194304.0
  G1 Old Gen: 368128.0
  G1 Eden Space: 1.2582912E7

通过JDK自带的jconsole,可以查看Micrometer上报的监控指标。
在这里插入图片描述

Vert.x Micrometer Metrics

Vert.x支持通过Micrometer将监控指标上报给后端,以上报给Prometheus为例,首先需要引入对应的依赖:

<dependency>
	<groupId>io.vertx</groupId>
	<artifactId>vertx-micrometer-metrics</artifactId>
	<version>4.5.10</version>
</dependency>
<!-- 
注意, Vert.x文档引入的依赖'micrometer-registry-prometheus'是会报错的, 需要使用'micrometer-registry-prometheus-simpleclient'
https://docs.micrometer.io/micrometer/reference/implementations/prometheus.html
Micrometer uses the Prometheus Java Client under the hood; there are two versions of it and Micrometer supports both. If you want to use the "new" client (1.x), use micrometer-registry-prometheus but if you want to use the "legacy" client (0.x), use micrometer-registry-prometheus-simpleclient.

<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-registry-prometheus</artifactId>
	<version>1.13.6</version>
</dependency>
-->
<dependency>
	<groupId>io.micrometer</groupId>
	<artifactId>micrometer-registry-prometheus-simpleclient</artifactId>
	<version>1.13.6</version>
</dependency>

Vertx有两种配置方式,一种是使用内置HTTP服务器将运行指标暴露给prometheus,注意,这种方式内置服务器的端口不要与应用的其他服务器端口相同;因一种是绑定到已有的http服务器中。

内置方式:

VertxPrometheusOptions vertxPrometheusOptions = new VertxPrometheusOptions()
		.setEnabled(true)
		.setStartEmbeddedServer(true)
		.setEmbeddedServerOptions(new HttpServerOptions().setPort(8081))
		.setEmbeddedServerEndpoint("/metrics") // prometheus默认使用的path为/metrics
		
		;
MicrometerMetricsOptions metricsOptions =  new MicrometerMetricsOptions().setPrometheusOptions(vertxPrometheusOptions).setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(metricsOptions));

整合到程序现有http服务器方式:

VertxPrometheusOptions vertxPrometheusOptions = new VertxPrometheusOptions().setEnabled(true);
MicrometerMetricsOptions metricsOptions =  new MicrometerMetricsOptions().setPrometheusOptions(vertxPrometheusOptions).setEnabled(true);
Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(metricsOptions));

Router router = Router.router(vertx);
router.route("/metrics").handler(PrometheusScrapingHandler.create());
vertx.createHttpServer().requestHandler(router).listen(8080);

下面写一个完整的案例:

package vertx.mon;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.system.ProcessorMetrics;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.ext.web.Router;
import io.vertx.micrometer.MicrometerMetricsOptions;
import io.vertx.micrometer.VertxPrometheusOptions;
import io.vertx.micrometer.backends.BackendRegistries;

public class Micrometer2 {
	public static void main(String[] args) {
		VertxPrometheusOptions vertxPrometheusOptions = new VertxPrometheusOptions()
				.setEnabled(true)
				.setStartEmbeddedServer(true)
				.setEmbeddedServerOptions(new HttpServerOptions().setPort(8081))
				.setEmbeddedServerEndpoint("/metrics");
		MicrometerMetricsOptions metricsOptions =  new MicrometerMetricsOptions().setPrometheusOptions(vertxPrometheusOptions).setEnabled(true);
		Vertx vertx = Vertx.vertx(new VertxOptions().setMetricsOptions(metricsOptions));
		
		 添加自定指标,这里使用了micrometer内置的JVM binder:ProcessorMetrics, 包含JVM CPU使用率相关指标。
		MeterRegistry meterRegistry = BackendRegistries.getDefaultNow();
		ProcessorMetrics processorMetrics = new ProcessorMetrics();
		processorMetrics.bindTo(meterRegistry);
		
		HttpServer server = vertx.createHttpServer();
		Router router = Router.router(vertx);
		router.route("/").handler(routingContext -> {
			routingContext.response().end("hello");
		}); 
		server.requestHandler(router).listen(8080);
	}
}

运行程序,可以通过对应端口(8081)和路径(/metrics)查看运行指标(注意,Vertx Http服务器的指标需要实际访问http页面才会出来):
在这里插入图片描述
在prometheus中配置抓取:

  - job_name: "vertx_exporter"
    scrape_interval: 10s
    static_configs:
      - targets: ["172.18.240.1:8081"]

这样,在prometheus就可以存储监控指标:
在这里插入图片描述
后续,还可以根据需要,通过Grafana生成可视化监控图表。

vertx_http_server_response_bytes_count{code="200",method="GET",route="/",} 3.0
vertx_http_server_response_bytes_sum{code="200",method="GET",route="/",} 15.0
vertx_http_server_response_bytes_count{code="404",method="GET",route="",} 1.0
vertx_http_server_response_bytes_sum{code="404",method="GET",route="",} 53.0

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何用PPT画箭头?用这2个ppt软件快速完成绘图!

ppt怎么画箭头&#xff1f; 有时在ppt中绘制流程图或传达承上启下的含义时&#xff0c;会用到箭头形状&#xff0c;运用到箭头元素来增强表达的清晰度和逻辑性。那可能有人会问&#xff0c;ppt怎么画箭头&#xff1f; 这似乎是一个小问题&#xff0c;但如果你对ppt工具不够熟…

java: 无法访问org.springframework.web.bind.annotation.RequestMapping

一、报错问题 java: 无法访问org.springframework.web.bind.annotation.RequestMapping 二、原因分析 SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17。所以仅需要将SpringBoot版本降低为3.0以下即可&#xff08;或…

[ DOS 命令基础 3 ] DOS 命令详解-文件操作相关命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

【TS】九天学会TS语法——3.TypeScript 函数

今天学习 TypeScript 的函数&#xff0c;包括函数类型、可选参数、默认参数、剩余参数。 函数声明和表达式函数类型可选参数和默认参数剩余参数 在 TypeScript 中&#xff0c;函数是编程的核心概念之一。它们允许我们将代码组织成可重用的块&#xff0c;并提供了强大的抽象能力…

Kafka 源码 KRaft 模式本地运行

KRaft&#xff08;Kafka Raft Metadata mode&#xff09;&#xff0c;从版本 2.8.0 开始作为测试特性引入&#xff0c;并在后续版本中持续得到改进和增强。 KRaft 模式是指 Kafka 使用 Raft 协议来管理集群元数据的一种运行模式&#xff0c;这标志着 Kafka 向去除对 ZooKeeper …

day05(单片机)SPI+数码管

目录 SPI数码管 SPI通信 SPI总线介绍 字节交换原理 时序单元 ​​​​​​​SPI模式 模式0 模式1 模式2 模式3 数码管 介绍 74HC595芯片分析 ​​​​​​​原理图分析 ​​​​​​​cubeMX配置​​​​​​​ 程序编写 硬件SPI ​​​​​​​软件SPI 作业&#xff1a; SPI数…

数据结构和算法-贪心算法01- 认识贪心

贪心算法 什么是贪心算法 一个贪心算法总是做出当前最好的选择&#xff0c;也就是说&#xff0c;它期望通过局部最优选择从而得到全局最优的解决方案。 ​ ----《算法导论》 贪心算法(Greedy Method): 所谓贪心算法就是重复地(或贪婪地)根据一个法则挑选解的一部分。当挑选完毕…

创新实践:基于边缘智能+扣子的智慧婴儿监控解决方案

在2024年全国大学生物联网设计竞赛中&#xff0c;火山引擎作为支持企业&#xff0c;不仅参与了赛道的命题设计&#xff0c;还为参赛队伍提供了相关的硬件和软件支持。以边缘智能和扣子的联合应用为核心&#xff0c;参赛者们在这场竞赛中的方案展现出了卓越的创新性和实用性&…

6款IntelliJ IDEA插件,让Spring和Java开发如虎添翼

文章目录 1、SonarLint2、JRebel for IntelliJ3、SwaggerHub插件4、Lombok插件5、RestfulTool插件6、 Json2Pojo插件7、结论 对于任何Spring Boot开发者来说&#xff0c;两个首要的目标是最大限度地提高工作效率和确保高质量代码。IntelliJ IDEA 是目前最广泛使用的集成开发环境…

CSS弹性布局:灵活布局的终极指南

在网页设计中&#xff0c;CSS 弹性布局&#xff08;Flexbox&#xff09;是一个不可或缺的工具。它能帮助你轻松地排列和对齐元素&#xff0c;尤其是在响应式设计中表现出色。今天&#xff0c;我们就来深入探讨一下 Flexbox 的各个属性&#xff0c;让你彻底掌握这个强大的布局工…

论文阅读:Computational Long Exposure Mobile Photography (一)

这篇文章是谷歌发表在 2023 ACM transaction on Graphic 上的一篇文章&#xff0c;介绍如何在手机摄影中实现长曝光的一些拍摄效果。 Abstract 长曝光摄影能拍出令人惊叹的影像&#xff0c;用运动模糊来呈现场景中的移动元素。它通常有两种模式&#xff0c;分别产生前景模糊或…

CTF入门教程(非常详细)从零基础入门到竞赛,看这一篇就够了!

目录 一、CTF简介 二、CTF竞赛模式 三、CTF各大题型简介 四、CTF学习路线 4.1、初期 1、htmlcssjs&#xff08;2-3天&#xff09; 2、apachephp &#xff08;4-5天&#xff09; 3、mysql &#xff08;2-3天&#xff09; 4、python (2-3天) 5、burpsuite &#xff08;…

linux 进程调度学习笔记

https://zhuanlan.zhihu.com/p/1248579228 吐血整理 | 肝翻 Linux 进程调度所有知识点 执行调度 Kernel 判断当前进程标记是否为 TIF_NEED_RESCHED&#xff0c;是的话调用 schedule 函数&#xff0c;执行调度&#xff0c;切换上下文&#xff0c;这也是上面抢占(preempt)机制的…

django图书管理系统-计算机毕业设计源码00648

摘要 图书管理系统在数字化阅读趋势、图书馆自动化管理、用户体验需求和信息技术应用等方面具有重要的研究意义。图书馆自动化管理系统的引入和应用提高了图书借阅过程的效率和准确性&#xff0c;减少了对手工操作和纸质记录的需求。用户对系统的易用性、查询速度、借还流程有更…

SQL实战训练之,力扣:2020. 无流量的帐户数(递归)

目录 一、力扣原题链接 二、题目描述 三、建表语句 四、题目分析 五、SQL解答 六、最终答案 七、验证 八、知识点 一、力扣原题链接 2020. 无流量的帐户数 二、题目描述 表: Subscriptions ------------------- | Column Name | Type | ------------------- | accoun…

ARM base instruction -- ccmp (immediate)

Conditional Compare (immediate) sets the value of the condition flags to the result of the comparison of a register value and an immediate value if the condition is TRUE, and an immediate value otherwise. 此指令一般出现在 cmp 指令之后&#xff0c;表示双重比…

【支付行业-支付系统架构及总结】

记得第一次看埃隆马斯克&#xff08;Elon Musk&#xff09;讲第一性原理的视频时&#xff0c;深受震撼&#xff0c;原来还可以这样处理复杂的事务。这篇文章也尝试化繁为简&#xff0c;探寻支付系统的本质&#xff0c;讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付…

【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、锁分类

目录 一、问题综述 1. 进程和线程的区别&#xff1f; 2. 进程的状态有哪些&#xff1f; 3. 进程之间的通信方式? &#xff08;1&#xff09;管道 &#xff08;2&#xff09;消息队列 &#xff08;3&#xff09;共享内存 &#xff08;4&#xff09;信号量 &#xff08…

Java算法OJ(6)归并分治

目录 1.前言 2.正文 2.1归并分治的概念 2.2计算数组的小和 2.2.1题目 2.2.2示例 2.2.3代码 2.3翻转对 2.3.1题目 2.3.2示例 2.3.3代码 3.小结 1.前言 哈喽大家好吖&#xff0c;今天继续来给大家带来Java算法——归并分治的讲解&#xff0c;学习这篇的前提可以先把…

【网络】自定义协议——序列化和反序列化

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是序列化和分序列&#xff0c;并且自己能手撕网络版的计算器。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不…