分布式任务框架设计


在项目中使用到分布式任务框架,用起来比较好用实用,便研究了一下设计的原理。由于是公司自研的分布式框架,不便写的太过详细,这里主要记录一下设计的原理。同时我也会对比我以前使用的任务框架,做一下对比

公司自研的分布式任务对客户端来说,使用感觉友好,代码基本没有的业务入侵。任务框架也使用了一些第三方的中间件,主要使用了如下组件以及作用

  • 数据库(Mysql):保存用户的定时任务配置数据,如corn表达式等
  • 消息队列(RabbitMQ):当任务触发时,任务框架的服务端发消息,触发客户端执行任务
  • Zookeeper:用来为任务提供分布式锁,保证任务只会在一个实例上执行

这里对客户端还是有一定的侵入性的,首先用户的定时任务配置数据,是保存在客户端的,需要每个客户端实例使用的数据库中都要添加一个Task表。当用户需要添加Task时,需要在客户端自己库的Task表中插入任务配置数据,这些配置数据也会同步给服务端(这里就涉及到数据一致性的问题,后面会讲)

这与我以前在平安使用的任务中间件不同,平安的任务框架,任务配置数据是保存在服务端的,服务端会提供一个web页面来给使用Task的服务添加任务,配置任务等,当然也支持,在web上面对任务做权限控制,即时触发等。而我这里使用的任务数据配置在客户端的Task表中,权限控制是没办法做的

Task任务表设计如下,其实就是一些定时任务常见的配置项,corn表达式,任务类型,触发时间之类的。

-- st_trading.Task definition

CREATE TABLE `Task` (
  `id` varchar(256) COLLATE utf8mb4_general_ci NOT NULL,
  `type` varchar(256) COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务标识',
  `firstTriggerTime` datetime NOT NULL COMMENT '首次触发时间',
  `cronString` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'cron表达式,为空标识是一次性task',
  `dataMap` text COLLATE utf8mb4_general_ci COMMENT '任务参数',
  `nextTriggerTime` datetime NOT NULL COMMENT '下次执行时间',
  `round` int NOT NULL,
  `createTime` datetime NOT NULL,
  `timeZoneId` varchar(30) COLLATE utf8mb4_general_ci DEFAULT 'GMT' COMMENT '任务执行时区',
  `mutexId` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '分布式锁key',
  `canaryVersion` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '灰度版本',
  `contextMap` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `status` int DEFAULT '0',
  `priority` int NOT NULL DEFAULT '2' COMMENT '优先级',
  PRIMARY KEY (`id`),
  KEY `IX_Task_nextTriggerTime` (`nextTriggerTime`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

这个表在每个客户端都会创建一个,服务端也会有这个表,保存全部客户端的定时任务配置数据。整体架构如下图

举个例子,假设Task表有如下数据

当客户端创建了一个名为target_clear_task_us 的Task时,在MQ中也会创建名字一样的topic,当定时任务触发时,任务的服务端会给target_clear_task_us的topic下发任务触发的消息,客户端收到消息会就会执行任务。

但是这里有一个问题,没办法保证任务名称的唯一性,假如全公司很多团队都是使用这个任务框架,那么task的名称就有可能会一样,这时任务是没办法触发的,只能与对应的开发沟通修改任务名称。

前面说到服务端会保存所有客户端的任务配置数据,从这个图中可以看到,服务端同步客户端数据的方式,是简单粗暴的连接了所有客户端的数据库,然后每10秒轮询所有的任务数据,同时也判断任务是否要触发。这里我曾经思考过,是否可以改成监听客户端binlog的方式同步数据,而不是轮询。但是后面想了想轮询的做法有几个好处

  1. 可以保证服务端数据与客户端数据的一致性,因为服务端不保存数据,任务配置数据全部是由客户端维护的
  2. 可靠性,如果是binlog的方式,意味着要增加一个中间件的使用,降低了可靠性,提高了维护成本
  3. 因为是服务端代码实现了,可以做一些加入一些异常告警,权限控制等

当然也有缺点:

  1. 如果有新的服务接入Task,需要把数据地址告知服务端,也需要发布版本。如果是binlog同步的话,服务端完全是无感的
  2. 对于服务端同样有一定的侵入性,因为需要维护所有客户端的数据库连接

Author: 顺坚
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source 顺坚 !
评论
 Previous
单元化架构 单元化架构
在当今的互联网业内,很多大型互联网系统,比如淘宝、支付宝、网商银行等,都已经实现了单元化架构,并从中获益匪浅。其实在我们手机里面很多常用APP都是单元化架构,类似高德导航、支付宝、网商银行等金融银行类APP都是将单元化架构进行了很多年,都已
2023-10-22
Next 
消息队列灰度 消息队列灰度
最近公司项目比较多,多项目多市场并行开发,需要服务支持灰度发布,以便进行生产验证,避免出现问题。在项目中消息中间件和分布式Task是必不可少的,接口调用的灰度转发可以通过网关实现,因此主要需要对这两个中间件做灰度转发控制 所谓灰度服务,是
2023-10-21
  TOC