高可用实现背景
多实例部署 + 服务注册 + 服务发现这一套组合拳打下来,实现高可用那还不是分分钟的事情。所以很多人看到 PowerJob 的介绍页面中写了任意组件支持集群部署以实现高可用,想当然的以为也是走了上述的那套流程。然后看到系统依赖组件时,发现……emmm…… Zookeeper 呢?没看着。那找找 Nacos ?emmm……也没找着……不仅没找着,还发现文档中明明白白的写着,最小依赖仅为关系型数据库。许多用户看到这里就有点百思不得其解了,正常来讲都会有两个疑惑。
首先,为什么不用注册中心呢?
要做到分布式环境下的高可用,肯定是需要服务注册、服务发现这样的概念的。没有外部注册中心,说白了就是自己去实现了一套类似的机制。那为什么要怎么做呢?
其实答案很简单——成本。这个成本指的是用户的接入成本。对于一个需要部署的重型开源项目来说,每少一个外部依赖,就多一份潜在的用户。额外的系统依赖代表着额外的技术栈和额外的维护成本,如果企业本身没有这一套技术体系(比如没用到 zookeeper),而 PowerJob 又强依赖 zookeeper,那大概率只能说再见喽~
简单高“可用”
PowerJob 系统中的基础组件为调度服务器 server 和执行器 worker,server 负责调度定时任务,并派发到 worker 执行,是一个典型的 C/S 架构。
C/S 架构下,如果目标是 server 和 client 可以相互联通的“高可用”,那么实现起来其实非常容易。
首先,启动多个 server 应用实例,集群部署。然后将多个 server 的 IP 地址统统填入 worker 的配置文件中,worker 启动时,随机找一个 IP 进行连接,失败则重试。一旦成功连接到某一台 server,就开始上报自己的地址信息。server 通过持有这个信息也可以和 worker 进行通讯。如此一来,一个最简单版本的“高可用”集群就搭建完成了。但是……它真的可用吗?
答案显然是否定的(否则也不会有这篇文章了是不是~)。以上方案主要存在两个问题:
- 任务调度需要保证唯一性,即某个任务在某一个时刻只能被一台机器调度,否则就会导致重复执行。而前文提及的方案中,每一台 server 都是完全等价的,因此只能依靠分布式锁来保证唯一性,即抢到锁的 server 执行调度,其他 server 只能充当战地记者,默默地边缘 OB。这种方案下,无论部署多少台 server,系统整体的调度性能其实是固定的,多实例部署只能做到高可用,而不能做到高性能。
- server 无法持有完整的 worker 集群信息。PowerJob 的定位是任务调度中间件,旨在为企业下各部门各业务线提供精准的调度和分布式计算能力。因此肯定会有集群分组的概念,就像 RocketMQ 中存在 ProducerGroup 和 ConsumerGroup 一样,PowerJob 有着 AppName 的概念。一个 AppName 逻辑上对应了某个应用下的一组任务,物理上对应了这个应用所部署的集群。为了便于 server 统一管理以及一些额外功能的实现(分布式计算),server 持有某一个 AppName 下完整的集群信息是一个强诉求,而前文提及的“瞎猫撞上死耗子”式方案,显然没办法做到这一点。
基于以上两点,征途是星辰大海的 PowerJob 需要探索出一种更合理、更强大的高可用架构。
powerJob 高可用实现思想
worker 因为没办法获取 server 的准确状态,所以不能由 worker 来决定连接哪一台 server。因此,worker 需要做的,只是服务发现。即定时使用 HTTP 请求任意一台 server,请求获取当前该分组(appName)对应的 server。
而 server 收到来自 worker 的服务发现请求后,其实就是进行了一场小型的分布式选主:server 依赖的数据库中存在着 server_info 表,其中记录了每一个分组(appName)所对应的 server 信息。如果该 server 发现表中存在记录,那就说明该 worker 集群中已经有别的 worker 事先请求 server 进行选举,那么此时只需要发送 PING 请求检测该 server 是否存活。如果发现该 server 存活,那么直接返回该 server 的信息作为该分组的 server。否则就完成篡位,将自己的信息写入数据库表中,成为该分组的 server。
细心的小伙伴可能又要问了?发送 PING 请求检测该 server 是否存活,不还是有和刚才一样的问题吗?请求不同,发送方和接收方都有可能出问题,凭什么认为是原先的 server 挂了呢?
确实,在这个方案下,依旧没办法解决 server 到底挂没挂这个堪比“真假美猴王”的玄学问题。但是,这还重要吗?我们的目标是某个分组下所有的 worker 都连接到同一台 server,因此,即便产生那种误打误撞篡位的情况,在服务发现机制的加持下,整个集群最终还是会连接到同一台 server,完美实现我们的需求。
...
...
This is copyright.