

用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很厉害?
source link: https://mp.weixin.qq.com/s?__biz=MzIwMDY0Nzk2Mw%3D%3D&%3Bmid=2650325865&%3Bidx=1&%3Bsn=02a1cea22fc86fbe019b7b255fc5615d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

micrometer 中自带了很多其他框架的指标信息,可以很方便的通过 prometheus 进行采集和监控,常用的有 JVM 的信息,Http 请求的信息,Tomcat 线程的信息等。
对于一些比较活跃的框架,有些还是不支持的,比如 Dubbo。如果想监控 Dubbo 的一些指标,比如线程池的状况,我们需要手动去扩展,输出对应的线程池指标才行。
在这种情况下,肯定是没什么思路的,因为你不知道怎么去扩展,下面给大家介绍去做一件事情之前的思考,方式方法很重要。
-
Dubbo 有没有现成的实现?
-
参考 micrometer 中指标的实现,依葫芦画瓢?
Dubbo 有没有现成的实现?
完整的实现应该没有,至少我还没用过,也没有那种去搜索引擎一搜就大把结果的现状,于是我在 Dubbo 的 Github 上找到了一个相关的项目 dubbo-spring-boot-actuator。
https://github.com/apache/dubbo-spring-boot-project/tree/master/dubbo-spring-boot-actuator
dubbo-spring-boot-actuator 看名称就知道,提供了 Dubbo 相关的各种信息端点和健康检查。从这里面也许能发现点有用的代码。
果不其然,在介绍页面中看到了想要的内容,线程池的指标数据,只不过是拼接成了字符串显示而已。
"threadpool": {
"source": "management.health.dubbo.status.extras",
"status": {
"level": "OK",
"message": "Pool status:OK, max:200, core:200, largest:0, active:0, task:0, service port: 12345",
"description": null
}
}
然后就去翻 dubbo-spring-boot-actuator 的代码了,没找到线程池这块的代码。后面在 dubbo.jar 中找到了 ThreadPoolStatusChecker 这个类,核心逻辑在这里面。 现在已经解决了第一个问题,就是获取到 Dubbo 的线程池对象。

参考 micrometer 中指标的实现,依葫芦画瓢?
线程池对象能拿到了,各种数据也就能获取了。接下来的问题就是如何暴露出去给 prometheus 采集。
两种方式,一种是自定义一个新的端点暴露,一种是直接在已有的 prometheus 端点中增加指标数据的输出,也就是依葫芦画瓢。
看源码中已经有很多 Metrics 的实现了,我们也实现一个 Dubbo 线程池的 Metrics 即可。

上图框起来的就是一个已经存在的线程池 Metrics,可以直接复用代码。
实现的主要逻辑就是实现一个 MeterBinder 接口,然后将你需要的指标进行输出即可。于是打算在 bindTo 方法中获取 Dubbo 的线程池对象,然后输出指标。经过测试,在 MeterBinder 实例化的时候 Dubbo 还没初始化好,拿不到线程池对象,绑定后无法成功输出指标。
后面还是打算采用定时采样的方式来输出,自定义一个后台线程,定时去输出数据。可以用 Timer,我这图简单就直接 while 循环了。
/**
* Dubbo线程池指标
*
* @author yinjihuan
*/
@Configuration
public class DubboThreadMetrics {
@Autowired
private MeterRegistry meterRegistry;
private final Iterable<Tag> TAG = Collections.singletonList(Tag.of("thread.pool.name", "dubboThreadPool"));
@PostConstruct
public void init() {
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
Map<String, Object> executors = dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY);
for (Map.Entry<String, Object> entry : executors.entrySet()) {
ExecutorService executor = (ExecutorService) entry.getValue();
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor tp = (ThreadPoolExecutor) executor;
Gauge.builder("dubbo.thread.pool.core.size", tp, ThreadPoolExecutor::getCorePoolSize)
.description("核心线程数")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.largest.size", tp, ThreadPoolExecutor::getLargestPoolSize)
.description("历史最高线程数")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.max.size", tp, ThreadPoolExecutor::getMaximumPoolSize)
.description("最大线程数")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.active.size", tp, ThreadPoolExecutor::getActiveCount)
.description("活跃线程数")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.thread.count", tp, ThreadPoolExecutor::getPoolSize)
.description("当前线程数")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.queue.size", tp, e -> e.getQueue().size())
.description("队列大小")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.taskCount", tp, ThreadPoolExecutor::getTaskCount)
.description("任务总量")
.baseUnit("threads")
.register(meterRegistry);
Gauge.builder("dubbo.thread.pool.completedTaskCount", tp, ThreadPoolExecutor::getCompletedTaskCount)
.description("已完成的任务量")
.baseUnit("threads")
.register(meterRegistry);
}
}
}
}).start();
}
}
指标信息:

配置线程池图表
创建一个新的 dashboard 配置图表,然后新建 panel 配置指标信息

左侧配指标信息,右侧选择对应的图表格式。需要注意的是,如果有多个服务实例,Metrics 这边最好是根据服务实例来显示,需要在指标后面增加条件,如下:
dubbo_thread_pool_max_size_theads{application="$application", instance=~"$instance"}

关于作者:尹吉欢,简单的技术爱好者,《Spring Cloud 微服务-全栈技术与案例解析》, 《Spring Cloud 微服务 入门 实战与进阶》作者, 公众号 猿天地 发起人。
- END -
后台回复 学习资料 领取学习视频
如有收获,点个在看,诚挚感谢
Recommend
-
25
用了这么多年铅笔,现在才知道它是这样制成的
-
86
自己都不知道自己那么厉害~没脾气了~
-
52
问与答 - @li19910102 - 用了很多年的招商银行信用卡,目前额度 6W,后来办了精致白金卡,但从 2015 年开始至今,一毛钱额度都没涨过,还有办法吗?就算进大黑屋,被风控,也没有这么长时间的吧?这么多年一直按是全额还款,就
-
25
现代程序员写代码没有人敢说自己没用过泛型,这个泛型模板T可以被任何你想要的类型替代,确实很魔法很神奇,很多人也习以为常了,但就是这么有趣的泛型T底层到底是怎么帮你实现的,不知道有多少人清楚底层玩法,这篇我就试着来分享一下,...
-
16
作为一个 Java 程序员,日常编程早就离不开泛型。泛型自从 JDK1.5 引进之后,真的非常提高生产力。一个简单的泛型 T
-
33
最近真的是活久见了…不知道你是否也有碰到之前Fork过的国外开源项目,最近突然崩了,原因居然是好多项目都把 master 分支改为了 main 分支!更可怕的是修改原因居然是涉及种族歧视。用了那么多年的master,居...
-
2
面试题:说说你对泛型的理解? 面试考察点 考察目的:了解求职者对于Java基础知识的掌握程度。 考察范围:工作1-3年的Java程序员...
-
11
V2EX › 程序员 用了多年的逻辑鼠标,终于还是被他的驱动给劝退了。 wmwmajie
-
6
V2EX › iPhone ios 版讯飞输入法用了这么多年,竟然出现了广告!
-
7
V2EX › 宽带症候群 用了十多年的上海电信老用户,想转投上海联通的怀抱。请问需要注意什么细节嘛?
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK