当前位置: 首页 > article >正文

解决SpringBoot 3.3.x集成Micrometer和Prometheus,导致项目阻塞,无法启动

        小伙伴们,你们好,我是老寇,我又回来辣,几个月不见甚是想念啊!!!

使用SpringBoot 3.3.x集成Micrometer和Prometheus,导致项目无法启动,因为集成的组件特别多,具体什么组件有问题,无法得知。但是,作为一名程序员,基本排查问题的思路,还是需要具备的!

排查问题方向

1.版本不兼容【代码冲突】

2.错误使用

因此,按照这两个方向进行排查,首先,排除 错误使用 剩下就是版本问题,因为是引入micrometer-registry-prometheus才导致项目阻塞,而对于组件,一般有两种解决方案,一种是降低版本【治标不治本】,另一种阅读源码,找出具体代码,然后改掉【治本】

所以,就有以下两种解决方案

1.降低micrometer的版本至1.11.12,但是需要注意的是spring boot 3.5会删除 PrometheusSimpleclientMetricsExportAutoConfiguration 因此,这种方式不推荐

1  <dependencies>
    <dependency>
      <groupId>io.micrometer</groupId>
      <artifactId>micrometer-registry-prometheus</artifactId>
      <version>1.11.12</version>
    </dependency>
  </dependencies>

2.直接阅读源码,找出问题的根源,经过我debug,找出造成阻塞的代码。注意,使用ObjectProvider,会延迟加载,需要被实际调用,才会完成初始化

源码里面有这么一段注释,大致意思是:MeterRegistry依赖于Tracer,Tracer又依赖于MeterRegistry,所以通过延迟加载的方式来打破循环

Since the MeterRegistry can depend on the {@link Tracer} (Exemplars) and the {@link Tracer} can depend on the MeterRegistry (recording metrics),

this {@link SpanContext} breaks the cycle by lazily loading the {@link Tracer}.

 注意:ObjectProvider.getObject() 本身不会直接造成项目阻塞,但如果Bean的获取耗时过长,可能会导致调用该方法的线程被阻塞,此外,如果存在循环依赖,还会造成死锁的情况

@AutoConfiguration(before = PrometheusMetricsExportAutoConfiguration.class,
		after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ Tracer.class, SpanContext.class })
public class PrometheusExemplarsAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
    // 延迟加载
	SpanContext spanContext(ObjectProvider<Tracer> tracerProvider) {
		return new LazyTracingSpanContext(tracerProvider);
	}

	/**
	 * Since the MeterRegistry can depend on the {@link Tracer} (Exemplars) and the
	 * {@link Tracer} can depend on the MeterRegistry (recording metrics), this
	 * {@link SpanContext} breaks the cycle by lazily loading the {@link Tracer}.
	 */
	static class LazyTracingSpanContext implements SpanContext {

		private final SingletonSupplier<Tracer> tracer;

		LazyTracingSpanContext(ObjectProvider<Tracer> tracerProvider) {
			this.tracer = SingletonSupplier.of(tracerProvider::getObject);
		}

		@Override
		public String getCurrentTraceId() {
			Span currentSpan = currentSpan();
			return (currentSpan != null) ? currentSpan.context().traceId() : null;
		}

		@Override
		public String getCurrentSpanId() {
			Span currentSpan = currentSpan();
			return (currentSpan != null) ? currentSpan.context().spanId() : null;
		}

		@Override
		public boolean isCurrentSpanSampled() {
			Span currentSpan = currentSpan();
			if (currentSpan == null) {
				return false;
			}
			Boolean sampled = currentSpan.context().sampled();
			return sampled != null && sampled;
		}

		@Override
		public void markCurrentSpanAsExemplar() {
		}

		private Span currentSpan() {
			return this.tracer.obtain().currentSpan();
		}

	}

}

因此,改为直接从IOC容器,获取Bean,源码改动如下

@AutoConfiguration(before = PrometheusMetricsExportAutoConfiguration.class,
		after = MicrometerTracingAutoConfiguration.class)
@ConditionalOnBean(Tracer.class)
@ConditionalOnClass({ Tracer.class, SpanContext.class })
public class PrometheusExemplarsAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	SpanContext spanContext() {
		return new TracingSpanContext();
	}

	/**
	 * Since the MeterRegistry can depend on the {@link Tracer} (Exemplars) and the
	 * {@link Tracer} can depend on the MeterRegistry (recording metrics), this
	 * {@link SpanContext} breaks the cycle by lazily loading the {@link Tracer}.
	 */
	static class TracingSpanContext implements SpanContext {

		@Override
		public String getCurrentTraceId() {
			Span currentSpan = currentSpan();
			return ObjectUtil.isNotNull(currentSpan) ? currentSpan.context().traceId() : null;
		}

		@Override
		public String getCurrentSpanId() {
			Span currentSpan = currentSpan();
			return ObjectUtil.isNotNull(currentSpan) ? currentSpan.context().spanId() : null;
		}

		@Override
		public boolean isCurrentSpanSampled() {
			Span currentSpan = currentSpan();
			if (ObjectUtil.isNull(currentSpan)) {
				return false;
			}
			return currentSpan.context().sampled();
		}

		@Override
		public void markCurrentSpanAsExemplar() {
		}

		private Span currentSpan() {
			try {
				return SpringContextUtil.getBean(Tracer.class).currentSpan();
			}
			catch (Exception e) {
				return null;
			}
		}

	}

}

大功告成

这个问题,可是困扰了我好几周呢,中间可是折腾好久,换好多种实现,其中,不外乎换组件,换架构!终于,硬着头皮解决这个问题啦,哈哈哈~

我是老寇,我们后会有期


http://www.kler.cn/a/324282.html

相关文章:

  • Python3.11.9+selenium,选择证书用多线程+键盘enter解决
  • spring-cache concurrentHashMap 自定义过期时间
  • 量化交易系统开发-实时行情自动化交易-3.4.3.3.期货市场深度数据
  • 【AI图像生成网站Golang】雪花算法
  • 高级数据结构——hash表与布隆过滤器
  • H3C NX30Pro刷机教程-2024-11-16
  • 演示:基于WPF的DrawingVisual开发的频谱图和律动图
  • Meta首款多模态Llama 3.2开源:支持图像推理,还有可在手机上运行的版本 | LeetTalk Daily...
  • Python连接Kafka收发数据等操作
  • [leetcode]53_最大子数组(序列)和
  • 2024年空间计算/XR的现状:双子座阶段的探索与展望
  • 关于电商API接口接入及其相关应用分析【主流电商API接口】
  • 音视频处理工具FFmpeg与Java结合的简单使用
  • 【计算机网络 - 基础问题】每日 3 题(二十七)
  • Stable Diffusion绘画 | Checkpoint Merger 模型融合
  • 如何区分这个ip是真实ip,不是虚假的ip
  • 论文阅读 - SWATTING Spambots: Real-time Detection of Malicious Bots on X
  • RabbitMQ的高级特性-延迟队列
  • 个人计算机与网络的安全
  • 初探shell与bash使用指南
  • spring cloud Gateway网关
  • 网络编程(12)——完善粘包处理操作(id字段)
  • 【最新】微信小程序连接onenet——stm32+esp8266+onenet实现查看温湿度,控制单片机
  • 探索CefSharp,Cefsharp浏览器能做自动填表和模拟登录
  • 长芯微LPQ76930锂电池组保护芯片完全P2P替代BQ76930
  • 江协科技STM32学习- P20 实验-TIM编码器接口测速