目录
在spring1.5x使用micrometer和influxdb时,会引入micrometer-spring-legacy和micrometer-registry-influx两个包,如下maven配置,本文主要介绍下这两个包
1 2 3 4 5 6 7 8 9 10 11 |
<dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-spring-legacy</artifactId> <version>1.0.4</version> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-influx</artifactId> <version>1.0.4</version> </dependency> |
1 Micrometer概述
主要包含计量器(Meter)和计量器的注册(MeterRegistry)组成。
- Meter负责收集指标,每一个api都会创建一个计量器;
- MeterRegistry用于管理计量器,并把所管理的Meter发送到监控中心,每一个监控中心对应一个MeterRegsitry,如InfluxdbMetricRegisty,是负责influxdb监控系统。micrometertor通过CompositeMeterRegistry实现了可以同时支持向多个监控中心发送指标。
1.1 指标计量器
Timer相比其他counter等计量器多了 可以同时收集次数,耗时等指标信息,如果缺少自断,还可以进行自定义Timer
2 micrometer-registry-influx
1、作用
核心就在于提供了一个InfluxMeterRegistry。InfluxMeterRegistry作用就是定时发送指标数据到influxdb,所以如果需要修改influxdb的请求URL中sql的格式,可以修改这个类。因为它继承自MeterRegistry所以还具有组成Meter和使用Meter记录指标作用。参考之前写的InfluxdbReporter,就是发送指标数据到influxdb。
- 注册meter,在什么时候被谁注册?在micrometre-spring-legacy的WebMvcMetricsFilter#filter中
- 使用meter收集数据,在什么时候被谁调用?在micrometre-spring-legacy的WebMvcMetricsFilter#filter中
2、初始化InfluxMeterRegistry
在初始化时,启动一线程,该线程定时的向influxdb发送指标数据。
1 2 3 4 5 6 |
public InfluxMeterRegistry(InfluxConfig config, Clock clock, ThreadFactory threadFactory) { super(config, clock); this.config().namingConvention(new InfluxNamingConvention()); this.config = config; start(threadFactory); } |
3 micrometer-spring-legacy
3.1 收集http request指标
提供了一个WebMvcMetricsFilter过滤器,通过这个过滤器来收集指标。流程如下:
3.1.1 Filter初始化
如何初始化这个Filter,提供了一个配置类,自动会加重MeterRegistry类型的对象,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@Configuration @ConditionalOnClass(DispatcherServlet.class) @ConditionalOnWebApplication @EnableConfigurationProperties(MetricsProperties.class) public class ServletMetricsConfiguration { @Bean @ConditionalOnMissingBean(WebMvcTagsProvider.class) public DefaultWebMvcTagsProvider servletTagsProvider() { return new DefaultWebMvcTagsProvider(); } /** * 自动加载MeterRegistry对象,这里会加载一个CompositeMeterRegistry,当我们引入了micrometer-registry-influx之后,它也会被注册到CompositeMeterRegistry中。 */ @SuppressWarnings("deprecation") @Bean public WebMvcMetricsFilter webMetricsFilter(MeterRegistry registry, MetricsProperties properties, WebMvcTagsProvider tagsProvider, WebApplicationContext ctx) { return new WebMvcMetricsFilter(registry, tagsProvider, properties.getWeb().getServer().getRequestsMetricName(), properties.getWeb().getServer().isAutoTimeRequests(), new HandlerMappingIntrospector(ctx)); } } |
生成Filter对象之后,spring boot就会自动加载这个Filter,不需要FilterRegistrationBean来加载。https://stackoverflow.com/questions/19825946/how-to-add-a-filter-class-in-spring-boot
3.1.2 收集指标流程
通过WebMvcMetricsFilter的record()操作记录指标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private void record(TimingSampleContext timingContext, HttpServletResponse response, HttpServletRequest request, Object handlerObject, Throwable e) { // 存在注解@timed for (Timed timedAnnotation : timingContext.timedAnnotations) { timingContext.timerSample.stop(Timer.builder(timedAnnotation, metricName) .tags(tagsProvider.httpRequestTags(request, response, handlerObject, e)) .register(registry)); } if (timingContext.timedAnnotations.isEmpty() && autoTimeRequests) { // 在执行记录指标时,如果MeterRegry中没有这个Timer,此时会创建一个。 timingContext.timerSample.stop(Timer.builder(metricName) .tags(tagsProvider.httpRequestTags(request, response, handlerObject, e)) .register(registry)); } for (LongTaskTimer.Sample sample : timingContext.longTaskTimerSamples) { sample.stop(); } } |
1、WebMvcTagsProvider负责成tags
在DefaultWebMvcTagsProvider中生成uri、exception、status、method的tags。这些tags的值会组成一个标志Metert的唯一键,假设每一个唯一键对应一个Timer,此时uri、exception、status、method有多少种组合,就有多少个Meter。
1 2 3 4 5 6 7 8 |
@Override public Iterable<Tag> httpRequestTags(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler, @Nullable Throwable ex) { return Arrays.asList(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(ex), WebMvcTags.status(response)); } |
在influxdb中查看tags
1 2 |
http_server_requests,exception=None,method=GET,metric_type=histogram,status=200,uri=/health http_server_requests,exception=None,method=GET,metric_type=histogram,status=200,uri=/helloJson |
2、创建Timer
通过Timer#register根据Tags 来生成一个Timer。一组Tag标识一个Timer,如Influxdb中就是一个Seriries就对应一个Timer,如下对应四个Timer。
1 2 3 4 5 6 7 8 9 |
/////////////// /// Timer.java ///////////// public Timer register(MeterRegistry registry) { // the base unit for a timer will be determined by the monitoring system implementation return registry.timer(new Meter.Id(name, tags, null, description, Type.TIMER), distributionConfigBuilder.build(), pauseDetector == null ? registry.config().pauseDetector() : pauseDetector); } |
tag内容如下:
1 2 3 4 |
http_server_requests,exception=None,method=GET,metric_type=histogram,status=200,uri=/health http_server_requests,exception=None,method=GET,metric_type=histogram,status=500,uri=/health http_server_requests,exception=None,method=GET,metric_type=histogram,status=200,uri=/helloJson http_server_requests,exception=None,method=GET,metric_type=histogram,status=500,uri=/helloJson |
最终是通过Timer中MetricRegistry来实现,在MeterRegistry中保存了Meter集合,如下
1 |
private volatile PMap<Id, Meter> meterMap = HashTreePMap.empty(); |
对于Id的hash值是由name和tags的组合而成,如下
1 2 3 4 5 6 |
### Meter#Id 类中hashcode()方法 @Override public int hashCode() { return Objects.hash(name, tags); } |
通过MeterRegistry来创建一个Timer(Timer的父类是Meter)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
###### MeterRegistry ######## private <M extends Meter> M registerMeterIfNecessary(Class<M> meterClass, Meter.Id id, @Nullable DistributionStatisticConfig config, BiFunction<Meter.Id, DistributionStatisticConfig, Meter> builder, Function<Meter.Id, M> noopBuilder) { Meter.Id mappedId = id; for (MeterFilter filter : filters) { mappedId = filter.map(mappedId); } //1、判断该Meta是否需要收集指标 if (!accept(id)) { //noinspection unchecked return noopBuilder.apply(id); } ........ // 2查询或者创建一个meter Meter m = getOrCreateMeter(config, builder, mappedId, noopBuilder); ..... //noinspection unchecked return (M) m; } |
3、使用Timer记录指标
如上面的“timingContext.timerSample.stop(xxx)”的stop方法
1 2 3 4 5 |
public long stop(Timer timer) { long durationNs = clock.monotonicTime() - startTime; timer.record(durationNs, TimeUnit.NANOSECONDS); return durationNs; } |
3.2 收集Tomcat指标
3.2.1 初始化
通过@Configuration来加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Configuration @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") public class TomcatMetricsConfiguration { @Bean public TomcatMetrics metrics(ApplicationContext applicationContext) { Manager manager = null; if (applicationContext instanceof EmbeddedWebApplicationContext) { manager = getManagerFromContext((EmbeddedWebApplicationContext) applicationContext); } return new TomcatMetrics(manager, Collections.emptyList()); } .. } |
3.3 保存指标
支持不同监控系统来保存指标,如Influx/Ganglie/Grahite等系统,在如下包中来通过自动化配置类来初始化对应的MeterRegtry,如对于influxdb,就是通过InfluxMetricsExportAutoConfiguration 配置类初始化InfluxMeterRegtry对象。
这里以influxdb为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@Configuration @AutoConfigureBefore({CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class}) @AutoConfigureAfter(MetricsAutoConfiguration.class) @ConditionalOnBean(Clock.class) @ConditionalOnClass(InfluxMeterRegistry.class) @ConditionalOnProperty(prefix = "management.metrics.export.influx", name = "enabled", havingValue = "true", matchIfMissing = true) @EnableConfigurationProperties(InfluxProperties.class) @Import(StringToDurationConverter.class) public class InfluxMetricsExportAutoConfiguration { @Bean @ConditionalOnMissingBean(InfluxConfig.class) public InfluxConfig influxConfig(InfluxProperties props) { return new InfluxPropertiesConfigAdapter(props); } @Bean @ConditionalOnMissingBean public InfluxMeterRegistry influxMeterRegistry(InfluxConfig config, Clock clock) { return new InfluxMeterRegistry(config, clock); } } |
这里存在一个问题:当引入多个micrometer-registry-xx多个监控系统时,在初始化WebMetricsFilter时,注册哪一个MetrRegry???
如下代码,micrometer-spring-legacy会首先加载CompositMeterRegstry,其他监控系统的MeterRegry都会注册到CompositMeterRegestry.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Conditional(CompositeMeterRegistryConfiguration.MultipleNonPrimaryMeterRegistriesCondition.class) class CompositeMeterRegistryConfiguration { // 通过@Primary指定优先使员工CompositMeterRegstry @Bean @Primary public CompositeMeterRegistry compositeMeterRegistry(Clock clock, List<MeterRegistry> registries) { // 加载所有的registries return new CompositeMeterRegistry(clock, registries); } } |
3.4 收集那些指标
如查看Influxdb中收集的Micrometer指标信息,如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
> show measurements; name: measurements name ---- # 1、datasource信息 dataSource_connections_active dataSource_connections_max dataSource_connections_min http_server_requests # 2、jvm信息 jvm_buffer_count jvm_buffer_memory_used jvm_buffer_total_capacity jvm_classes_loaded jvm_classes_unloaded jvm_gc_live_data_size jvm_gc_max_data_size jvm_gc_memory_allocated jvm_gc_memory_promoted jvm_gc_pause jvm_memory_committed jvm_memory_max jvm_memory_used jvm_threads_daemon jvm_threads_live jvm_threads_peak logback_events # 3、process信息 process_cpu_usage process_files_max process_files_open process_start_time process_uptime # 4、系统信息 system_cpu_count system_cpu_usage system_load_average_1m # 5、tomcat信息 tomcat_cache_access tomcat_cache_hit tomcat_global_error tomcat_global_received tomcat_global_request tomcat_global_request_max tomcat_global_sent tomcat_servlet_error tomcat_servlet_request tomcat_servlet_request_max tomcat_threads_busy tomcat_threads_config_max tomcat_threads_current |
目前只介绍了Http Request和Tomcat 的监控信息,对于JVM、DataSoruce、System等信息是如何收集的??通过MeteRegistryPostProcessor来加载micrometer-core中MeterBinder的类,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@NonNullApi public class MeterRegistryPostProcessor implements BeanPostProcessor { ...... @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MeterRegistry) { // 将binders装载到MeterRegistry getConfigurer().configure((MeterRegistry) bean); } return bean; } @SuppressWarnings("unchecked") private MeterRegistryConfigurer getConfigurer() { if (this.configurer == null) { // 加载Mircrometer-core中所有MeterBinder类型的类。 this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class), beansOfType(MeterFilter.class), (Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType( MeterRegistryCustomizer.class), this.context.getBean(MetricsProperties.class).isUseGlobalRegistry()); } return this.configurer; } } |
这个MeterRegistryPostProcessor是在如下配置类进行初始化的
1 2 3 4 5 6 7 8 9 10 11 12 |
@Configuration public class MetricsAutoConfiguration { @Bean @ConditionalOnMissingBean public Clock micrometerClock() { return Clock.SYSTEM; } @Bean public static MeterRegistryPostProcessor meterRegistryPostProcessor(ApplicationContext context) { return new MeterRegistryPostProcessor(context); } |
MeterBinder在MeterBindersConfiguration配置类中加载
1 2 3 4 5 6 7 8 9 10 11 12 |
@Configuration class MeterBindersConfiguration { @Bean @ConditionalOnProperty(value = "management.metrics.binders.jvm.enabled", matchIfMissing = true) @ConditionalOnMissingBean public JvmGcMetrics jvmGcMetrics() { return new JvmGcMetrics(); } ... } |
3.4.1 JVM指标
1、mircormeter自带信息
(1)JvmGcMetrics记录GC的信息
measurement | 描述 | 指标信息 |
jvm_gc_pause | gc总次数、累计耗时、平均耗时、最大时间 :
根据action进行分为两类GC: (1)end of major GC (Full GC) (2)end of minor GC (YGC)
|
1、字段 > select * from jvm_gc_pause limit 3; name: jvm_gc_pause time action cause count mean metric_type sum upper —- —— —– —– —- ———– — —– 1591744518683000000 end of minor GC Metadata GC Threshold 1 27 histogram 27 27 1591744518687000000 end of minor GC Allocation Failure 2 41 histogram 82 49 1591744518694000000 end of major GC Metadata GC Threshold 1 141 histogram 141 141 2、tags字段 > show series from jvm_gc_pause key — jvm_gc_pause,action=end\ of\ major\ GC,cause=Metadata\ GC\ Threshold,metric_type=histogram jvm_gc_pause,action=end\ of\ minor\ GC,cause=Allocation\ Failure,metric_type=histogram jvm_gc_pause,action=end\ of\ minor\ GC,cause=Metadata\ GC\ Threshold,metric_type=histogram |
jvm_gc_max_data_size |
老年代自服务启动之后最大值 |
1、字段值 > select * from jvm_gc_max_data_size limit 2; name: jvm_gc_max_data_size time metric_type value —- ———– —– 1591744517660000000 gauge 1431830528 1591744577134000000 gauge 1431830528 2、tags > show series from jvm_gc_max_data_size key — jvm_gc_max_data_size,metric_type=gauge |
jvm_gc_live_data_size |
full gc 的之后老年代的大小,老年代大小在变化。 | 1、字段
> select * from jvm_gc_live_data_size limit 1; name: jvm_gc_live_data_size time metric_type value —- ———– —– 1591744518676000000 gauge 38198896 2、tags > show series from jvm_gc_live_data_size key — jvm_gc_live_data_size,metric_type=gauge |
jvm_gc_memory_allocated |
两次YGC之间新生代增长大小 |
1、字段 > select * from jvm_gc_memory_allocated limit 1; name: jvm_gc_memory_allocated time metric_type value —- ———– —– 1591744517659000000 counter 1273199040 2、tags > show series from jvm_gc_memory_allocated key — jvm_gc_memory_allocated,metric_type=counter |
jvm_gc_memory_promoted |
两次fullgc之间老年生代增长大小 | 1、字段
> select * from jvm_gc_memory_promoted limit 1; name: jvm_gc_memory_promoted time metric_type value —- ———– —– 1591744518697000000 counter 24576 2、tags > show series from jvm_gc_memory_promoted key — jvm_gc_memory_promoted,metric_type=counter |
(2)jvm线程
measurement | 描述 | 指标新 |
jvm_threads_daemon |
守护线程 |
1、字段 > select * from jvm_threads_daemon limit 1; name: jvm_threads_daemon time metric_type value —- ———– —– 1591744518673000000 gauge 30 2、tags > show series from jvm_threads_daemon key — jvm_threads_daemon,metric_type=gauge |
jvm_threads_live | 守护线程和非守护线程之和 | 1、字段
> select * from jvm_threads_live limit 1; name: jvm_threads_live time metric_type value —- ———– —– 1591744518692000000 gauge 34 2、tags > show series from jvm_threads_live key — jvm_threads_live,metric_type=gauge |
jvm_threads_peak | 服务启动以来线程的最大数目。 | 1、字段
> select * from jvm_threads_peak limit 1; name: jvm_threads_peak time metric_type value —- ———– —– 1591744518696000000 gauge 36 2、tags > show series from jvm_threads_peak key — jvm_threads_peak,metric_type=gauge |
(3)JvmMemoryMetrics记录堆和buff的信息
measurement | 描述 | 指标信息 |
jvm_memory_committed |
used是已经被使用的内存大小,committed是当前可使用的内存大小(包括已使用的),committed >= used。committed不足时jvm向系统申请,若超过max则发生OutOfMemoryError错误 | 1、字段
> select * from jvm_memory_committed limit 1; name: jvm_memory_committed time area id metric_type value —- —- — ———– —– 1591744517659000000 nonheap Code Cache gauge 20643840 2、tags > show series from jvm_memory_committed key — jvm_memory_committed,area=heap,id=PS\ Eden\ Space,metric_type=gauge jvm_memory_committed,area=heap,id=PS\ Old\ Gen,metric_type=gauge jvm_memory_committed,area=heap,id=PS\ Survivor\ Space,metric_type=gauge jvm_memory_committed,area=nonheap,id=Code\ Cache,metric_type=gauge jvm_memory_committed,area=nonheap,id=Compressed\ Class\ Space,metric_type=gauge jvm_memory_committed,area=nonheap,id=Metaspace,metric_type=gau |
jvm_memory_max |
各个代的最大值。1、PS\ Eden\ Space : Eden最大值 2、PS\ Old\ Gen:老年代最大值 3、PS\ Survivor\ Space:suvivor最大值 |
1、字段 > select * from jvm_memory_max limit 1; name: jvm_memory_max time area id metric_type value —- —- — ———– —– 1591744518670000000 nonheap Compressed Class Space gauge 1073741824 2、tags > show series from jvm_memory_max key — jvm_memory_max,area=heap,id=PS\ Eden\ Space,metric_type=gauge jvm_memory_max,area=heap,id=PS\ Old\ Gen,metric_type=gauge jvm_memory_max,area=heap,id=PS\ Survivor\ Space,metric_type=gauge jvm_memory_max,area=nonheap,id=Code\ Cache,metric_type=gauge jvm_memory_max,area=nonheap,id=Compressed\ Class\ Space,metric_type=gauge jvm_memory_max,area=nonheap,id=Metaspace,metric_type=gauge |
jvm_memory_used | 各个代的占用大小
1、PS\ Eden\ Space : Eden占用大小 2、PS\ Old\ Gen:老年代占用大小 3、PS\ Survivor\ Space:suvivor占用大小 |
1、字段 > select * from jvm_memory_used limit 1; name: jvm_memory_used time area id metric_type value —- —- — ———– —– 1591744517658000000 heap PS Old Gen gauge 38207088 2、tags > show series from jvm_memory_used key — jvm_memory_used,area=heap,id=PS\ Eden\ Space,metric_type=gauge jvm_memory_used,area=heap,id=PS\ Old\ Gen,metric_type=gauge jvm_memory_used,area=heap,id=PS\ Survivor\ Space,metric_type=gauge jvm_memory_used,area=nonheap,id=Code\ Cache,metric_type=gauge jvm_memory_used,area=nonheap,id=Compressed\ Class\ Space,metric_type=gauge jvm_memory_used,area=nonheap,id=Metaspace,metric_type=gauge |
jvm_buffer_count |
缓存区个数 |
1、字段 > select * from jvm_buffer_count limit 1; name: jvm_buffer_count time id metric_type value —- — ———– —– 1591744518672000000 direct gauge 10 2、tags > show series from jvm_buffer_count key — jvm_buffer_count,id=direct,metric_type=gauge jvm_buffer_count,id=mapped,metric_type=gauge |
jvm_buffer_total_capacity | 直接缓冲区direct_capacity:直接缓冲区总大小(字节)
内存映射缓冲区mapped_capacity:内存映射缓冲区总大小(字节) |
1、tags
> select * from jvm_buffer_total_capacity limit 1; name: jvm_buffer_total_capacity time id metric_type value —- — ———– —– 1591744518671000000 direct gauge 81920 2、series > show series from jvm_buffer_total_capacity key — jvm_buffer_total_capacity,id=direct,metric_type=gauge jvm_buffer_total_capacity,id=mapped,metric_type=gauge |
jvm_buffer_memory_used |
直接缓冲区direct_capacity:直接缓冲区已使用大小(字节)
内存映射缓冲区mapped_capacity:内存映射缓冲区已使用大小(字节) |
1、字段 > select * from jvm_buffer_memory_used limit 1; name: jvm_buffer_memory_used time id metric_type value —- — ———– —– 1591744517657000000 direct gauge 81920 2、tags > show series from jvm_buffer_memory_used key — jvm_buffer_memory_used,id=direct,metric_type=gauge jvm_buffer_memory_used,id=mapped,metric_type=gauge |
(4)class
measurement | 描述 | 指标信息 |
jvm_classes_loaded |
加载类的数量 |
1、字段 > select * from jvm_classes_loaded limit 1; name: jvm_classes_loaded time metric_type value —- ———– —– 1591744518693000000 gauge 10697 2、tag > show series from jvm_classes_loaded key — jvm_classes_loaded,metric_type=gauge |
jvm_classes_unloaded |
卸载类的数量 |
1、字段 > select * from jvm_classes_unloaded limit 1; name: jvm_classes_unloaded time metric_type value —- ———– —– 1591744518694000000 counter 0 2、tag > show series from jvm_classes_unloaded key — jvm_classes_unloaded,metric_type=counter |
2、关注指标有哪些
(1)app维度
YGC次数、full gc次数、ygc时间(平均每分钟)、fullgc时间(平均每分钟)的四个IP占比的饼图,表示各个实例的占比情况,方便确认哪台机器有问题
(2)单实例
- Ygc次数;fullgc次数;Ygc时间(平均每分钟);fullgc时间(平均每分钟);
- 堆栈的eden、survior、old的实时占用大小和最大值;
- jvm总线程数和守护线程数、
3、如何添加自定义tag,比如IP
以JvmGcMetrics类为例,在MeterBindersConfiuration.java中如下,通过@ConditionalOnMissingBean,以用户初始为准
1 2 3 4 5 6 7 8 9 10 11 |
@Configuration class MeterBindersConfiguration { @Bean @ConditionalOnProperty(value = "management.metrics.binders.jvm.enabled", matchIfMissing = true) @ConditionalOnMissingBean public JvmGcMetrics jvmGcMetrics() { return new JvmGcMetrics(); } ... } |
重新在服务中通过tags初始化一个JvmGcMetrics对象:
1 2 3 4 5 6 7 8 9 |
@Configuration class MircoMeterConfiguration { @Bean public JvmGcMetrics jvmGcMetrics() { return new JvmGcMetrics(Lists.newArrayList(Tag.of("tag1", "100"),Tag.of("tag1", "111"))); } ... } |
3.5 如何定时保存指标
通过定时线程池来按固定频率发送指标,所以这个频率不能调整太小,因为如果太小比如1s,那么如果这些指标没有在1s发送完成,后续的发送就会阻塞,因为这个线程池是固定频率的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public abstract class StepMeterRegistry extends MeterRegistry { private final StepRegistryConfig config; .... public void start(ThreadFactory threadFactory) { if (publisher != null) stop(); // 通过定时线程池来按固定频率发送指标 if (config.enabled()) { publisher = Executors.newSingleThreadScheduledExecutor(threadFactory) .scheduleAtFixedRate(this::publish, config.step().toMillis(), config.step().toMillis(), TimeUnit.MILLISECONDS); } } .... |
4 应用-支持Http Request指标的扩展信息
如需要对一个Request增加IP、每一个请求的信息(错误码、耗时等)等指标信息,分为Tag字段和非Tag字段两种类型。
目前的micrometer-spring-legacy和micrometer-registry-influx是无法支持的,需要自定义开发来实现。
4.1 Tag类型
需要新增一个IP和Env信息,记录这个请求的 Ip和环境,方便后续统计,如统计流量分布情况。这些信息需要作为tag类型。
1、重写DefaultWebMvcTagsProvider类
1 2 3 4 5 6 7 8 9 |
@Override public Iterable<Tag> httpRequestTags(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler, @Nullable Throwable ex) { // IpTagBuilder和EnvTagBuilder分别用来生成Ip信息的tag和Env信息的统计信息 return Arrays.asList(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(ex), WebMvcTags.status(response),IpTagBuilder.build(),EnvTagBuilder.build()); } |
4.2 非Tag类型
如需要新增一个url_extend字段,类型是String类型的,里面存放每一个请求的耗时、LogId、错误码等信息,例如格式可以为“耗时1_LogID1_错误码1;耗时2_LogId2_错误2″。
对于Influx的Filed类型的值,都是保存在StepTimer中,所以此时需要自定义一个StepTimer来增加这个扩展字段,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class CustomStepTimer extends StepTimer { private StepString extendInfo; @SuppressWarnings("ConstantConditions") public CustomStepTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector, TimeUnit baseTimeUnit, boolean supportsAggregablePercentiles) { // 原有逻辑 super(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit, supportsAggregablePercentiles); // 新增的extendInfo字段 extendInfo = new StepString(clock, distributionStatisticConfig.getExpiry().toMillis()); } @Override protected void recordNonNegative(long amount, TimeUnit unit) { // 原来逻辑 super.recordNonNegative(amount, unit); // 新增逻辑 extendInfo.getCurrent().append(buildExtendInfo()); } /** * 获取扩展信息 * * @return */ public StringBuffer extendInfo() { return extendInfo.poll(); } /** * TODO 生成extend info * * @return */ private String buildExtendInfo() { return ""; } } |
在StepTimer中都是记录的StepLong的值,此时在这个自定义的Timer中需要一个String类型字段,所以参考StepLong自定义了StepString。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public class StepString { private final Clock clock; private final long stepMillis; // 线程安全 private final StringBuffer current = new StringBuffer(); private final AtomicLong lastInitPos; private volatile StringBuffer previous = new StringBuffer(""); public StepString(Clock clock, long stepMillis) { this.clock = clock; this.stepMillis = stepMillis; lastInitPos = new AtomicLong(clock.wallTime() / stepMillis); } private void rollCount(long now) { final long stepTime = now / stepMillis; final long lastInit = lastInitPos.get(); if (lastInit < stepTime && lastInitPos.compareAndSet(lastInit, stepTime)) { // 设置previcout previous = new StringBuffer(current.toString()); // 清空current current.setLength(0); } } public StringBuffer getCurrent() { return current; } /** * @return The value for the last completed interval. */ public StringBuffer poll() { rollCount(clock.wallTime()); return previous; } } |
4.3 重写InfluxMeterRetry
InfluxMeterRetry负责把指标数据输出到Influxdb,所以新增了字段,无论是tag类型或者非tag类型,都需要重写InfluxMeterRetry,来修改拼接调用influxdb的url的参数。
1 2 3 4 |
Class CustomInfluxMeteRetry extends InfluxMetryRetry{ } |
1、需要重写writeTimer方法来来修改拼接调用influxdb的url的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private Stream<String> writeTimer(Timer timer) { // 原有逻辑 final Stream.Builder<Field> fields = Stream.builder(); fields.add(new Field("sum", timer.totalTime(getBaseTimeUnit()))); fields.add(new Field("count", timer.count())); fields.add(new Field("mean", timer.mean(getBaseTimeUnit()))); fields.add(new Field("upper", timer.max(getBaseTimeUnit()))); // 新增字段 if(timer instanceof CustomStepTimer){ timeer = (CustomStemTimer)timer; filds.add(new Fild("extend",timer.extendInfo()) } return Stream.of(influxLineProtocol(timer.getId(), "histogram", fields.build(), clock.wallTime())); } |
上面的Filed也是需要修改的,目前Influxdb都支持long的值,如果需要支持string,还需要修改下Filed中value的类型为String类型。
1 2 3 4 5 6 |
class Field { final String key; final String value; ...... } |
2、在新增非Tag类型实现了一个Timer时,还需要重写InfluxMeterRestry中
1 2 3 4 5 6 7 8 |
@Override protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector) { // 如果是request就返回自定义的Timer:CustomStepTimer // 否则,返回原有StepTimer. } |
4.3 重新定义WebMvcMetricsFilter
如何指定使用自定义MvcTagsProvider类或者自定义InfluxMeterRegestry?此时需要实现一个WebMvcMetricsFilter,这是为了替换如下WebMvcTagsProvider类
1 2 |
private final MeterRegistry registry; private final WebMvcTagsProvider tagsProvider |
5 mirometer版本roadmap
https://github.com/micrometer-metrics/micrometer/releases
6 参考
1、使用 Micrometer 记录 Java 应用性能指标https://www.ibm.com/developerworks/cn/java/j-using-micrometer-to-record-java-metric/index.html
2、jvm监控-阿里云:https://www.alibabacloud.com/help/zh/doc-detail/70073.htm
(全文完)