前言
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微服務 入門 實戰與進階》作者, 公眾號猿天地發起人。