• admin
  • 956
  • 2026-02-05 12:44:22

FIRST(第一个):固定选择第一个机器;

LAST(最后一个):固定选择最后一个机器;

ROUND(轮询):按照注册机器的列表顺序进行任务调度;

RANDOM(随机):随机选择在线的机器;

CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上;

LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;

LEAST_RECENTLY_USED(最近最久未使用):最久未使用的机器优先被选举;

FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;

BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;

SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务。

虽然策略比较多,但是常用的策略就是固定在一台机器上面跑或者轮询策略。假设有一个密集的定时调度任务,每隔两分钟跑一次,如果在同一个机器上跑,遇到调用下游接口响应慢,或者处理的业务数据暴增,可能出现前一次任务还没有完成,下一次任务已经发起了。为了防止任务重叠在一台机器上,可以采用 xxl-job 轮询的策略。

面试官:按照你刚才举的例子,定时任务每隔两分钟跑一次,如果选择固定在一台机器上跑,比如选择路由策略是 FIRST 或者 LAST,有多个任务重叠在一台机器上,xxl-job 是怎样解决的?

我:对于跑批间隔时间比较短的定时任务,因为调度很密集,执行器很容易造成任务阻塞。xxl-job 提供了 3 种阻塞处理策略:

单机串行(默认):调度请求进入单机执行器后,调度请求进入 FIFO 队列并以串行方式运行;

丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;

覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务。

单机串行(默认):调度请求进入单机执行器后,调度请求进入 FIFO 队列并以串行方式运行;

丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;

覆盖之前调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,将会终止运行中的调度任务并清空队列,然后运行本地调度任务。

采用单机串行的策略,如果前面的任务没有处理完成,后面的任务只能排队,等待被调度。

面试官:单机任务排队执行,这种策略看上去没有什么问题。你有遇到过相关问题吗?

我:一般场景下是不会有影响的,但如果一个任务跟日期相关,有任务排队到第二天才能执行,很可能会造成业务影响。

面试官:能举一个具体的场景吗?

我:举一个贷款业务的例子,系统通过跑批任务给应还款日是第二天的客户发送还款短信通知,处理逻辑是每个任务查出应还款日是第二天的一批用户,然后发送短信通知。这里的第二天需要用系统日期来判断。如果有排队的任务到第二天才执行,那应还款的部分用户会收不到通知。

面试官:这种场景 xxl-job 有解决方案吗?

我:采用轮询的路由策略,可以让排队的任务调度的不同的机器上,这样可以减少单机器的任务积压问题。

面试官:采用轮询的路由策略,表面看是可以解决单机器的任务积压问题。但如果任务积压是因为下游接口响应慢、sql 查询性能差等造成的任务执行慢,采用轮询策略可以解决吗?

我:这类问题造成的任务排队,改为轮询策略也是解决不了的,轮询策略只能解决类似单机资源紧张造成跑批慢的情况。

面试官:那这些问题有什么解决方案吗?

我:业务上是有解决方案的,比如金融行业系统里面一般有“切日”的概念,跟日期相关的业务不会取系统日期,而是取数据库保存的账务日期,所有需要使用账务日期的任务跑批完成后,才会把数据库保存的账务日期切换到第二天。

面试官:采用轮询路由策略,还可能带来哪些问题呢?

我:还可能造成冥等的问题。比如一个任务的处理逻辑是,每隔两分钟从数据库查询 100 条状态是“未处理”的数据进行处理,处理完成后更新状态为“已处理”。如果机器一上的任务还没有执行完成,机器二上的任务已经开始调度,那很可能会把机器一上正在执行的 100 条数据查出来重复处理。

面试官:这个问题该怎么解决呢?

我:我提供两种解决思路:

第一,最简单的方式就是采用单机执行;

第二,增加一个中间状态“处理中”,执行查询数据的时候采用排它锁,查出后更新成中间状态,提交事务后再执行处理逻辑,处理逻辑执行完成后更新成“已处理”

第一,最简单的方式就是采用单机执行;

第二,增加一个中间状态“处理中”,执行查询数据的时候采用排它锁,查出后更新成中间状态,提交事务后再执行处理逻辑,处理逻辑执行完成后更新成“已处理”

updatexxxsetstatsu='处理中'whereidin(...)

面试官:那如果数据源不能加锁呢?比如数据源是邮件、接口查询。

我:可以对邮件或接口查到的数据唯一键进行保存,把唯一键做数据库主键,下一个任务可以根据主键做排除,再实现业务逻辑。

面试官:好的,恭喜你进入下一轮...

小时候上电脑课,到底为什么要穿鞋套?

微软是怎么硬气起来的?

喜提一个bug,聊聊@NotEmpty和@NotBlank

生产环境大面积404,这锅我不背!

美团一面问我i++跟++i的区别是什么

小时候上电脑课,到底为什么要穿鞋套?

微软是怎么硬气起来的?

喜提一个bug,聊聊@NotEmpty和@NotBlank

生产环境大面积404,这锅我不背!

美团一面问我i++跟++i的区别是什么返回搜狐,查看更多