上图是xxl-job的架构图,从架构图可以看出,xxl-job 分别有调度中心和执行器两大组成部分
- 调度中心。负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。支持可视化界面,可以在调度中心对任务进行新增,更新,删除,会实时生效。支持监控调度结果,查看执行日志,查看调度任务统计报表,任务失败告警等等。
- 执行器。负责接收调度请求,执行调度任务的业务逻辑。执行器启动后需要注册到调度中心。接收调度中心的发出的执行请求,终止请求,日志请求等等。
本文是关于执行器的梳理,也就是xxlJob客户端的代码,其中有几个关键部分:
1.接收server端的请求(接收任务下发)
2.将接收到的任务添加到待执行任务队列
3.多线程消费任务(执行任务)
4.将任务执行结果回写到结果队列
5.回调线程消费结果队列,将结果发送至server端
1.关键实例的创建
关键实例的创建是 com.xxl.job.core.executor.XxlJobExecutor
,具体使用时有两种实现
XxlJobExecutor 中封装了关键的启动流程,详情可参考方法:
com.xxl.job.core.executor.XxlJobExecutor#start
在spring框架下使用xxl-job,需要注册 XxlJobSpringExecutor,示例参考:
2. 接受http请求的server
客户端需要接收server端发起的http请求,用于触发任务的执行,因此是启动了一个用于处理http请求的server的,具体是实现类为: com.xxl.job.core.server.EmbedServer
启动EmbedServer
的方法为 :com.xxl.job.core.server.EmbedServer#start
中,其中完成了几个关键事项:
- 创建netty的io线程eventGroup
- 创建业务线程池
- 将请求处理类注册到channel的pipeline中
- 向server端注册appname
3.将待执行任务写入队列
之后的重点就在 EmbedHttpServerHandler
中了,其中封装了处理request的具体逻辑, channelRead0
方法:
com.xxl.job.core.server.EmbedServer.EmbedHttpServerHandler#channelRead0
此处的线程池
bizThreadPool
即为EmbedServer#start
创建的业务线程池,该业务线程池的大小是写死的
异步执行的具体方法为: com.xxl.job.core.server.EmbedServer.EmbedHttpServerHandler#process
根据请求uri的不同,路由到不同的逻辑上
这里我们主要梳理任务执行的逻辑,为: com.xxl.job.core.biz.impl.ExecutorBizImpl#run
上述方法中的jobHandler 是在client端启动时,扫描了注解
@XxlJob
注册到内存中的,具体可查看方法com.xxl.job.core.executor.impl.XxlJobSpringExecutor#initJobHandlerMethodRepository
至此,待执行任务进入了待执行任务队列。
- 基于上述分析可知,bizThreadPool 只是将待执行任务写入 jobThread的队列中,不是具体执行,其定位类比于tomcat中的poller,此参数调优的需求不大,因此代码中线程池各参数是写死的
- 具体业务执行不是依托于线程池,而是为每一个jobHandler创建一个jobThread,而每个jobThread中维护自己的待执行任务队列
4.执行任务
每一个 jobThread
中维护自己的待执行任务队列 triggerQueue
,线程的run方法循环消费队列任务,并执行逻辑。
关键方法: com.xxl.job.core.thread.JobThread#run
除了核心逻辑,每次任务执行时,每个jobThread分别执行一次 init() 方法和 destroy() 方法
5.执行结果的回写
相关实现在 TriggerCallbackThread
中,其中维护了一个全局的 callBackQueue
,每次任务执行完毕后,通过 com.xxl.job.core.thread.TriggerCallbackThread#pushCallBack
将任务执行结果写入 callBackQueue
创建子线程,循环消费任务
6.其它:业务线程说明
业务是由 jobThread
执行的,而 jobThread
的创建是以jobId
为唯一标识,那jobId是什么概念呢?
jobId是任务的唯一id,对应于表xxl-job
中的主键,该字段在表中是自增主键,即通过数据库保证jobId的唯一。
对应到管理页面上
一个任务可以指定其运行的执行器、cron表达式、jobHandler、路由策略、超时时间 等关键信息。