XXL-JOB:揭秘任务注册、自动发现

128 阅读2分钟

1 引言

我们在创建或者编辑执行器的时候,选择了自动注册,就会自动显示IP和端口。

效果:

这里面的IP和端口是怎么获取到呢?

2 任务的发现

保存的时候,调用的接口是:/jobgroup/update

关键代码块:

com.xxl.job.admin.controller.JobGroupController#findRegistryByAppName

private List<String> findRegistryByAppName(String appnameParam){
    HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
    // 从xxl_job_registry库表中直接查询
    List<XxlJobRegistry> list = xxlJobRegistryDao.findAll(RegistryConfig.DEAD_TIMEOUT, new Date());
    if (list != null) {
        for (XxlJobRegistry item: list) {
            if (RegistryConfig.RegistType.EXECUTOR.name().equals(item.getRegistryGroup())) {
                String appname = item.getRegistryKey();
                List<String> registryList = appAddressMap.get(appname);
                if (registryList == null) {
                    registryList = new ArrayList<String>();
                }

                if (!registryList.contains(item.getRegistryValue())) {
                    registryList.add(item.getRegistryValue());
                }
                appAddressMap.put(appname, registryList);
            }
        }
    }
    return appAddressMap.get(appnameParam);
}

直接从数据查询显然不是我们关注的流程,我们需要探索IP和地址如何获取并保存到数据库的。

3 任务的注册

查询方式可以通过反向查找的方式(上一期已经讲过)获取入口,这里我们直接从入口分析。注册的入口其实就是项目启动的时候,内置服务的启动,就会获取IP和端口进行注册。

入口:com.xxl.job.core.server.EmbedServer#start

调用链路

链路说明

项目启动初始化com.xxl.job.core.server.EmbedServer,就会通过NetUtil.findAvailablePort()获取端口,IpUtil.getIp()获取IP。

然后,通过com.xxl.job.admin.dao.XxlJobRegistryDao#registrySaveOrUpdate保存到数据库,完成任务的注册。

但是值得一提的是,在注册(com.xxl.job.core.thread.ExecutorRegistryThread#start)的时候,并不是一次性直接保存入库的,而是采用了带开关的线程死循环调用的。

这样避免一次注册失败,导致无法调度任务的问题。

4 结语

小小的一个注册发现,里面的设计思想却不简单。里面基本都采用了异步线程的方式,不影响其他业务模块。通过开关控制死循环,增加了模块的健壮性。

对于线程的死循环的时候,有很多工具都用到了,必须CountDownLatch、锁机制的重试等等。死循环用不好,可能会导致CPU的浪费、甚至内存溢出等问题,所以使用的时候就要小心。

死循环的开关设计尤为重要,尤其在多线程的的情况下保证共享变量的可见性,volatile 关键字有其特别的功能。

private volatile boolean toStop = false;

END


喜欢就点赞收藏,也可以关注我的微信公众号:编程朝花夕拾