一, 分布式任务调度的背景
无论是互联网应用或者企业级应用, 都充斥着大量的批处理任务. 我们常常需要一些任务调度系统来帮助解决问题. 随着微服务化架构的逐步演进, 单体架构逐渐演变为分布式, 微服务架构. 在此背景下, 很多原先的任务调度平台已经不能满足业务系统的需求, 于是出现了一些基于分布式的任务调度平台.
1.1 分布式任务调度的演进
在实际业务开发过程中, 很多时候我们无可避免地需要使用一些定时任务来解决问题. 通常我们会有多种解决方案: 使用 Crontab 或 SpringCron (当然这种情况可能机器很少而且任务简单又不是很多的情况下). 然而, 当应用复杂度升高, 定时任务数量增多且任务之间产生依赖关系时, Crontab 进行定时任务的管理配置就会非常混乱, 严重影响工作效率. 这时就会产生一系列问题:
任务管理混乱, 生命周期无法统一协调管理;
任务之间如果存在依赖关系, 难以编排.
随着互联网的发展, 分布式服务架构势越来越流行. 相应的也需要一个分布式任务调度系统来管理分布式架构中的定时任务.
1.2 分布式任务调度架构
当垂直应用越来越多, 应用之间交互也会越来越复杂, 通常我们采用分布式或者微服务架构, 将核心业务抽取出来, 形成单独的服务. 一个独立的微服务群体逐渐形成稳定的服务中心, 使得业务应用能更快地响应多变的市场需求.
此时, 用于提高业务复用及整合的分布式服务框架成为关键. 同时, 由于服务独立, 一般能做到定时任务独立的情况, 任务的更改对于整体系统的影响小之又小. 通常我们会采用任务与调度分离的方式(如上图所示), 任务的执行逻辑无需关注调度与编排, 同时可以保证执行器和调度的高可用, 易于开发和维护.
1.3 分布式任务调度优势
在分布式服务架构的基础上, 由于独立业务的数量可能很多, 此时如果定时任务单独在该服务中实现, 很可能会出现难以管理的情况, 且避免不了由于定时任务的更改而导致的业务重启. 因此, 一个独立的分布式任务调度系统是很必要的, 可以用来全局统筹管理所有的定时任务. 同时, 将任务的配置单独抽离出来, 作为该分布式任务调度系统的功能, 就能做到定时任务的更改不影响任何业务, 也不影响整个系统:
通过调度与任务分离的方式进行管理, 大大降低了开发和维护成本;
分布式部署, 保证了系统的高可用性, 伸缩性, 负载均衡, 提高了容错性;
可以通过控制台部署和管理定时任务, 方便灵活高效;
任务都可以持久化到数据库, 避免了宕机和数据丢失带来的隐患, 同时有完善的任务失败重做机制和详细的任务跟踪及告警策略.
二, 分布式任务调度技术选型
2.1 分布式任务调度考虑因素
任务编排: 多个业务之间的定时任务存在流程次序.
任务分片: 对于一个大型任务, 需要分片并行执行.
跨平台: 除了使用 Java 技术栈 (SpringBoot,Spring 等) 的项目之外, 还有使用其他语言的应用.
无侵入: 业务不希望与调度高耦合, 只关注业务的执行逻辑.
故障转移: 任务执行过程中遇到问题有补偿措施, 减少人工介入.
高可用: 调度系统自身必须保证高可用.
实时监控: 实时获取任务的执行状态.
可视化: 任务调度的操作提供可视化页面, 方便使用.
- @OnlineTask(description = "在线任务示例",enableSerial=true)
- @RequestMapping(value = "/example", method = { RequestMethod.POST }, produces = "application/json;charset=UTF-8")
- @CrossOrigin(methods = { RequestMethod.POST }, origins = "*")
- @ResponseBody
- public String example(@RequestBody String JSON) {
- /**
- * TODO: 客户端业务逻辑处理
- */
- Map<String, String> info = new HashMap<String, String>();
- info.put("status", "success");
- info.put("result", "as you need");
- return JSONHelper.toString(info);
- }
- org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount = 60
- org.quartz.threadPool.threadPriority = 5
- org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
- public static ExecutorService getExecutorService(String JobKey) {
- ExecutorService exec = executorPool.get(JobKey);
- if (exec == null) {
- LOGGER.info(Constants.LOG_PREFIX + "Initialize thread pool for running Jobs,Job is {}",JobKey);
- exec = Executors.newCachedThreadPool();
- executorPool.putIfAbsent(JobKey, exec);
- exec = executorPool.get(JobKey);
- }
- return exec;
- }
- public interface RestTemplate {
- /**
- * 异步 Post 方法 * @param request
- * @param responseType
- * @param uriVariables
- * @param <T>
- * @return
- */
- <T> ListenableFuture<ResponseEntity<T>> postAsyncForEntity(Request request, Class<T> responseType, Object... uriVariables); }
来源: https://yq.aliyun.com/articles/705745