对xxl-job架构的一点思考

本文中源码来自xxl-job 2.4.0版本

大家好,我是程序员侠客。在熟悉了xxl-job源码很多细节后,本文尝试跳出细节,思考其中值得借鉴学习的地方,如接口设计、RPC实现、CAP模型选择等。

希望在阅读源码过程中,帮助大家提高编程技巧外,还能提升个人架构思维。

一 接口或类体系设计

xxl-job源码中,接口或类的设计,简单清晰。

1.1 JobAlarm接口

仅有一个方法。提供了一个Email实现即EmailJobAlarm,我们可自行扩展其他方式,如电话、短信等。

java 复制代码
@Component
public class PhoneJobAlarm implements JobAlarm {
  public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog) {
    // 打电话
    return true;
  }
}

又定义了JobAlarmer类,在afterPropertiesSet()中初始化jobAlarmList,alarm过程就是遍历调用每一个JobAlarmer实现。

java 复制代码
@Component
public class JobAlarmer implements ApplicationContextAware, InitializingBean {
  private List<JobAlarm> jobAlarmList;

  // 简化后代码
  public boolean alarm(XxlJobInfo info, XxlJobLog jobLog) {
    boolea   result = true;
    // 遍历每一个实现类
    for (JobAlarm alarm : jobAlarmList) {
        result = result && alarm.doAlarm(info, jobLog);
    }
    return result;
  }
}

1.2 ExecutorRouter抽象类

该类用于定义路由策略,route方法根据任务参数,从执行器地址列表中选择一个节点来触发执行。

java 复制代码
public abstract ReturnT<String> route(TriggerParam triggerParam, List<String> addressList);

源码中提供了以下实现,继承体系非常清晰。 子类对象并没有交给Spring容器管理,而是在枚举类中创建,实现了单例。可见,这些子类都是无状态的。

1.3 IJobHandler抽象类

IJobHandler是对任务体的定义,有3个方法。 源码中提供了Glue、脚本、基于方法的实现。

1.4 AdminBiz接口

该接口定义了执行器指向调度中心的RPC调用:注册、取消注册和执行结果回调。 有两个实现:执行器发送请求、调度中心处理请求。 ExecutorBiz接口也是同样的模式。 用一个接口来约定server、client两端的可交互内容,显得非常清晰。

二 RPC实现

2.1 执行器Socket客户端实现

执行器被集成在业务应用中。这些应用不一定是web项目,也未必使用如springMVC这样的web框架。因此,执行器的Socket客户端,得独立实现。

xxl-job的执行器端,使用了Netty,封装在EmbedServer类中。

2.2 调度中心Socket客户端实现

调度中心向执行器发送请求,使用了JDK中原生的java.net.HttpURLConnection,作为Http客户端。在XxlJobRemotingUtil类中

2.3 调度中心Socket服务端实现

调度中心本身基于springboot开发,本身是个web项目,使用内置的tomcat,作为Socket服务端实现。 调度中心处理执行器请求,使用RESTful风格接口和springmvc框架,。

三 对xxl-job架构的思考

3.1 调度中心的CAP选择

调度中心独立于执行器,需要单独部署,支持集群模式(没有主节点)。由于数据保持在mysql中,实现了多节点间共享。因此,不需要Zookeeper这样的中间件。

调度中心集群的CAP模型,取决于Mysql的部署方式。

3.2 有状态的执行器

xxl-job执行器本身,是有状态的:待执行的任务队列、回调队列和本地日志文件

如果某个执行器突然宕机,会导致该执行器上积压的任务丢失;调度平台访问执行日志时,也可能查询无果。

3.3 任务体是否有状态

除了分片任务,在一次调度中仅有一个节点被选中,且任务的阻塞策略(有3种)不允许并发执行。 因此,任务体似乎没有并发操作时的数据一致性问题。

但是,我们自己实现的任务体,可能是有状态的。如某个任务中需要处理数据库表数据,在没有避免并发处理的措施时,可能导致意外结果。

因为,在路由策略和执行周期选择不合理时,会导致多个执行器节点并发执行。

如使用FAILOVER即失效转移路由时,由于网络通信原因误判了第一次失败,又重新路由到第二个节点。那么这两个节点间就并发执行了。

或者任务的周期间隔设置不合理,使得下一次调度触发时,上一次长耗时执行还未结束,这两次执行又路由到不同的节点,此时就并发了。

在实现任务体时,我们需要考虑幂等或防止并发执行。

相关推荐
Ashlee_code10 分钟前
什么是Web3?金融解决方案
开发语言·金融·架构·eclipse·web3·区块链·php
Edingbrugh.南空14 分钟前
Flink ClickHouse 连接器数据读取源码深度解析
java·clickhouse·flink
NE_STOP26 分钟前
SpringBoot--简单入门
java·spring
hqxstudying1 小时前
Java创建型模式---原型模式
java·开发语言·设计模式·代码规范
蓝倾1 小时前
如何使用Python通过API接口批量抓取小红书笔记评论?
前端·后端·api
Dcs1 小时前
VSCode等多款主流 IDE 爆出安全漏洞!插件“伪装认证”可执行恶意命令!
java
aloha_1 小时前
Flowable 引擎在启动时没办法找到AsyncListenableTaskExecutor类型的 bean
后端
保持学习ing1 小时前
day1--项目搭建and内容管理模块
java·数据库·后端·docker·虚拟机
京东云开发者1 小时前
Java的SPI机制详解
java
超级小忍2 小时前
服务端向客户端主动推送数据的几种方法(Spring Boot 环境)
java·spring boot·后端