場景
嘗試本地IDEA啟動dubbo-admin,連接到測試環境的zookeeper,查看各微服務情況。
等了幾分鍾發現仍未啟動成功,日志里不斷打印[DUBBO] Subscribe...
信息。
而成功啟動應打印
Tomcat started on port(s): 7001 (http) with context path ''
Started DubboAdminApplication in xxx seconds...
分析
日志里一直在打印訂閱(subscribe)相關信息,而測試環境dubbo-admin的啟動卻很快,不到1分鍾啟動完成。
dubbo-admin里有個RegistryServerSync
類,實現了InitializingBean
,DisposableBean
,NotifyListener
3個接口,
其中在InitializingBean
的afterPropertiesSet
方法:
public void afterPropertiesSet() throws Exception {
logger.info("Init Dubbo Admin Sync Cache...");
registryService.subscribe(SUBSCRIBE, this);
}
其中registryService
是注入到Spring容器的:
@Autowired
private RegistryService registryService;
在dubbo-admin應用的application.properties文件里配置了注冊中心類型和地址:
dubbo.registry.address=zookeeper://192.168.20.4:2181
通過Dubbo的SPI,接口RegistryService
的實現類是ZookeeperRegistry
,注意到它實現其父抽象類FailbackRegistry
里
的doSubscribe
方法中,是通過for循環遍歷服務逐個處理的,測試環境的zookeeper在阿里雲內網服務器上,本地公司內網與阿里雲內網
有大概20-30ms左右的延遲(通過ping命令和咨詢負責網絡運維的同事得知),而測試環境注冊的服務有幾百個,這是啟動慢的原因所在。
解決
思路: 自定義一個類跟ZookeeperRegistry
類似,單線程遍歷處理改為多線程並發處理。
步驟:
- 新建
MyZookeeperRegistry
類繼承FailbackRegistry
類,將原ZookeeperRegistry
類的代碼拷過來進行修改
關鍵代碼如下:
private ExecutorService es = Executors.newFixedThreadPool(50);
...
List<String> services = zkClient.addChildListener(root, zkListener);
if (services != null && !services.isEmpty()) {
long totalStart = System.currentTimeMillis();
for (String service : services) {
service = URL.decode(service);
anyServices.add(service);
String finalService = service;
es.submit(new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis();
subscribe(url.setPath(finalService).addParameters(Constants.INTERFACE_KEY, finalService,
Constants.CHECK_KEY, String.valueOf(false)), listener);
logger.info("subscribe cost=" + (System.currentTimeMillis() - start) + "ms" + ",url=" + url);
}
});
}
es.shutdown();
if (!es.awaitTermination(1, TimeUnit.MINUTES)) {
es.shutdownNow();
}
logger.info("subscribe all done,cost=" + (System.currentTimeMillis() - totalStart) + "ms");
}
注:
- 這里使用固定線程池用50個線程來並發處理訂閱
- 通過線程池提供的
shutdown
,awaitTermination
,shutdownNow
3個方法來實現主線程等待子線程執行,設置等待超時時間,並且打印總耗時
-
修改
application.properties
配置文件,使用自定義的MyZookeeperRegistry
類
dubbo.registry.address=zookeeper://xxx:2181
修改為:
dubbo.registry.address=myzookeeper://xxx:2181
-
重啟應用大概20多秒啟動成功
問題解決:)