1. QuorumCnxManager选举通信管理类
1.1 作用
- 管理与集群中其他节点的连接
- 选举通信的通道
1.2 核心内部类
- Listener
- QuorumCnxManager初始化时创建
- 创建ServerSocket对象绑定选举port(2888端口), 用来接收其他节点的选票
- accept其他节点的socket后, 读取sid为其创建SendWorker和RecvWorker对象, 将SendWorker放到this.senderWorkerMap中管理, 创建对应的消息发送队列ArrayBlockingQueue放到this.queueSendMap管理. 同时启动创建的SendWorker和RecvWorker线程
- 因为Socket对象可读可写, 既可发送选票信息也可接收选票信息. 所以初始化连接时做了优化, 两个节点节点直连只维护sid单向连接(sid小的作为server)
-
RecvWorker
接收指定节点选票信息的线程
Long sid;//只接收该sid节点的消息
Socket sock;//该sid节点的连接
final DataInputStream din;//该sid节点的连接
final QuorumCnxManager.SendWorker sw;//该RecvWorker对应的SendWorker
核心功能:阻塞读取对应节点连接的选票信息放到recvQueue队列
所有RecvWorker接收到的选票信息都放在同一个recvQueue队列 -
SendWorker
给指定节点发送选票信息的线程
- 从this.queueSendMap.get(this.sid)各自的队列获取选票消息并发送出去
- 维护this.lastMessageSent.put(this.sid, b)
- 通过维护的流对象发送选票信息
不同的节点对应不同的RecvWorker队列, 通过各自的流发消息.
-
Message
接收到的其他节点选票信息的抽象(原始消息只有buffer没有sid)
ByteBuffer buffer;//消息内容(包含length+消息字节数组)
long sid;//消息来源或目的节点sid -
QuorumConnectionReceiverThread
Listener异步方式创建SendWorker和RecvWorker的执行任务
final Socket sock;//Socket对应节点的sid
final Long sid;//sid -
QuorumConnectionReqThread
Listener异步方式创建SendWorker和RecvWorker的执行任务
final Socket sock;//Socket对应节点的sid
final Long sid;//sid
1.3 核心属性
- mySid
final long mySid, 当前节点的sid. 从myid配置文件中读取 - view
Map<Long, QuorumServer> view, zoo.cnf配置文件中的集群配置 - senderWorkerMap
ConcurrentHashMap<Long, QuorumCnxManager.SendWorker>
当前节点需发送到其他节点的选票信息发送线程, 内部封装流对象 - queueSendMap
ConcurrentHashMap<Long, ArrayBlockingQueue>
当前节点需发送到其他节点的选票信息 - lastMessageSent
ConcurrentHashMap<Long, ByteBuffer> lastMessageSent
维护当前节点需发送到其他节点的当前最新消息 - recvQueue
<QuorumCnxManager.Message> recvQueue, 接收到的其他节点发送的选票信息队列 - connectionThreadCnt
异步方式创建的SendWorker和RecvWorker的数量和
AtomicInteger connectionThreadCnt
所有节点全部存活的情况下, 该数量也小于所有节点数量*2, 因为Listener创建连接时有单向连接的优化. - threadCnt
同步方式创建的SendWorker和RecvWorker的数量和
AtomicInteger threadCnt
所有节点全部存活的情况下, 该数量也小于所有节点数量*2, 因为Listener创建连接时有优化. - connectionExecutor
ThreadPoolExecutor connectionExecutor
异步方式创建SendWorker和RecvWorker的执行线程池
1.4 重点方法
- toSend (Long sid, ByteBuffer b)
1.本节点需发送到其他节点的选票信息添加到对应sid的队列ArrayBlockingQueue
2.若目的sid是本机, 则直接放到recvQueue队列
3.若该sid与当前节点无连接, 则创建连接并初始化相关对象
2. FastLeaderElection选举leader类
2.1 功能
单一节点视觉的leader选举抽象
负责选举过程及选举状态维护
2.2 核心内部类
-
Messenger
选举时选票信息处理对象, 内部维护了一个WorkerSender和WorkerReceiver线程
FastLeaderElection.Messenger.WorkerSender ws;
FastLeaderElection.Messenger.WorkerReceiver wr; -
WorkerSender
选举时当前节点需发出的选票信息处理线程.
职责是从this.sendqueue队列里取出选票信息, 调用manager的toSend方法发送选票信息. 将选票信息对象格式转换成字节格式发出 -
WorkerReceiver
选举时选票信息接收处理线程, 从manager.pollRecvQueue获取消息,
1.将接收的字节格式选票信息转换成内部Notification对象格式
2.若当前节点是LOOKING状态, 则把选票放到recvqueue队列
3.若当前节点和投票人节点均是LOOKING状态且投票人节点周期小于当前节点, 则将当前节点的推选人信息发送到sendqueue队列
4.若当前节点不是LOOKING状态但消息来源节点是LOOKING状态, 将当前节点已选定的leader信息放到sendqueue队列
总结:
当收到选票信息时WorkerReceiver逻辑:
1.当前节点为LOOKING状态时, 无脑接收投票人节点选票
2.当前节点非LOOKING状态, 投票人为LOOKING时, 无脑发送LEADER信息
3.双方节点均为LOOKING状态时, 届期大的节点向对端节点发送推选人信息
-
ToSend
选举时需要发到其他节点选票信息对象格式
long leader;//推选节点id
long zxid;//候选节点事务id
long electionEpoch;//当前选举周期
ServerState state;//当前发送节点的状态
long sid;//当前发送节点sid
long peerEpoch;//当前发送节点周期 -
Notification
选举时接收到其他节点的选票信息对象格式
int version;//数据版本
long leader;//推选节点id
long zxid;//推选节点事务
long electionEpoch;//推选节点周期
ServerState state;//消息发送者的状态
long sid;//消息发送者等待sid
long peerEpoch;//消息发送者的周期
2.3 核心属性
- manager
QuorumCnxManager manager;//选举通信管理对象 - sendqueue
LinkedBlockingQueue<FastLeaderElection.ToSend>
当前节点选举时需要发送出的选票信息队列 - recvqueue
LinkedBlockingQueue<FastLeaderElection.Notification> recvqueue;
当前节点选举时从其他节点接收到的选票信息对象格式 - self
QuorumPeer self;当前选举参与者节点 - messenger
FastLeaderElection.Messenger messenger;//当前选举选票处理对象 - logicalclock
AtomicLong logicalclock = new AtomicLong();
当前节点视觉的选举周期 - proposedLeader
long proposedLeader;//当前节点推选人节点(选票)的sid - proposedZxid
long proposedZxid;//当前节点推选人节点(选票)的事务id - proposedEpoch
long proposedEpoch;//当前节点推选人节点(选票)的周期
2.4 核心方法
-
构造方法-初始化
1.初始化推选人节点的信息
2.初始化选票信息队列
3.初始化选票信心处理生产/消费线程 -
buildMsg
根据选举时选票信息对象格式构建字节格式选票信息
-
Vote lookForLeader()真实选举过程
投票过程中的两个核心选票容器
HashMap<Long, Vote> recvset = new HashMap();
投票箱A. 接收两类选票:
1.投票人节点为LOOKING状态且选票周期不小于当前周期的选票
2.投票人节点状态为FOLLOWING/LEADING且选票周期等于当前周期的选票
HashMap<Long, Vote> outofelection = new HashMap();
投票箱B. 仅接收状态为FOLLOWING/LEADING的投票人节点(任何周期)的选票
投票结束的条件:
1.超过一半的节点的选票为同一个节点且200ms内没有新的选票
2.先判断投票箱A, 再判断投票箱B
核心选举流程
- sendNotifications
将当前节点的选票信息形成消息放入sendqueue队列,在集群内广播
3. QuorumPeer选举参与者
3.1 功能
集群节点的抽象
管理服务端口, 维护节点状态
管理选举端口, 维护自动选举流程
3.2 核心内部类
-
ResponderThread
交流leader信息的线程.
通过3888端口发送UDP包, 和其他节点交流leaderId和zxid. 已经弃用 -
LearnerType
节点类型枚举
PARTICIPANT,//参与选举者的节点
OBSERVER;//不参与选举的节点 -
ServerState
选举参选者的状态枚举
LOOKING,//选举状态所有节点的状态(包括所有类型的节点)
FOLLOWING,//具有参选权且当前节点是从节点时的状态
LEADING,// 具有参选权且当前节点是主(leader)节点时的状态
OBSERVING;//不具有参选权且当前节点是从节点时的状态(提高选过程) -
QuorumServer
选举时的节点对象
public InetSocketAddress addr;
public InetSocketAddress electionAddr;
public String hostname;
public int port = 2888;
public int electionPort = -1;
public long id;
public QuorumPeer.LearnerType type;
3.3 核心属性
private QuorumPeer.LearnerType learnerType;//当前节点类型
private long myid;//当前节点的sid
private volatile Vote currentVote;//当前已选定的主节点
//参与选举的所有节点,从配置文件中读取
protected Map<Long, QuorumPeer.QuorumServer> quorumPeers;
private QuorumPeer.ServerState state;//当前节点选举状态
DatagramSocket udpSocket;//3888端口各节点交流leader信息的通道
Election electionAlg;//选举对象,默认为FastLeaderElection
public Leader leader;//选出来的leader
public Follower follower;//当前节点状态对应的对象,状态失效置为null
public Leader leader; //当前节点状态对应的对象,状态失效置为null
public Observer observer; //当前节点状态对应的对象,状态失效置为null
3.4 核心方法
-
初始化
先初始化为LOOKING状态 -
start方法
1.初始化数据
2.初始化服务端口
3.初始化选举对象并启动
4.启动选举流程
-
createElectionAlgorithm选举初始化
1.创建选举连接对象
2.启动选举连接外部消息相关线程
3.初始化选举端口, 监听其他节点的消息. 启动消息接收/发送线程
4.启动选举状态下的内部格式消息处理线程
-
run方法启动选举流程
轮询当前节点状态, 根据状态不同
1.LOOKING:调用FastLeaderElection的lookForLeader方法完成选举
2.LEADING:创建Leader对象阻塞调用其lead方法, 直到状态失效更新为LOOKING
3.FOLLOWING:创建Follower对象阻塞调用其followLeader方法, 直到状态失效更新为LOOKING
4.OBSERVING:创建Observer对象阻塞调用其observeLeader方法, 直到状态失效更新为LOOKING