定位解决dubbo-admin本地启动很慢问题

场景

尝试本地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,NotifyListener3个接口,
其中在InitializingBeanafterPropertiesSet方法:

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类似,单线程遍历处理改为多线程并发处理。

步骤:

  1. 新建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,shutdownNow3个方法来实现主线程等待子线程执行,设置等待超时时间,并且打印总耗时
  1. 修改application.properties配置文件,使用自定义的MyZookeeperRegistry
    dubbo.registry.address=zookeeper://xxx:2181
    修改为:
    dubbo.registry.address=myzookeeper://xxx:2181

  2. 重启应用大概20多秒启动成功

问题解决:)

posted @ 2021-11-30 21:40  cdfive  阅读(870)  评论(0编辑  收藏  举报