• 这个没有任何毛病,对不对?

    然后还需要特别注意的是,我说的是“在运行时”修改。

    怎么修改?

    很简单,ElasticJob 其实提供了对应的管理后台页面可以进行参数修改,但是我这里偷个懒,难得去部署对应的管理后台,,准备换个简单的思路。

    因为前面说了,ElasticJob 使用的是 zk 做为自己的注册中心,我直接用工具连接上 zk,然后修改 zk 节点就行了。

    我是怎么知道修改 zk 的哪个节点的呢?

    别着急,等下就讲,歪师傅先带你看效果。

    我这里用的工具是 ZooInspector,修改之后直接点击保存:

    然后,朋友们,注意了,看日志输出

    为了让你看的更加清楚,我把关键日志单独拿出来:

    第一台机器上的日志是这样的:

    第二台机器上的日志是这样的:

    和我们前面推理的结果一模一样。

    好,到这里就可以解答我的一个“按下不表”了。

    首先,shardingTotalCount 叫做作业分片总数,在我前面的例子中,作业分片总数一共是 3 片:

    分成三片之后,Elasticjob 怎么知道每一片应该处理哪些数据呢?

    它不知道,它也不用知道。它只需要告诉每一台服务器:“来,哥们,给你一个号你拿着。你们这波一共有多少多少个人,你是第几片。”

    就完事了。

    因为“昨日成功的订单”这个总的要处理的数据是不变的,所有每一台服务器知道一共要把这批数据分成几片,自己是第几片后,通过代码就能拿到对应的该处理的数据。

    然后你再去看官方描述中关于“分片项”你大概就能知道这到底是个啥玩意了:

    有的哥们比较猛,一次拿到两个号,也没关系,就是多处理一份数据嘛。这种情况就适用于两台机器的性能不一致的情况。

    但是我用这个案例并不是为了引出“性能不一致”这种极少数的情况,而是为了这个...

    当我再启动一个新的服务器,当第三台服务器加入之后,我们啥也没干,它自己就开始处理任务了。

    3 个分片,一台服务器处理一个分片的数据。

    能自动加入,就能自动退出,所以假设我把一台服务给关闭了:

    从日志可以看出来,数据并没有丢。

    第一台机器把本来该在下线的这台服务器上处理的数据给接管了:

    好了,到这里,基本功能就算演示完成,可以适当的响起一些掌声了。

    啥原理啊?

    其实关于原理,官方文档上也按照步骤进行了比较详细的说明:

    如果你不了解 zk 的大致工作原理、节点特性、监听机制啥的,后面肯定会看得比较懵逼。

    所以需要先去补一下这方面的信息,对于这部分的描述和源码的解读有很大帮助。

    如果你能大致理解 zk 的工作原理,那么整体读下来其实没有什么特别难以理解的地方,如果要深入理解每一个步骤的话,那肯定要读一下源码的。

    步骤都有了,去找对应的源码,不就是按图索骥,手拿把掐的事情吗。

    在阅读源码之前,还有一个非常重要的东西要铺垫一下,前面也说了:基于 zk 做的注册中心。

    所以你必须要了解“注册中心的数据结构”是怎么样的,每个节点是干啥的,才能理解代码里面操作 zk 节点的时候,到底是什么含义。

    关于注册中心的数据结构,文档上也有介绍:

    我觉得这个还是非常重要的,所以我多啰嗦几句,主要给你看看实际的数据是怎么样的。

    还是以我本地启动三个服务为例。

    启动起来之后,看 zk 上注册了这些节点:

    其中“why-elastic-job”和“springJob”分别是我们写在 application.yml 里面的 ZooKeeper 的命名空间和 Job 名称:

    config 节点

    config 节点里面是作业配置信息,以 YAML 格式存储:

    可以看到节点里面实际的值比我们配置的多,因为有很多默认项。每个默认项是干啥的,就自己去研究吧。

    前面我说的“运行时修改”,就修改的是这个地方信息。

    我为什么知道改这里?

    还不是官网告诉我的。

    instances 节点

    该节点是作业运行实例信息,子节点是当前作业运行实例的主键。

    作业运行实例主键由作业运行服务器的 IP 地址和 PID 构成。

    作业运行实例主键均为临时节点,当作业实例上线时注册,下线时自动清理。注册中心可以监控这些节点的变化,来协调分布式作业的分片以及高可用。

    具体到我们这个案例中,是这样的:

    instances 下面有三个子节点,代表有三个微服务。

    假设我停止运行一个服务,由于是 zk 的临时节点,这个地方就会变成 2 个:

    sharding 节点

    作业分片信息,子节点是分片项序号,从零开始,至分片总数减一。比如我们这里就是 0 到 2:

    分片项序号的子节点存储详细信息,每个分片项下的子节点用于控制和记录分片运行状态:

    可以看到 0,2 分片是运行在同一个 instance 上的,这一点和日志是匹配的:

    sharding 下除了 instance 节点外,可能还有其他的节点,详细信息说明如下:

    servers 节点

    作业服务器信息,子节点是作业服务器的 IP 地址。

    可在 IP 地址节点写入 DISABLED 表示该服务器禁用。

    在新的云原生架构下,servers 节点大幅弱化,仅包含控制服务器是否可以禁用这一功能。

    为了更加纯粹的实现作业核心,servers 功能未来可能删除,控制服务器是否禁用的能力应该下放至自动化部署系统。

    leader 节点

    作业服务器主节点信息,下面有三个子节点:

    除了节点介绍外,在官网描述上有这样的一句话:

    换句话说就是,如果你想了解作业,那这个节点是很重要的。看源码的时候,需要特别关注对于 leader 节点下的操作。

    在我们的案例中,instance 里面的信息是这样的:

    表示这个节点是主节点。

    源码

    知道了 zk 上每个节点的用处,看源码的时候比着看就行了。

    源码比较多,歪师傅这里只能带着你做个非常简单的导读。

    首先,因为很多逻辑都是基于 zk 节点在来做的,所以最重要的是各种各样的 zk 节点监听器,ElasticJob 在启动时,会执行这个方法,开启监听器:

    比如前面说的这个节点:

    如果这个节点存在,则说明需要重新分片,对应的监听器是这个:

    那么什么时候会触发“重新分片”呢?

    所以在 shardingListenerManager 监听器里面我们可以看到这两个逻辑:

    满足条件之后,就会执行设置重新分片标识的代码:

    该方法里面,创建了一个新的节点:

    这个节点,就是它:

    再比如,看看这个方法:

    这个方法是做对作业进行分片逻辑的。

    对作业进行分片,首先我们要知道当前有哪些实例在运行,对不对?

    那怎么才能知道呢?

    instances 节点请求出战:

    shardingIfNecessary 方法的第一行逻辑就是读取 instances 节点下的数据:

    获取到节点之后,是不是就可以分片了?

    理论上是这样的,但是别着急,你看源码里面还有这样一个判断:

    isLeaderUntilBlock,看方法名称也知道了,看看 Leader 节点是不是到位了,如果没到位,需要等一下 Leader 选举结束。

    怎么判断 Leader 节点是不是到位了?

    前面文档中说了,就是看这个节点是否存在:

    对应到源码就是这样的:

    所以这就是我前面说的,你看源码的时候得结合 zk 节点的用途一起看,知道节点的用途就能理解源码里面操作节点的目的是什么。

    然后,在这里多说一句。

    shardingIfNecessary 这个方法是读取配置,处理分片逻辑的。

    但是这个方法在每一个实例中都会运行,岂不是每个实例都会执行一次分片逻辑?

    这样处理的话,由于多个地方执行分片逻辑,就需要考虑冲突和一致性的问题,导致逻辑非常的复杂。

    虽然这个方法每个实例都会执行,但是其实只需要有一个实例执行分片逻辑就行了。

    那么哪个节点来执行呢?

    你肯定也猜到了,当然是主节点来干这个事儿嘛。如果当前节点不是主节点 return 就完事了:

    怎么看当前节点是否是主节点呢?

    前面已经出现多次了,zk 里面记录着的:

    如果当然节点是主节点,就接着往下执行,就是“作业分片策略”了:

    目前官方提供了三个不同的分片策略:

    对应的实现类是这样的:

    逻辑都非常简单,上手 Debug 两次就能摸清楚。

    建议直接把项目拉下来,然后从测试用例入手。

    好了,源码导读就到这里了。

    我觉得我已经算是告诉你关于 ElasticJob 源码阅读的方式和注意点,如果你掌握到了,留言区留言“清晰”二字,支持一波。

    如果你还是云里雾里的,没事,是我的问题。大胆的说出来:什么玩意?看求不懂。呸,垃圾作者。

    如果你是第一次接触到 ElasticJob,那么读到这里的时候,你的内心关于 ElasticJob 应该还有很多疑问以及不清楚的细节。

    很好,带着你的问题,去翻源码吧。

    源码之下无秘密。

    下面这个环节叫做[荒腔走板],技术文章后面我偶尔会记录、分享点生活相关的事情,和技术毫无关系。我知道看起来很突兀,但是我喜欢,因为这是一个普通博主的生活气息。

    荒腔走板

    这周终于是把《长安三万里》看了,之前一直想看,但是又被三个小时的时长劝退。

    我个人觉得确实是值得豆瓣高分的。

    看完之后,包括看的过程中,我老是想起之前在网上看到的一段话,关于“一颗子弹”和“教育闭环”的。

    “一颗子弹”是指在《我与地坛》看到的一段书评,其内容是:一个人十三四岁的夏天,在路上捡到一支真枪,因为年少无知,天不怕地不怕,他扣下扳机。没有人死,也没有人受伤,他认为自己开了空枪。后来他三十岁或者更老,走在路上听到背后隐隐约约的风声。他停下来回过身去,子弹正中眉心。

    “教育闭环”是指教育具有长期性和滞后性,起初你只能理解表层的道理,直到多年后的某个瞬间,你才能真正领悟到书上知识的真谛,此时教育的任务才算真正完成。

    我小时候读到“两岸猿声啼不住,轻舟已过万重山”的时候,重点总是在“两岸猿声”上,想象着猿猴的叫声是什么样的,那是一番怎样有趣的画面。

    后来,甚至可以说是今年,这个电影上映之后,我才明白当年读书的时候我忽略的“轻舟已过万重山”背后才是有更加蜿蜒曲折、激动人心的故事。

    这句诗就是当年的那一颗子弹,命中了马上三十岁的我,至此,教育才算完成了闭环。

    今年,让我产生同样感受的,还有当年完全忽略的这句话:孔乙己是站着喝酒而穿长衫的唯一的人穿的虽然是长衫,可是又脏又破似平十多年没有补,也没有洗。他对人说话,总是满口之乎者也叫人半懂不懂的。

    此外,电影中多次提到“长安”,虽然我们学的是同样的课本,读的是一样的诗,但是每个人对与“长安”的认知和理解是不一样的。

    现在提到长安,我脑海中出现的第一个画面永远是当年看《河西走廊》纪录片的时候那一个画面。

    第一集《使者》,张骞出使西域,被匈奴囚禁九年后同随从堂邑父出逃,继续西行。

    靠强大意志力穿越塔克拉玛干沙漠和帕米尔高原,到达西域。回程再次被俘,数年后带匈奴妻子和堂邑父又一次出逃东归。

    十三年后,终于再次望到长安城,张骞匍匐在地,长跪不起。

    西北望长安,可怜无数山。

    这一跪,看的我眼泪婆娑。

    12-19 17:25