江湖车侠|PowerJob 的自实现高可用方案,妙妙妙

本文适合有 Java 基础知识的人群
江湖车侠|PowerJob 的自实现高可用方案,妙妙妙作者:HelloGitHub-Salieri
HelloGitHub 推出的《讲解开源项目》系列 。
碎碎念高可用放到今天已经不是一个新颖的词汇了 , 怎么实现高可用大家也已经了然于心 。 多实例部署 + 服务注册 + 服务发现这一套组合拳打下来 , 实现高可用那还不是分分钟的事情 。 所以很多人看到 PowerJob 的介绍页面中写了任意组件支持集群部署以实现高可用 , 想当然的以为也是走了上述的那套流程 。 然后看到系统依赖组件时 , 发现......emmm...... zookeeper 呢?没看着 。 那找找 Nacos ?emmm......也没找着......不仅没找着 , 还发现文档中明明白白的写着 , 最小依赖仅为关系型数据库 。 许多用户看到这里就有点百思不得其解了 , 正常来讲都会有两个疑惑 。
江湖车侠|PowerJob 的自实现高可用方案,妙妙妙首先 , 为什么不用注册中心呢?
要做到分布式环境下的高可用 , 肯定是需要服务注册、服务发现这样的概念的 。 没有外部注册中心 , 说白了就是自己去实现了一套类似的机制 。 那为什么要怎么做呢?
其实答案很简单——成本 。 这个成本指的是用户的接入成本 。 对于一个需要部署的重型开源项目来说 , 每少一个外部依赖 , 就多一份潜在的用户 。 额外的系统依赖代表着额外的技术栈和额外的维护成本 , 如果企业本身没有这一套技术体系(比如没用到 zookeeper) , 而 PowerJob 又强依赖 zookeeper , 那大概率只能说再见喽~
第一个问题解决了 , 接下来进入第二个问题~
江湖车侠|PowerJob 的自实现高可用方案,妙妙妙
简单高“可用”PowerJob 系统中的基础组件为调度服务器 server 和执行器 worker , server 负责调度定时任务 , 并派发到 worker 执行 , 是一个典型的 C/S 架构 。
C/S 架构下 , 如果目标是 server 和 client 可以相互联通的“高可用” , 那么实现起来其实非常容易 。
首先 , 启动多个 server 应用实例 , 集群部署 。 然后将多个 server 的 IP 地址统统填入 worker 的配置文件中 , worker 启动时 , 随机找一个 IP 进行连接 , 失败则重试 。 一旦成功连接到某一台 server , 就开始上报自己的地址信息 。 server 通过持有这个信息也可以和 worker 进行通讯 。 如此一来 , 一个最简单版本的“高可用”集群就搭建完成了 。 但是......它真的可用吗?
江湖车侠|PowerJob 的自实现高可用方案,妙妙妙答案显然是否定的(否则也不会有这篇文章了是不是~) 。 以上方案主要存在两个问题:

  1. 任务调度需要保证唯一性 , 即某个任务在某一个时刻只能被一台机器调度 , 否则就会导致重复执行 。 而前文提及的方案中 , 每一台 server 都是完全等价的 , 因此只能依靠分布式锁来保证唯一性 , 即抢到锁的 server 执行调度 , 其他 server 只能充当战地采访人员 , 默默地边缘 OB 。 这种方案下 , 无论部署多少台 server , 系统整体的调度性能其实是固定的 , 多实例部署只能做到高可用 , 而不能做到高性能 。
  2. server 无法持有完整的 worker 集群信息 。 PowerJob 的定位是任务调度中间件 , 旨在为企业下各部门各业务线提供精准的调度和分布式计算能力 。 因此肯定会有集群分组的概念 , 就像 RocketMQ 中存在 ProducerGroup 和 ConsumerGroup 一样 , PowerJob 有着 AppName 的概念 。 一个 AppName 逻辑上对应了某个应用下的一组任务 , 物理上对应了这个应用所部署的集群 。 为了便于 server 统一管理以及一些额外功能的实现(分布式计算) , server 持有某一个 AppName 下完整的集群信息是一个强诉求 , 而前文提及的“瞎猫撞上死耗子”式方案 , 显然没办法做到这一点 。