定时微服务
前置准备
背景和现状
市面上已有的定时微服务技术:Java Timer、Rocket MQ、xxl-job、Quartz、Robfig/Cron
- 很多公司的业务只需要一个简单的“定时”功能。为了满足一个简单定时功能而引入过于强大而且臃肿的任务调度组件是不合适的
- 设计一个功能聚焦、轻量级、维护成本低的定时微服务组件是很有必要的
定时微服务LyyTimer
- 依赖简单:只需要提供MySQL、Redis和Kafka组件的支持即可,接入和维护的成本低
- 学习成本低:整个定时微服务功能聚焦,容易上手
- 特性优:LyyTimer具有高精准、高负载、异常处理等特性,能基本满足大部分的定时需求
难点
高精准
相对于单点定时(例如Java.Timer)基本能做到毫秒级触发,几乎所有定时任务框架或者消息队列,都只能保证秒级别误差
常见导致时间误差大的原因:
- 定时脚本运行耗时
- 等待定时脚本运行
- 消息队列中消息堆积
- 分布式定时器延时受网络影响
- 数据库中查找任务数据耗时
- 特殊时间段任务过多
高负载
能够支持同时处理大量任务,并且尽可能少的堆积任务
高负载解决思路和方法:
- 应用层方面减少任务量,选择将同一时刻的多个独立任务合并成一个“计划列表”
- 分库分表:将一个大型数据库分成多个较小的数据库,并将每个数据库进一步分成多个较小的表,每个表只包含部分数据
- 垂直分表:保持列完整
- 水平分表:只保留部分列
- 分库分表组合
- 数据分区:将大数据拆分成多个小数据分区,将这些小分区存储在不同的物理设备或服务器上,提供数据的存取效率和处理速度
- 线程池技术
- 复用线程,降低了资源消耗
- 高效地管理多个线程
任务异常处理
定时任务需求可以允许有一定的误差,但是不能不触发
任务异常的原因:
创建(激活)任务接口异常:在创建任务阶段发生异常,系统可以直接将异常原因返回给用户。用户感知到异常后,可通过主动重试解决问题,即该阶段的异常不应该由框架解决
任务流转异常:一旦任务成功存储在数据库中,就认为该定时任务创建成功,微服务框架就要负责该任务后续的流转(查询数据库、发送消息队列、缓存等)。因此,框架需要解决任务流转过程中发生的异常
任务触发阶段异常:当任务执行后,系统需要回调告知业务方任务结果。对于业务方来说,如果回调失败导致结果丢失,就相当于该任务没有执行过
常用的解决方案
- (立即)重试机制:可以应付一些偶发性异常,例如数据库抖动、Redis抖动
- 失败兜底机制:一般来说,兜底策略都是采用一些“无脑”脚本,全表扫描数据库,针对失败记录进行重试操作。但是兜底策略一般在性能上不占优势,所以需要选择合适的时间间隔执行兜底脚本
- 失败报警机制:当短时间内有大量任务失败时,需要及时报警
- 人工干预