可水平扩展的周期性HTTP请求调度执行系统设计咨询
周期性HTTP请求调度系统设计方案建议
一、Request Loader水平扩展避免重复读取的实现方案
针对多实例部署Request Loader时防止重复拉取任务的需求,有几个K8s环境下可直接落地的成熟方案:
- 基于数据库行级锁的无侵入方案
不需要引入额外中间件,直接利用主流SQL数据库都支持的SELECT FOR UPDATE SKIP LOCKED语法实现,扫表时执行如下语句:SELECT * FROM scheduled_requests WHERE next_run_time <= NOW() FOR UPDATE SKIP LOCKED LIMIT 100;
多个实例同时执行该语句时,数据库会自动跳过已经被其他实例锁定的行,天然不会出现重复读取的问题,中小规模任务量下性能完全够用。 - 基于分布式锁的时间分片方案
按任务执行时间拆分为固定粒度的分片(比如5分钟一个分片),每个Request Loader实例启动时抢占对应时间分片的分布式锁(可基于Redis、etcd实现),仅扫描自己持有锁的分片范围内的待执行任务,也能完全避免重复读取。 - 基于一致性哈希的固定分片方案
任务量较大时可以把任务ID按一致性哈希拆分为N个分片,K8s下用StatefulSet给每个Request Loader实例分配固定序号,实例启动后仅负责扫描对应序号范围内分片的任务,扩展性更好,只需额外实现扩缩容时的分片重平衡逻辑即可。
二、数据库扫描机制的合理性判断
数据库扫描属于成本极低的可落地方案,是否合理完全取决于你的业务规模:
- 适用场景:任务量级在十万级以下、调度精度要求在分钟级的场景,只要给
next_run_time字段加上索引,扫描性能完全够用,后续排查问题、做逻辑修改的成本也极低。 - 不适用场景:任务量级超过百万级、调度精度要求到秒级的场景,高频扫库会给SQL数据库带来很大的查询压力,调度精度也很难保障,这种情况不建议用纯数据库扫描方案。
三、替代优化方案参考
如果后续业务规模有上涨预期,可以对现有架构做优化,不用自己从零实现Request Loader的调度逻辑:
- 直接用成熟的分布式定时调度框架(比如XXL-JOB、PowerJob)替代自研Request Loader,这类框架本身已经解决了多实例调度不重复、高可用的问题,你只需要实现调度触发后将任务推送到消息队列的逻辑即可。
- 不想引入额外框架的话,也可以把扫库的轮询逻辑改成事件驱动:任务写入数据库时,同时在Redis中给对应任务设置过期时间等于执行间隔的key,通过Redis的键过期事件通知Request Loader拉取对应任务执行,能大幅降低SQL数据库的压力。
内容的提问来源于stack exchange,提问作者nmartins
相关产品推荐
相关产品推荐

