一起养成写作习惯!这是我参与「掘金日新计划 · 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();
}
同样的,如果ExecutorService是ThreadPoolExecutor的话,那我们就可以真正获取核心线程参数了
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
}
附:
如果文中有描述失误内容,或者没有描述清楚的,可以将问题发我邮箱,harveytuan@163.com, 如果有其他问题,也可以联系我,大家一起共同讨论。