文章目录前言1. 环境准备1.1 开发环境1.2 依赖配置2 基础知识埋点2.1 核心定义2.2 分类多角度2.3 手动埋点定制业务逻辑监控2.3.1 编程式2.3.2 注解式2.4 自动埋点开箱即用3. 代码实现3.1 创建注册表3.2 自动埋点3.3 手动埋点3.4 输出指标3.5 输出示例前言需求说明不依赖Spring/SpringBoot不接入Prometheus、Grafana等监控服务仅使用Micrometer核心包内存内完成埋点、读取指标死循环模拟业务操作生产/消费任务同时绑定JVM内置指标采用内置SimpleMeterRegistry内存注册表仅本地存储不对外推送。1. 环境准备1.1 开发环境工具版本JDK17Maven3.6Micrometer1.17.0本示例仅依赖micrometer-core无需Spring、Prometheus或任何外部服务。1.2 依赖配置创建一个普通Maven工程只引入micrometer核心基础包无任何web、监控导出依赖dependencyManagementdependenciesdependencygroupIdio.micrometer/groupIdartifactIdmicrometer-bom/artifactIdversion1.17.0/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdio.micrometer/groupIdartifactIdmicrometer-core/artifactId/dependency/dependenciesmicrometer-core包micrometer-core严格遵循最小依赖编译期仅依赖JSpecify无其他第三方强制依赖停顿检测功能需要运行时依赖LatencyUtils默认关闭故不默认引入客户端百分位统计需要运行时依赖HdrHistogram不用可直接排除。2 基础知识埋点2.1 核心定义可观测性中的埋点英文通常叫instrumentation或telemetry/data collection是指在系统内植入采集逻辑收集指标、日志、链路追踪等遥测数据以此还原系统内部运行状态的技术手段是可观测性体系数据采集的核心基础。简单来说就是提前在系统各处布下 “信息采集传感器”传感器持续产出指标、调用链路、运行日志三类信息。有了这些数据我们才能看清分布式系统内部运行状况做到快速排查故障、分析性能问题。典型应用场景用户行为分析页面访问、按钮点击、转化漏斗性能监控接口耗时RT、QPS、错误率业务指标订单量、支付金额、活跃用户数故障排查异常堆栈、链路追踪2.2 分类多角度按实现方式可分为类型做法特点自动埋点SDK 自动采集通用信号省事覆盖不到业务细节手动埋点手写采集代码精准但要维护混合埋点两者结合最优实践按数据类型可分为类型采什么例子比喻指标埋点数值统计数量、耗时“今天订单量 1000、平均耗时 200ms”仪表盘日志埋点事件明细记录“用户 A 在 10:23 登录失败”行车记录仪链路埋点调用路径追踪“请求→网关→订单服务→支付服务”导航轨迹按位置层次可分为类型位置典型采集内容前端埋点浏览器 / H5页面浏览、点击、停留时长、转化漏斗移动端埋点App / 小程序启动、崩溃、手势、页面跳转后端埋点服务端接口耗时、QPS、错误率、依赖调用基础设施埋点主机 / 容器 / 中间件CPU、内存、磁盘、网络、JVM、DB 连接池按业务目的可分为类型目的典型场景业务埋点分析用户行为、衡量业务效果注册转化、下单漏斗、功能使用率性能埋点监控系统性能表现接口 RT、页面加载时间、慢查询异常埋点捕获和追踪错误接口报错、崩溃、超时、限流触发安全埋点审计合规、风控登录尝试、越权操作、敏感数据访问按指标类型可分为类型采集形态例子对应 API计数型只增不减的累计值订单总数、错误次数Counter计时型事件的耗时分布接口响应时间Timer分布型数据的分布情况一批请求耗时的 P99DistributionSummary状态型瞬时高低起伏的值当前连接数、内存占用Gauge增量型可增可减的累计值队列长度、积分余额Gauge或自增自减按采集时机可分为类型触发方式例子事件触发型用户/系统动作发生时记录点击按钮、发起请求状态变更型某个状态发生变化时记录订单状态从待支付变已支付周期采集型定时轮询采集当前状态每隔 10 秒抓一次 CPU 使用率异常触发型发生异常时才记录接口超时、抛异常时上报堆栈同一个埋点换个角度就属于不同类别比如记录订单接口的耗时├── 角度一怎么埋手动埋点 ├── 角度二埋什么指标埋点 ├── 角度三埋在哪后端埋点 ├── 角度四为什么性能埋点 ├── 角度五什么形态计时型 └── 角度六什么时候事件触发型2.3 手动埋点定制业务逻辑监控当需要监控特定业务流程如订单支付成功率、特定方法耗时时自动埋点无法满足需求此时需通过代码进行手动埋点。手动埋点又分两类编程式直接调API和注解式AOP拦截。2.3.1 编程式直接用MeterRegistry创建和调用核心分为三步获取→创建→调用。简单示例// 1. 获取 MeterRegistry通常通过依赖注入AutowiredprivateMeterRegistryregistry;// 2. 创建或获取 meter同名同 tag 只创建一次Counterordersregistry.counter(orders.created,channel,app);TimerapiTimerregistry.timer(api.request.duration,endpoint,/order);// 3. 在关键位置埋点调用orders.increment();// Counter: 1apiTimer.record(()-doSomething());// Timer: 自动统计耗时registry.gauge(queue.size,queue,Queue::size);// Gauge: 实时反映队列大小或者用全局静态入口Metrics.counter(orders.created).increment();2.3.2 注解式使用Timed等注解标记在方法或类上Micrometer会通过AOP自动拦截并记录该方法执行耗时。这种方式代码侵入性小适合快速统计接口性能但无法像编程式那样灵活记录业务状态。使用示例Timed(valueorder.create,extraTags{service,order},percentiles{0.5,0.95})publicOrdercreateOrder(MeterTag(keyuserType)StringuserId){// 方法执行时间自动记录到 Timer// 异常时自动打 exceptionXxxException 标签returndoCreate(userId);}Counted(valuepayment.count,recordFailuresOnlytrue)publicvoidpay(StringorderId){// 仅失败时计数标签 resultfailure exception...doPay(orderId);}2.4 自动埋点开箱即用Micrometer提供了多种框架、中间件、数据库等指标集成会自动采集大量基础指标无需编写额外代码。这种方式适用于监控JVM状态、HTTP请求、数据库连接池等通用场景。在Spring Boot使用场景中只需引入spring-boot-starter-actuator和对应的注册表依赖如micrometer-registry-prometheus并通过配置management.endpoints.web.exposure.include暴露监控端点即可 。如果只使用Micrometer核心依赖需要手动创建注册表并绑定指标器即可自动实现埋点采集指标。3. 代码实现完整代码如下publicclassJvmMetricsDemoApplication{publicstaticvoidmain(String[]args)throwsInterruptedException{SimpleMeterRegistryregistrynewSimpleMeterRegistry();Metrics.addRegistry(registry);// 1. 注册 JVM 指标 newJvmMemoryMetrics().bindTo(registry);newJvmGcMetrics().bindTo(registry);newJvmThreadMetrics().bindTo(registry);newProcessorMetrics().bindTo(registry);// 2. 业务对象 业务指标 OrderServiceorderServicenewOrderService();TimerorderTimerregistry.timer(biz.order.create);// 3. 死循环调用业务 System.out.println( JVM 业务指标监控 (每5秒输出一次CtrlC 停止) \n);longlastPrintSystem.currentTimeMillis();while(true){// 调用业务方法自动记录耗时orderTimer.record(orderService::createOrder);// 每 5 秒输出全部指标longnowSystem.currentTimeMillis();if(now-lastPrint5000){lastPrintnow;printAllMetrics(registry);}TimeUnit.MILLISECONDS.sleep(200);}}privatestaticvoidprintAllMetrics(SimpleMeterRegistryregistry){System.out.println(───── LocalTime.now() ─────);registry.getMeters().forEach(meter-meter.measure().forEach(ms-{if(ms.getValue()0){System.out.printf(%-45s %-15s %.2f%n,meter.getId().getName(),ms.getStatistic(),ms.getValue());}}));System.out.println();}/** * 业务对象 */staticclassOrderService{voidcreateOrder(){try{// 模拟业务逻辑耗时 20~100msTimeUnit.MILLISECONDS.sleep((long)(Math.random()*8020));}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}3.1 创建注册表SimpleMeterRegistry是Micrometer内置的内存级注册表所有指标数据仅保存在JVM堆内存中不对外推送、不落盘适合开发调试和单元测试场景。// SimpleMeterRegistry内存注册表不依赖任何外部监控系统MeterRegistryregistrynewSimpleMeterRegistry();通常一个应用只有一个MeterRegistry入口可以使用CompositeMeterRegistry包含多个子Registry实现一次埋点、到处上报。3.2 自动埋点使用bindTo(registry)将JVM内部状态内存、GC、线程、CPU自动绑定为Micrometer指标之后每次读取时实时获取当前值无需手动采样。// 绑定 JVM 内置指标newJvmMemoryMetrics().bindTo(registry);// 堆内存、非堆内存newJvmGcMetrics().bindTo(registry);// GC 次数、耗时newJvmThreadMetrics().bindTo(registry);// 线程数、状态分布newProcessorMetrics().bindTo(registry);// CPU 使用率、CPU 时间3.3 手动埋点业务对象是一个简单的内部类createOrder()模拟20~100ms的业务处理耗时staticclassOrderService{voidcreateOrder(){// 模拟业务逻辑耗时 20~100msTimeUnit.MILLISECONDS.sleep((long)(Math.random()*8020));}}创建一个普通业务对象再用Timer来统计其方法调用耗时。Timer是最常用的业务埋点方式。// 创建业务对象OrderServiceorderServicenewOrderService();// 注册 Timer统计下单耗时TimerorderTimerregistry.timer(biz.order.create);死循环调用业务Timer自动记录每次调用的耗时。longlastPrintSystem.currentTimeMillis();while(true){// 调用业务方法Timer 自动记录耗时orderTimer.record(orderService::createOrder);// 每 5 秒输出全部指标longnowSystem.currentTimeMillis();if(now-lastPrint5000){lastPrintnow;printAllMetrics(registry);}TimeUnit.MILLISECONDS.sleep(200);// 避免 CPU 空转}3.4 输出指标遍历注册表中所有Meter打印每个测量值。过滤零值以减少输出噪音。privatestaticvoidprintAllMetrics(SimpleMeterRegistryregistry){System.out.println(───── LocalTime.now() ─────);registry.getMeters().forEach(meter-meter.measure().forEach(ms-{if(ms.getValue()0){System.out.printf(%-45s %-15s %.2f%n,meter.getId().getName(),ms.getStatistic(),ms.getValue());}}));System.out.println();}3.5 输出示例───── 13:51:45 ───── jvm.memory.used VALUE 16800000.00 ← 堆内存已用 jvm.memory.max VALUE 1073741824.00 ← 堆内存上限 jvm.memory.committed VALUE 163577856.00 ← 堆内存已提交 jvm.threads.live VALUE 11.00 ← 活跃线程 jvm.threads.daemon VALUE 9.00 ← 守护线程 jvm.threads.peak VALUE 11.00 ← 线程峰值 jvm.threads.states VALUE 5.00 ← RUNNABLE 线程 jvm.threads.started COUNT 11.00 ← 累计启动线程 jvm.gc.max.data.size VALUE 4255121408.00 ← GC 数据区上限 process.cpu.usage VALUE 0.08 ← CPU 使用率 process.cpu.time COUNT 4062500000.00 ← CPU 时间(ns) system.cpu.count VALUE 12.00 ← CPU 核心数 biz.order.create COUNT 25.00 ← 下单调用次数 biz.order.create TOTAL_TIME 1.35 ← 下单总耗时(秒) biz.order.create MAX 0.09 ← 下单最大耗时(秒)
Micrometer 系列【4】入门案例
文章目录前言1. 环境准备1.1 开发环境1.2 依赖配置2 基础知识埋点2.1 核心定义2.2 分类多角度2.3 手动埋点定制业务逻辑监控2.3.1 编程式2.3.2 注解式2.4 自动埋点开箱即用3. 代码实现3.1 创建注册表3.2 自动埋点3.3 手动埋点3.4 输出指标3.5 输出示例前言需求说明不依赖Spring/SpringBoot不接入Prometheus、Grafana等监控服务仅使用Micrometer核心包内存内完成埋点、读取指标死循环模拟业务操作生产/消费任务同时绑定JVM内置指标采用内置SimpleMeterRegistry内存注册表仅本地存储不对外推送。1. 环境准备1.1 开发环境工具版本JDK17Maven3.6Micrometer1.17.0本示例仅依赖micrometer-core无需Spring、Prometheus或任何外部服务。1.2 依赖配置创建一个普通Maven工程只引入micrometer核心基础包无任何web、监控导出依赖dependencyManagementdependenciesdependencygroupIdio.micrometer/groupIdartifactIdmicrometer-bom/artifactIdversion1.17.0/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdio.micrometer/groupIdartifactIdmicrometer-core/artifactId/dependency/dependenciesmicrometer-core包micrometer-core严格遵循最小依赖编译期仅依赖JSpecify无其他第三方强制依赖停顿检测功能需要运行时依赖LatencyUtils默认关闭故不默认引入客户端百分位统计需要运行时依赖HdrHistogram不用可直接排除。2 基础知识埋点2.1 核心定义可观测性中的埋点英文通常叫instrumentation或telemetry/data collection是指在系统内植入采集逻辑收集指标、日志、链路追踪等遥测数据以此还原系统内部运行状态的技术手段是可观测性体系数据采集的核心基础。简单来说就是提前在系统各处布下 “信息采集传感器”传感器持续产出指标、调用链路、运行日志三类信息。有了这些数据我们才能看清分布式系统内部运行状况做到快速排查故障、分析性能问题。典型应用场景用户行为分析页面访问、按钮点击、转化漏斗性能监控接口耗时RT、QPS、错误率业务指标订单量、支付金额、活跃用户数故障排查异常堆栈、链路追踪2.2 分类多角度按实现方式可分为类型做法特点自动埋点SDK 自动采集通用信号省事覆盖不到业务细节手动埋点手写采集代码精准但要维护混合埋点两者结合最优实践按数据类型可分为类型采什么例子比喻指标埋点数值统计数量、耗时“今天订单量 1000、平均耗时 200ms”仪表盘日志埋点事件明细记录“用户 A 在 10:23 登录失败”行车记录仪链路埋点调用路径追踪“请求→网关→订单服务→支付服务”导航轨迹按位置层次可分为类型位置典型采集内容前端埋点浏览器 / H5页面浏览、点击、停留时长、转化漏斗移动端埋点App / 小程序启动、崩溃、手势、页面跳转后端埋点服务端接口耗时、QPS、错误率、依赖调用基础设施埋点主机 / 容器 / 中间件CPU、内存、磁盘、网络、JVM、DB 连接池按业务目的可分为类型目的典型场景业务埋点分析用户行为、衡量业务效果注册转化、下单漏斗、功能使用率性能埋点监控系统性能表现接口 RT、页面加载时间、慢查询异常埋点捕获和追踪错误接口报错、崩溃、超时、限流触发安全埋点审计合规、风控登录尝试、越权操作、敏感数据访问按指标类型可分为类型采集形态例子对应 API计数型只增不减的累计值订单总数、错误次数Counter计时型事件的耗时分布接口响应时间Timer分布型数据的分布情况一批请求耗时的 P99DistributionSummary状态型瞬时高低起伏的值当前连接数、内存占用Gauge增量型可增可减的累计值队列长度、积分余额Gauge或自增自减按采集时机可分为类型触发方式例子事件触发型用户/系统动作发生时记录点击按钮、发起请求状态变更型某个状态发生变化时记录订单状态从待支付变已支付周期采集型定时轮询采集当前状态每隔 10 秒抓一次 CPU 使用率异常触发型发生异常时才记录接口超时、抛异常时上报堆栈同一个埋点换个角度就属于不同类别比如记录订单接口的耗时├── 角度一怎么埋手动埋点 ├── 角度二埋什么指标埋点 ├── 角度三埋在哪后端埋点 ├── 角度四为什么性能埋点 ├── 角度五什么形态计时型 └── 角度六什么时候事件触发型2.3 手动埋点定制业务逻辑监控当需要监控特定业务流程如订单支付成功率、特定方法耗时时自动埋点无法满足需求此时需通过代码进行手动埋点。手动埋点又分两类编程式直接调API和注解式AOP拦截。2.3.1 编程式直接用MeterRegistry创建和调用核心分为三步获取→创建→调用。简单示例// 1. 获取 MeterRegistry通常通过依赖注入AutowiredprivateMeterRegistryregistry;// 2. 创建或获取 meter同名同 tag 只创建一次Counterordersregistry.counter(orders.created,channel,app);TimerapiTimerregistry.timer(api.request.duration,endpoint,/order);// 3. 在关键位置埋点调用orders.increment();// Counter: 1apiTimer.record(()-doSomething());// Timer: 自动统计耗时registry.gauge(queue.size,queue,Queue::size);// Gauge: 实时反映队列大小或者用全局静态入口Metrics.counter(orders.created).increment();2.3.2 注解式使用Timed等注解标记在方法或类上Micrometer会通过AOP自动拦截并记录该方法执行耗时。这种方式代码侵入性小适合快速统计接口性能但无法像编程式那样灵活记录业务状态。使用示例Timed(valueorder.create,extraTags{service,order},percentiles{0.5,0.95})publicOrdercreateOrder(MeterTag(keyuserType)StringuserId){// 方法执行时间自动记录到 Timer// 异常时自动打 exceptionXxxException 标签returndoCreate(userId);}Counted(valuepayment.count,recordFailuresOnlytrue)publicvoidpay(StringorderId){// 仅失败时计数标签 resultfailure exception...doPay(orderId);}2.4 自动埋点开箱即用Micrometer提供了多种框架、中间件、数据库等指标集成会自动采集大量基础指标无需编写额外代码。这种方式适用于监控JVM状态、HTTP请求、数据库连接池等通用场景。在Spring Boot使用场景中只需引入spring-boot-starter-actuator和对应的注册表依赖如micrometer-registry-prometheus并通过配置management.endpoints.web.exposure.include暴露监控端点即可 。如果只使用Micrometer核心依赖需要手动创建注册表并绑定指标器即可自动实现埋点采集指标。3. 代码实现完整代码如下publicclassJvmMetricsDemoApplication{publicstaticvoidmain(String[]args)throwsInterruptedException{SimpleMeterRegistryregistrynewSimpleMeterRegistry();Metrics.addRegistry(registry);// 1. 注册 JVM 指标 newJvmMemoryMetrics().bindTo(registry);newJvmGcMetrics().bindTo(registry);newJvmThreadMetrics().bindTo(registry);newProcessorMetrics().bindTo(registry);// 2. 业务对象 业务指标 OrderServiceorderServicenewOrderService();TimerorderTimerregistry.timer(biz.order.create);// 3. 死循环调用业务 System.out.println( JVM 业务指标监控 (每5秒输出一次CtrlC 停止) \n);longlastPrintSystem.currentTimeMillis();while(true){// 调用业务方法自动记录耗时orderTimer.record(orderService::createOrder);// 每 5 秒输出全部指标longnowSystem.currentTimeMillis();if(now-lastPrint5000){lastPrintnow;printAllMetrics(registry);}TimeUnit.MILLISECONDS.sleep(200);}}privatestaticvoidprintAllMetrics(SimpleMeterRegistryregistry){System.out.println(───── LocalTime.now() ─────);registry.getMeters().forEach(meter-meter.measure().forEach(ms-{if(ms.getValue()0){System.out.printf(%-45s %-15s %.2f%n,meter.getId().getName(),ms.getStatistic(),ms.getValue());}}));System.out.println();}/** * 业务对象 */staticclassOrderService{voidcreateOrder(){try{// 模拟业务逻辑耗时 20~100msTimeUnit.MILLISECONDS.sleep((long)(Math.random()*8020));}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}3.1 创建注册表SimpleMeterRegistry是Micrometer内置的内存级注册表所有指标数据仅保存在JVM堆内存中不对外推送、不落盘适合开发调试和单元测试场景。// SimpleMeterRegistry内存注册表不依赖任何外部监控系统MeterRegistryregistrynewSimpleMeterRegistry();通常一个应用只有一个MeterRegistry入口可以使用CompositeMeterRegistry包含多个子Registry实现一次埋点、到处上报。3.2 自动埋点使用bindTo(registry)将JVM内部状态内存、GC、线程、CPU自动绑定为Micrometer指标之后每次读取时实时获取当前值无需手动采样。// 绑定 JVM 内置指标newJvmMemoryMetrics().bindTo(registry);// 堆内存、非堆内存newJvmGcMetrics().bindTo(registry);// GC 次数、耗时newJvmThreadMetrics().bindTo(registry);// 线程数、状态分布newProcessorMetrics().bindTo(registry);// CPU 使用率、CPU 时间3.3 手动埋点业务对象是一个简单的内部类createOrder()模拟20~100ms的业务处理耗时staticclassOrderService{voidcreateOrder(){// 模拟业务逻辑耗时 20~100msTimeUnit.MILLISECONDS.sleep((long)(Math.random()*8020));}}创建一个普通业务对象再用Timer来统计其方法调用耗时。Timer是最常用的业务埋点方式。// 创建业务对象OrderServiceorderServicenewOrderService();// 注册 Timer统计下单耗时TimerorderTimerregistry.timer(biz.order.create);死循环调用业务Timer自动记录每次调用的耗时。longlastPrintSystem.currentTimeMillis();while(true){// 调用业务方法Timer 自动记录耗时orderTimer.record(orderService::createOrder);// 每 5 秒输出全部指标longnowSystem.currentTimeMillis();if(now-lastPrint5000){lastPrintnow;printAllMetrics(registry);}TimeUnit.MILLISECONDS.sleep(200);// 避免 CPU 空转}3.4 输出指标遍历注册表中所有Meter打印每个测量值。过滤零值以减少输出噪音。privatestaticvoidprintAllMetrics(SimpleMeterRegistryregistry){System.out.println(───── LocalTime.now() ─────);registry.getMeters().forEach(meter-meter.measure().forEach(ms-{if(ms.getValue()0){System.out.printf(%-45s %-15s %.2f%n,meter.getId().getName(),ms.getStatistic(),ms.getValue());}}));System.out.println();}3.5 输出示例───── 13:51:45 ───── jvm.memory.used VALUE 16800000.00 ← 堆内存已用 jvm.memory.max VALUE 1073741824.00 ← 堆内存上限 jvm.memory.committed VALUE 163577856.00 ← 堆内存已提交 jvm.threads.live VALUE 11.00 ← 活跃线程 jvm.threads.daemon VALUE 9.00 ← 守护线程 jvm.threads.peak VALUE 11.00 ← 线程峰值 jvm.threads.states VALUE 5.00 ← RUNNABLE 线程 jvm.threads.started COUNT 11.00 ← 累计启动线程 jvm.gc.max.data.size VALUE 4255121408.00 ← GC 数据区上限 process.cpu.usage VALUE 0.08 ← CPU 使用率 process.cpu.time COUNT 4062500000.00 ← CPU 时间(ns) system.cpu.count VALUE 12.00 ← CPU 核心数 biz.order.create COUNT 25.00 ← 下单调用次数 biz.order.create TOTAL_TIME 1.35 ← 下单总耗时(秒) biz.order.create MAX 0.09 ← 下单最大耗时(秒)