zookeeper - 多线程分析(10)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 概述    这篇文章目的主要是为了讲解清楚zookeeper启动过程中启动各类服务,说白了就是启动了线程提供服务,希望通过这个文章的梳理能够让大家对整个zookeeper的线程架构有一个清晰的印象。

概述

    这篇文章目的主要是为了讲解清楚zookeeper启动过程中启动各类服务,说白了就是启动了线程提供服务,希望通过这个文章的梳理能够让大家对整个zookeeper的线程架构有一个清晰的印象。

线程分析

整个zookeeper的线程按照功能进行划分,主要分为下面5类,分别是 选举相关线程Client连接相关线程Peer连接相关线程Peer调用链线程quorumPeer线程

img_f01a64fc503a5f83f392cc38a4582a13.png
zookeeper线程分析

选举相关线程

    选举相关线程顾名思义,就是在zookeeper集群发生重新选举的时候用于处理选举相关的线程,核心类在于FastLeaderElection,该类内部包含WorkerReceiverWorkerSender两个核心线程,负责发送和接收选举报文。 

    FastLeaderElection本身其实就在一个单独的线程,也就是后面说的quorumPeer线程中运行的,本身内部具备一个while循环再处理选举相关的逻辑,所以可以认为FastLeaderElection也是一个线程。

img_063b35d175e02026fabddb8d54e619b4.png
选举相关线程


img_1b3744ef3f50e42284a9496211843e86.png
quorumPeer的选举线程


img_920e4a097ed96fa26b261fdadfeacf83.png
FastLeaderElection


Client连接相关线程

    client连接相关线程顾名思义,就是zookeeper在处理client连接过程中相关的线程,其实zookeeper在实现的server的多线程模型其实就是Nio模型中提到的相关模型,有兴趣可以阅读《netty概念小结》。

    该实现的核心类主要是NIOServerCnxnFactory,该类内部包含expirerThread、acceptThread、selectorThreads,这里我只关注后面两者,其中acceptThread主要负责处理client的连接,selectorThreads主要负责处理每个client具体的数据读写,也就是说我们把accept和io处理进行了分离。


img_be4b0e6c7464c00d80da41cdb7e36e05.png
accept线程


img_457c146e69939787a59af2573770cf71.png
selector线程


Peer连接相关线程

    peer之间的连接主要是zookeeper集群间通信的连接,一般是指follower或learner连接server的过程。当我们的一个zookeeper节点作为leader的时候我们会启动一个accept线程LearnerCnxAcceptor用于接受peer的连接,作为server端我们会为每个peer发送过来的连接建立一个线程进行处理,针对每个连接新建线程的核心类是LearnerHandler。

    这部分的accept线程LearnerCnxAcceptor和处理线程LearnerHandler主要用于zookeeper的节点之间的通信,跟client无关,跟client无关,跟client无关

img_40c52c0b9e505060e5c46415f0271567.png
LearnerCnxAcceptor
img_be3034d31ee7f5a7016e63881800537c.png
LearnerHandler



Peer调用链线程


img_0c436ba75ed2d614ac9eb3aeeeb441c4.png
leader调用链


img_0613b99695b6a7ce63bbfffb7912f181.png
follower调用链

PrepRequestProcessor 

    PrepRequestProcessor线程。该线程消费请求队列submittedRequests,开始实施一致性算法。submittedRequests有两个来源,一是接入的客户端直接提交,提交的请求既包括写请求,也包括一些查询请求;另一个是由Follower转发,转发内容只包括写请求和同步请求。PrepRequestProcessor收到submittedRequest后,将请求转发给CommitProcessor线程和SyncRequestProcessor线程的输入队列;对于其中的写请求,向所有follower发送PROPOSAL消息(异步发送)。

img_63c8517e4b05fc4d80a5a54ee8998882.png
PrepRequestProcessor


CommitProcessor

    该线程主要消费两个队列queuedRequests和committedRequests。queuedRequests保存PrepRequestProcessor线程下发的submittedRequest消息。committedRequests保存Proposal通过后,LearnerHanlder线程(后文会有说明)发来的提交请求。

    CommitProcessor在这里做了如下处理:对于queuedRequests中客户端的查询request,直接返回本地数据;对于客户端提交的或follower转发来的写请求,作为一个pendingRequest等待相应的表决结果返回committedRequest到committedRequests队列。对于队列中到来的每一个committedRequest,如果当前有pendingRequest等待,并且其sessionId,zxid和这个请求匹配,则处理pendingRequest(如果原始请求发自客户端,pendingRequest会携带客户端连接对象,从而能够发送响应给客户端),否则直接处理committedRequest(这种情况对应Follower中的CommitProcessor直接接收到了commit消息)。处理的过程是记录committedLog,变更本地数据。如果请求从客户端来,发送响应给客户端。那么如果一个pendingRequest始终等不到对应的committedRequest到来呢?答案是会一直等待,从而会阻止之后所有queuedRequest请求的处理!开始看到这里以为是个bug,后来想想,如果发生这种情况,已经说明Zookeeper的voter节点超过半数Fault了(不管是消息丢失还是宕机)。这时整个Zookeeper服务只能是不可用了。否则只要过半的voter节点可用,一定会有相应的committedRequest返回。同时这里也保证了写请求按到达顺序生效。

img_6a7906c1a07ce736c0fdb5d416fbd257.png
CommitProcessor


SyncRequestProcessor

    该线程负责将submittedRequest记录到Log。ZooKeeper使用一个简单的内存数据库ZKDatabase来处理日志、session信息和datatree(znode树,类似文件系统结构,用来组织存放实际数据。与文件系统不同的是目录也可以有数据)日志采用1000条批量flush到日志文件,满一定条数起单独线程生成snap文件。记录完日志后直接发送ACK消息给Leader对象—作为一个投票者投出自己的一票。

img_fd4a09cb26299a43798c3c19d2a92c00.png
SyncRequestProcessor


FollowerRequestProcessor

    该线程主要是负责follower接收client连接后的报文处理链条,follower接收到client的报文后提交到queuedRequests队列,由FollowerRequestProcessor进行处理并提交到CommitProcessor进行处理。

img_8dc5852b03d4f85eb45660c5a3749df3.png
FollowerRequestProcessor


quorumPeer线程

    quorumPeer的在zookeeper集群模式下每个节点本身就是一个quorumPeer服务,内部启用线程来处理发生重新选举的场景,也就是说白了就是每个zk节点就是quorumPeer节点。

img_6133267c52b278d02c846c1e011da045.png
QuorumPeer线程用于选举


参考文章

    ZooKeeper 核心模块代码浅析

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
27天前
|
并行计算 安全 Java
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
在Python开发中,GIL(全局解释器锁)一直备受关注。本文基于CPython解释器,探讨GIL的技术本质及其对程序性能的影响。GIL确保同一时刻只有一个线程执行代码,以保护内存管理的安全性,但也限制了多线程并行计算的效率。文章分析了GIL的必要性、局限性,并介绍了多进程、异步编程等替代方案。尽管Python 3.13计划移除GIL,但该特性至少要到2028年才会默认禁用,因此理解GIL仍至关重要。
126 16
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
|
4月前
|
存储 NoSQL Redis
Redis 新版本引入多线程的利弊分析
【10月更文挑战第16天】Redis 新版本引入多线程是一个具有挑战性和机遇的改变。虽然多线程带来了一些潜在的问题和挑战,但也为 Redis 提供了进一步提升性能和扩展能力的可能性。在实际应用中,我们需要根据具体的需求和场景,综合评估多线程的利弊,谨慎地选择和使用 Redis 的新版本。同时,Redis 开发者也需要不断努力,优化和完善多线程机制,以提供更加稳定、高效和可靠的 Redis 服务。
115 1
|
4月前
线程CPU异常定位分析
【10月更文挑战第3天】 开发过程中会出现一些CPU异常升高的问题,想要定位到具体的位置就需要一系列的分析,记录一些分析手段。
126 0
|
2月前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
95 4
|
8月前
|
存储 SQL 监控
JAVA 线程池的分析和使用
JAVA 线程池的分析和使用
57 0
|
5月前
|
安全 Java API
Java线程池原理与锁机制分析
综上所述,Java线程池和锁机制是并发编程中极其重要的两个部分。线程池主要用于管理线程的生命周期和执行并发任务,而锁机制则用于保障线程安全和防止数据的并发错误。它们深入地结合在一起,成为Java高效并发编程实践中的关键要素。
63 0
|
6月前
|
存储 监控 Java
|
6月前
|
安全 Java 开发者
Swing 的线程安全分析
【8月更文挑战第22天】
90 4
|
6月前
|
Java 数据库连接 数据库
当线程中发生异常时的情况分析
【8月更文挑战第22天】
159 4
|
6月前
|
安全 Java 程序员
线程安全与 Vector 类的分析
【8月更文挑战第22天】
102 4

热门文章

最新文章