(篇三)线程池管理之-dubbo线程池监控

557 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

前言

dubbo中大量使用了SPI机制,通过ExtensionLoader在各个地方都提供了自定义的实现。 比如:protocol,LoadBalance等

而我们这次就要通过extensionLoader来获取dubbo中的线程池信息。

1. ExecutorRepository

dubbo4.x 中提供了ExecutorRepository来提供线程管理。

@SPI("default")
public interface ExecutorRepository {

    /**
     *
     * @param url
     * @return
     */
    ExecutorService createExecutorIfAbsent(URL url);

    ExecutorService getExecutor(URL url);

    /**
     * 根据url修改线程池参数
     *
     * @param url
     * @param executor
     */
    void updateThreadpool(URL url, ExecutorService executor);

    /**
     * Returns a scheduler from the scheduler list, call this method whenever you need a scheduler for a cron job.
     * If your cron cannot burden the possible schedule delay caused by sharing the same scheduler, please consider define a dedicate one.
     *
     * @return
     */
    ScheduledExecutorService nextScheduledExecutor();

    ScheduledExecutorService getServiceExporterExecutor();

    /**
     * Get the default shared threadpool.
     *
     * @return
     */
    ExecutorService getSharedExecutor();

}

而它的默认实现DefaultExecutorRepository中有一个值得大家注意的属性。

private ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> data = new ConcurrentHashMap<>();

不难看出,这里其实就是对dubbo中线程池的管理。

2. 如何拿到具体的线程池信息?

dubbo中大量使用了SPI来提供丰富的扩展点,其中一个比较核心的方法。 ExtensionLoader.getExtensionLoader(xxx.class).getDefaultExtension()

这个方法就可以获取当前dubbo启动后指定组件的默认实现。 之后我们会单独展开聊一下dubbo的SPI机制实现的源码。这里只是提及一下。

上述方法,拿到的是一个interface,代表一个抽象的扩展点,而怎么拿到具体实现中的指定属性值呢? 这里提供一个不错的思路。

ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
if(executorRepository != null) {
    // 获取具体的filed属性值
    final Field data = executorRepository.getClass().getDeclaredField(“data”);
    // 保证底层字段可以访问
    data.setAccessible(true);
    ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> executors =
                (ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>>)data.get(executorRepository);

}

2.1 filed操作

下面这个方法就是field.get(obj)的源码,

其中 *obj* - 要从中提取表示字段的值的对象。

返回值为,传入具体对象`obj`中该字段filed对应的实际值

@CallerSensitive
@ForceInline // to ensure Reflection.getCallerClass optimization
public Object get(Object obj)
    throws IllegalArgumentException, IllegalAccessException
{
    if (!override) {
        Class<?> caller = Reflection.getCallerClass();
        checkAccess(caller, obj);
    }
    return getFieldAccessor(obj).get(obj);
}

所以通过这种方式我们就拿到了真正的ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> data的信息了。

2.2 监控指标

如果上文中能拿到了dubbo中具体的ExecutorService信息,那我们就可以关注具体的实现了, 参考篇一中的实现,我们也可以很容易的对dubbo的线程核心进行监控。

这里加一个扩展点,如果你想分别对consumer和provider做监控,那么在监控数据时,要传递这个参数进去。

for (Map.Entry<String, ConcurrentMap<Integer, ExecutorService>> concurrentMapEntry : executors.entrySet()) {
    // 这里的type,可能是consumer或者provider
    String type = concurrentMapEntry.getKey();
    // 具体的线程信息, Key为:url.getPort();
    ConcurrentMap<Integer, ExecutorService>> concurrentMapEntry.getValue();
}

同样的,如果ExecutorServiceThreadPoolExecutor的话,那我们就可以真正获取核心线程参数了

if (executor instanceof ThreadPoolExecutor) {
    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
}

附:

如果文中有描述失误内容,或者没有描述清楚的,可以将问题发我邮箱,harveytuan@163.com, 如果有其他问题,也可以联系我,大家一起共同讨论。