美文网首页
用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很

用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很

作者: 猿天地 | 来源:发表于2021-02-07 18:57 被阅读0次

    前言

    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微服务 入门 实战与进阶》作者, 公众号猿天地发起人。

    相关文章

      网友评论

          本文标题:用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很

          本文链接:https://www.haomeiwen.com/subject/lfzztltx.html