xxl-job 执行器管理

1.客户端进行执行器的注册

java 复制代码
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(XxlJobSpringExecutor.class);

    // start
    //执行器调用的开始
    @Override
    public void afterSingletonsInstantiated() {

        // init JobHandler Repository
        /*initJobHandlerRepository(applicationContext);*/

        // init JobHandler Repository (for method)
        initJobHandlerMethodRepository(applicationContext);

        // refresh GlueFactory
        GlueFactory.refreshInstance(1);

        // super start
        try {
      //调用XxlJobExecutor中的start方法
            super.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

XxlJobExecutor.initEmbedServer()

scss 复制代码
private void initEmbedServer(String address, String ip, int port, String appname, String accessToken) throws Exception {

        // fill ip port
        port = port>0?port: NetUtil.findAvailablePort(9999);
        //获取本机ip地址
        ip = (ip!=null&&ip.trim().length()>0)?ip: IpUtil.getIp();

        // generate address
        //将ip与port组装成address
        if (address==null || address.trim().length()==0) {
            String ip_port_address = IpUtil.getIpPort(ip, port);   // registry-address:default use address to registry , otherwise use ip:port if address is null
            address = "http://{ip_port}/".replace("{ip_port}", ip_port_address);
        }

        // accessToken
        if (accessToken==null || accessToken.trim().length()==0) {
            logger.warn(">>>>>>>>>>> xxl-job accessToken is empty. To ensure system security, please set the accessToken.");
        }

        // start
        embedServer = new EmbedServer();
    //启动embedServer 
        embedServer.start(address, port, appname, accessToken);
    }

embedServer.start(...)中有这个样一个函数

scss 复制代码
// start registry
//执行器注册到xxl_job_register
startRegistry(appname, address);

调用 ExecutorRegistryThread.start(...)

arduino 复制代码
public void startRegistry(final String appname, final String address) {
    // start registry
    ExecutorRegistryThread.getInstance().start(appname, address);
}

ExecutorRegistryThread.start(...)

typescript 复制代码
public void start(final String appname, final String address){

        // valid
        if (appname==null || appname.trim().length()==0) {
            logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appname is null.");
            return;
        }
        if (XxlJobExecutor.getAdminBizList() == null) {
            logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null.");
            return;
        }

        registryThread = new Thread(new Runnable() {
            @Override
            public void run() {

                // registry
                while (!toStop) {
                    try {
                        //构造注册参数
                        RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appname, address);
                        for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
                            try {
                                //基于HttpURLConnection调用服务端registry接口注册
                                ReturnT<String> registryResult = adminBiz.registry(registryParam);
                                //在一个服务端注册成功即可。用的同一个数据库
                                if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
                                    registryResult = ReturnT.SUCCESS;
                                    logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                    break;
                                } else {
                                    logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                }
                            } catch (Exception e) {
                                logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e);
                            }

                        }
                    } catch (Exception e) {
                        if (!toStop) {
                            logger.error(e.getMessage(), e);
                        }

                    }

                    try {
                        if (!toStop) {
                            TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
                        }
                    } catch (InterruptedException e) {
                        if (!toStop) {
                            logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage());
                        }
                    }
                }

                // registry remove
                //当执行器被停止,相应的也去服务端注销执行器
                try {
                    RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appname, address);
                    for (AdminBiz adminBiz: XxlJobExecutor.getAdminBizList()) {
                        try {
                            ReturnT<String> registryResult = adminBiz.registryRemove(registryParam);
                            if (registryResult!=null && ReturnT.SUCCESS_CODE == registryResult.getCode()) {
                                registryResult = ReturnT.SUCCESS;
                                logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                                break;
                            } else {
                                logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult});
                            }
                        } catch (Exception e) {
                            if (!toStop) {
                                logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e);
                            }

                        }

                    }
                } catch (Exception e) {
                    if (!toStop) {
                        logger.error(e.getMessage(), e);
                    }
                }
                logger.info(">>>>>>>>>>> xxl-job, executor registry thread destroy.");

            }
        });
        registryThread.setDaemon(true);
        registryThread.setName("xxl-job, executor ExecutorRegistryThread");
        registryThread.start();
    }

项目启动之后,获取配置文件中的xxl.job.executor.appname 以及xxl.job.executor.address 的内容,调用服务端的api/registry 接口进行注册,每BEAT_TIMEOUT时间调用一次api/registry 接口用来保活

xxl.job.executor.address 值不存在,获取xxl.job.executor.ipxxl.job.executor.port 的值进行拼装,这两个值也不存在,ip会调接口获取本机ip,port默认为9999

2.服务端进行执行器的自动注册

客户端调用的api/registry接口,最终会调用JobRegistryHelper.registry(...)

scss 复制代码
public ReturnT<String> registry(RegistryParam registryParam) {

  // valid
  if (!StringUtils.hasText(registryParam.getRegistryGroup())
    || !StringUtils.hasText(registryParam.getRegistryKey())
    || !StringUtils.hasText(registryParam.getRegistryValue())) {
   return new ReturnT<String>(ReturnT.FAIL_CODE, "Illegal Argument.");
  }

  // async execute
  registryOrRemoveThreadPool.execute(new Runnable() {
   @Override
   public void run() {
    //更新xxl_job_registry中update_time字段
    int ret = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registryUpdate(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());
    if (ret < 1) {
     //在xxl_job_registry中新增记录
     XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().registrySave(registryParam.getRegistryGroup(), registryParam.getRegistryKey(), registryParam.getRegistryValue(), new Date());

     // fresh
     freshGroupRegistryInfo(registryParam);
    }
   }
  });

  return ReturnT.SUCCESS;
 }

注册过的执行器更新update_time字段,后续用这个字段判活。更新字段失败的执行器进行注册,即在表xxl_job_registry中,新增一条记录

服务端项目从#XxlJobAdminConfig.afterProperties() 开始执行

csharp 复制代码
@Override
    public void afterPropertiesSet() throws Exception {
        adminConfig = this;
        xxlJobScheduler = new XxlJobScheduler();
        xxlJobScheduler.init();
    }

XxlJobScheduler.init()

csharp 复制代码
public void init() throws Exception {
       ...省略其他内容,日后展开
        // admin registry monitor run
        //将注册的客户端地址放到xxl_job_group中
        JobRegistryHelper.getInstance().start();
    }
scss 复制代码
public class JobRegistryHelper {
 private static Logger logger = LoggerFactory.getLogger(JobRegistryHelper.class);

 private static JobRegistryHelper instance = new JobRegistryHelper();
 public static JobRegistryHelper getInstance(){
  return instance;
 }

 private ThreadPoolExecutor registryOrRemoveThreadPool = null;
 private Thread registryMonitorThread;
 private volatile boolean toStop = false;

 public void start(){

  // for registry or remove
  registryOrRemoveThreadPool = new ThreadPoolExecutor(
    2,
    10,
    30L,
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<Runnable>(2000),
    new ThreadFactory() {
     @Override
     public Thread newThread(Runnable r) {
      return new Thread(r, "xxl-job, admin JobRegistryMonitorHelper-registryOrRemoveThreadPool-" + r.hashCode());
     }
    },
    new RejectedExecutionHandler() {
     @Override
     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
      r.run();
      logger.warn(">>>>>>>>>>> xxl-job, registry or remove too fast, match threadpool rejected handler(run now).");
     }
    });

  // for monitor
  registryMonitorThread = new Thread(new Runnable() {
   @Override
   public void run() {
    while (!toStop) {
     try {
      // auto registry group
      //获取xxl_job_group中类型为自动注册的执行器
      List<XxlJobGroup> groupList = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().findByAddressType(0);
      if (groupList!=null && !groupList.isEmpty()) {

       // remove dead address (admin/executor)
       //获取xxl_job_registry中超时的执行器
       List<Integer> ids = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findDead(RegistryConfig.DEAD_TIMEOUT, new Date());
       if (ids!=null && ids.size()>0) {
        //从xxl_job_registry删除down的执行器
        XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().removeDead(ids);
       }

       // fresh online address (admin/executor)
       HashMap<String, List<String>> appAddressMap = new HashMap<String, List<String>>();
       //查找所有存活的执行器
       //三次心跳时间之内通信过的服务器
       List<XxlJobRegistry> list = XxlJobAdminConfig.getAdminConfig().getXxlJobRegistryDao().findAll(RegistryConfig.DEAD_TIMEOUT, new Date());
       if (list != null) {
        for (XxlJobRegistry item: list) {
         //将执行器按照名字分类,存储相应的ip:port
         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);
         }
        }
       }

       // fresh group address
       //存活的执行器的地址存储到xxl_job_group表中
       //建立执行器时,保证和配置文件中的名字一样,为了现在根据appname获取地址
       for (XxlJobGroup group: groupList) {
        List<String> registryList = appAddressMap.get(group.getAppname());
        String addressListStr = null;
        if (registryList!=null && !registryList.isEmpty()) {
         //将地址排序
         Collections.sort(registryList);
         StringBuilder addressListSB = new StringBuilder();
         for (String item:registryList) {
          addressListSB.append(item).append(",");
         }
         addressListStr = addressListSB.toString();
         addressListStr = addressListStr.substring(0, addressListStr.length()-1);
        }
        group.setAddressList(addressListStr);
        group.setUpdateTime(new Date());

        XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().update(group);
       }
      }
     } catch (Exception e) {
      if (!toStop) {
       logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
      }
     }
     try {
      TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT);
     } catch (InterruptedException e) {
      if (!toStop) {
       logger.error(">>>>>>>>>>> xxl-job, job registry monitor thread error:{}", e);
      }
     }
    }
    logger.info(">>>>>>>>>>> xxl-job, job registry monitor thread stop");
   }
  });
  registryMonitorThread.setDaemon(true);
  registryMonitorThread.setName("xxl-job, admin JobRegistryMonitorHelper-registryMonitorThread");
  registryMonitorThread.start();
 }

}

限于配置的自动注册执行器,每BEAT_TIMEOUT 时间重复一次下边的操作

1.从xxl_job_registry表中获取超时的执行器并删除,更新时间为BEAT_TIMEOUT * 3 之前的执行器为超时

2.从xxl_job_registry表中获取未超时的执行器,按照key为registry_key(客户端xxl.job.executor.appname 值),value为registry_value(客户端xxl.job.executor.address值)放到map中

3.根据服务端配置的appName从map获取所有执行器的地址,排序,放到xxl_job_group的address_list字段中

相关推荐
man201737 分钟前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#1 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@1 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣1 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
前端SkyRain2 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁2 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
老猿讲编程2 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust
黄小耶@2 小时前
python如何使用Rabbitmq
分布式·后端·python·rabbitmq
宅小海4 小时前
Scala-List列表
开发语言·后端·scala
蔚一5 小时前
Javaweb—Ajax与jQuery请求
前端·javascript·后端·ajax·jquery