一 springboot应用集成xxl-job
在使用springboot开发的应用中,引入xxl-job-core模块,就使项目具备了执行器的功能。
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.4.0</version>
</dependency>
添加配置,需提供以下信息。
创建
XxlJobSpringExecutor的bean对象,并交由spring管理。
基于方法编写任务体,使用
@XxlJob标注,指定任务名。
启动调度平台,进行任务配置。BEAN名称即
@XxlJob的value值。
二 执行器的启动
XxlJobSpringExecutor是XxlJobExecutor的子类,实现了SmartInitializingSingleton接口。当所有单例bean初始化完成后,spring会调用实现了该接口的bean的afterSingletonsInstantiated()方法。
来看该方法:
- 先调用
initJobHandlerMethodRepository(applicationContext),扫描@XxlJob标注的方法,包装成MethodJobHandler对象,添加到jobHandlerRepository的Map中。
// key即任务名
private static ConcurrentMap<String, IJobHandler> jobHandlerRepository = new ConcurrentHashMap<String, IJobHandler>();
MethodJobHandler是IJobHandler接口的一个实现,将通过反射执行任务体。
- 最后调用
super.start(),完成上下文初始化。
2.1 XxlJobExecutor类
XxlJobExecutor是个核心类,持有所有配置,定义所有http请求的处理方式。
XxlJobExecutor.start()中完成以下工作,执行器将启动成功。
从中可知:
- 日志文件保存在执行器机器上,过期删除通过异步线程实现;
- 执行器与每个调度平台(集群部署),都有一个
通信组件即AdminBizImpl; - 执行器执行完任务,向调度平台异步汇报结果;
- 创建netty客户端,用于向调度平台发起注册、处理调度平台的指令。
三 执行器与调度平台的双向通信
com.xxl.job.core.biz.ExecutorBiz接口,定义了执行器面向调度平台的业务功能:响应心跳、响应空闲心跳、终止执行、读取执行日志、执行任务
com.xxl.job.core.biz.AdminBiz接口,定义了调度平台面向执行器的业务功能:响应执行回调、注册、取消注册请求
可见,执行器和调度平台,是双向交互的。
四 执行器注册流程
XxlJobExecutor.start()中调用了initEmbedServer,创建EmbedServer实例。EmbedServer在异步线程中,创建netty客户端,来处理调度平台的请求,同时发起注册。
注册和取消注册,都在registryThread中完成。
4.1 续期
注册是周期性动作,每30秒发起一次。当执行器连续3次注册失败时,将被调度平台从执行器列表中移除。
这一点与eureka客户端的注册续期相似。
如果由调度平台主动向每个执行器发送心跳探测,来实现续期。那么,调度平台在性能、网络IO上将承受很大压力。
4.2 为什么向任意调度平台注册成功即可?
因为调度平台维护的执行器实例列表,在数据库表中保存,而不是进程内存。当调度平台采用集群部署时,并不需要在多个节点间做任何数据同步,只要有一个节点成功处理了注册请求即可。
所有节点都从数据库表读取同一份注册表记录。
4.3 beat接口不是心跳探测吗?
在ExecutorBizImpl中有心跳实现。难道调度平台周期性向执行器发送心跳,来探测它是否在线,从而决定是否将它从注册表中移除?
其实,beat接口用于故障转移路由策略(在
xxl-job-admin模块的ExecutorRouteFailover类)。调度平台会依次向执行器发送beat请求,将第一个在线的执行器作为调度目标。
4.4 执行器处理请求
使用Netty接收来自调度平台的请求,并创建了
maximumPoolSize = 200、队列长度=2000的线程池。
由
EmbedHttpServerHandler类,向线程池提交任务,来异步处理每个请求。
根据请求url识别指令,指令的具体实现都在
ExecutorBizImpl类。如处理kill请求: