对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即失效转移路由时,由于网络通信原因误判了第一次失败,又重新路由到第二个节点。那么这两个节点间就并发执行了。

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

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

相关推荐
互联网搬砖老肖3 小时前
Web 架构之攻击应急方案
前端·架构
zizisuo3 小时前
9.3.云原生架构模式
云原生·架构
熊大如如6 小时前
Java 反射
java·开发语言
猿来入此小猿6 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo7 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder7 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
pjx9877 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
炒空心菜菜7 小时前
SparkSQL 连接 MySQL 并添加新数据:实战指南
大数据·开发语言·数据库·后端·mysql·spark
多多*7 小时前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle
爱喝酸奶的桃酥7 小时前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql