分布式任务调度平台xxl-job
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc
1.前言
xuxueli/xxl-job是一个分布式任务调度平台。它在github上有1w多个star,有多家公司都已经用在生产实践中。
在萌叔看来这是一个"Less is more"的典型。它的设计的非常简单,最大的优点是实用。
2.主要结构和逻辑
XXL-JOB任务调度平台分为2个部分,Scheduler
和Executor
。具体的实现Scheduler
对应是xxl-job-admin
,同时xxl-job-admin
还配有web UI,可以配置管理任务。
Scheduler
和Executor
之间通过HTTP API交互,因此Executor
可以通过各种语言实现。比如Golang的
xxl-job/xxl-job-executor-go
以上图为例,scheduleThread
将任务通过Executor
是的/run
api推送给Executor
{
"jobId": 3,
"executorHandler": "task.test",
"executorParams": "x=100",
"executorBlockStrategy": "SERIAL_EXECUTION",
"executorTimeout": 0,
"logId": 17,
"logDateTime": 1606100913829,
"glueType": "BEAN",
"glueSource": "",
"glueUpdatetime": 1606099566000,
"broadcastIndex": 0,
"broadcastTotal": 1
}
Executor
会根据executorHandler
找到对应的handler
,执行完之后,又会调用xxl-job-admin
的/xxl-job-admin/api/callback
回报任务的执行结果。从上面的描述我们可以知道,xxl-job-admin
和excutor
都必须暴露出api服务(都是HTTP接口)。
Scheduler
可以有多个。它们之间通过MySQL进行同步。
主要的调度逻辑在JobScheduleHelper中
在每一轮执行调度逻辑之前, Scheduler
必须先获得行锁
while (!scheduleThreadToStop) {
...
// 加行锁
try {
preparedStatement = conn.prepareStatement( "select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );
preparedStatement.execute();
...
} catch (Exception e) { ... } finally {
...
// 注意:锁必须正常释放
conn.commit();
...
}
由于xxl_job_lock
表中只有一条记录,所以这个逻辑与请求表锁类似,开销是比较大的。
其实这里还可以利用分治法的思想,让不同的任务对应到不同的行锁。来提高整体的并发度。依萌叔的推测, xxl-job
设计时考虑就是调度任务的数量不会太多。因此性能不是它的最主要关注点。
3. 其它
xxl-job内部没有使用Zookeeper这种数据库,因此在高可用性上与Quartz相比还是会稍微弱一些。好在它依赖少,搭建、学习的成本就会非常低。让萌叔不经想到了《鹿鼎记》。
澄观微笑道:「师侄从十一岁上起始练少林长拳,总算运气极好,拜在恩师晦智禅师座下,学得比同门师兄弟们快得多,到五十三岁时,於这指法巳略窥门径。」--《鹿鼎记》
注意: 对MySQL而言,如果xxl-job-admin
在持有行锁的期间发生异常退出,与MySQL的连接断开。一段时间之后,MySQL会自动主动释放这个行锁。因此并不会出现死锁的问题。
好文章