zookeeper源码 — 四、session建立
目录
- session建立的主要过程
- 客户端发起连接
- 服务端创建session
session建立的主要过程
用一张图来说明session建立过程中client和server的交互
主要流程
- 服务端启动,客户端启动
- 客户端发起socket连接
- 服务端accept socket连接,socket连接建立
- 客户端发送ConnectRequest给server
- server收到后初始化ServerCnxn,代表一个和客户端的连接,即session,server发送ConnectResponse给client
- client处理ConnectResponse,session建立完成
客户发起连接
和server建立socket连接
客户端要发起连接要先启动,不论是使用curator client还是zkClient,都是初始化org.apache.zookeeper.ZooKeeper#ZooKeeper
。
Zookeeper初始化的主要工作是初始化自己的一些关键组件
- Watcher,外部构造好传入
- 初始化StaticHostProvider,决定客户端选择连接哪一个server
- ClientCnxn,客户端网络通信的组件,主要启动逻辑就是启动这个类
ClientCnxn包含两个线程
- SendThread,负责client端消息的发送和接收
- EventThread,负责处理event
ClientCnxn初始化的过程就是初始化启动这两个线程,客户端发起连接的主要逻辑在SendThread线程中
// org.apache.zookeeper.ClientCnxn.SendThread#run
@Override
public void run() {
clientCnxnSocket.introduce(this,sessionId);
clientCnxnSocket.updateNow();
clientCnxnSocket.updateLastSendAndHeard();
int to;
long lastPingRwServer = System.currentTimeMillis();
final int MAX_SEND_PING_INTERVAL = 10000; //10 seconds
while (state.isAlive()) {
try {
// client是否连接到server,如果没有连接到则连接server
if (!clientCnxnSocket.isConnected()) {
if(!isFirstConnect){
try {
Thread.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
LOG.warn("Unexpected exception", e);
}
}
// don't re-establish connection if we are closing
if (closing || !state.isAlive()) {
break;
}
// 这个里面去连接server
startConnect();
clientCnxnSocket.updateLastSendAndHeard();
}
// 省略中间代码...
clientCnxnSocket.doTransport(to, pendingQueue, outgoingQueue, ClientCnxn.this);
// 省略中间代码...
}
SendThread#run是一个while循环,只要client没有被关闭会一直循环,每次循环判断当前client是否连接到server,如果没有则发起连接,发起连接调用了startConnect
private void startConnect() throws IOException {
state = States.CONNECTING;
InetSocketAddress addr;
if (rwServerAddress != null) {
addr = rwServerAddress;
rwServerAddress = null;
} else {
// 通过hostProvider来获取一个server地址
addr = hostProvider.next(1000);
}
// 省略中间代码...
// 建立client与server的连接
clientCnxnSocket.connect(addr);
}
到这里client发起了socket连接,server监听的端口收到client的连接请求后和client建立连接。
通过一个request来建立session连接
socket连接建立后,client会向server发送一个ConnectRequest来建立session连接。两种情况会发送ConnectRequest
- 在上面的connect方法中会判断是否socket已经建立成功,如果建立成功就会发送ConnectRequest
- 如果socket没有立即建立成功(socket连接建立是异步的),则发送这个packet要延后到doTransport中
发送ConnectRequest是在下面的方法中
// org.apache.zookeeper.ClientCnxn.SendThread#primeConnection
void primeConnection() throws IOException {
LOG.info("Socket connection established to "
+ clientCnxnSocket.getRemoteSocketAddress()
+ ", initiating session");
isFirstConnect = false;
long sessId = (seenRwServerBefore) ? sessionId : 0;
ConnectRequest conReq = new ConnectRequest(0, lastZxid,
sessionTimeout, sessId, sessionPasswd);
// 省略中间代码...
// 将conReq封装为packet放入outgoingQueue等待发送
outgoingQueue.addFirst(new Packet(null, null, conReq,
null, null, readOnly));
}
clientCnxnSocket.enableReadWriteOnly();
if (LOG.isDebugEnabled()) {
LOG.debug("Session establishment request sent on "
+ clientCnxnSocket.getRemoteSocketAddress());
}
}
请求中带的参数
- lastZxid:上一个事务的id
- sessionTimeout:client端配置的sessionTimeout
- sessId:sessionId,如果之前建立过连接取的是上一次连接的sessionId
- sessionPasswd:session的密码
服务端创建session
和client建立socket连接
在server启动的过程中除了会启动用于选举的网络组件还会启动用于处理client请求的网络组件
org.apache.zookeeper.server.NIOServerCnxnFactory
主要启动了三个线程:
- AcceptThread:用于接收client的连接请求,建立连接后交给SelectorThread线程处理
- SelectorThread:用于处理读写请求
- ConnectionExpirerThread:检查session连接是否过期
client发起socket连接的时候,server监听了该端口,接收到client的连接请求,然后把建立练级的SocketChannel放入队列里面,交给SelectorThread处理
// org.apache.zookeeper.server.NIOServerCnxnFactory.SelectorThread#addAcceptedConnection
public boolean addAcceptedConnection(SocketChannel accepted) {
if (stopped || !acceptedQueue.offer(accepted)) {
return false;
}
wakeupSelector();
return true;
}
建立session连接
SelectorThread是一个不断循环的线程,每次循环都会处理刚刚建立的socket连接
// org.apache.zookeeper.server.NIOServerCnxnFactory.SelectorThread#run
while (!stopped) {
try {
select();
// 处理对立中的socket
processAcceptedConnections();
processInterestOpsUpdateRequests();
} catch (RuntimeException e) {
LOG.warn("Ignoring unexpected runtime exception", e);
} catch (Exception e) {
LOG.warn("Ignoring unexpected exception", e);
}
}
// org.apache.zookeeper.server.NIOServerCnxnFactory.SelectorThread#processAcceptedConnections
private void processAcceptedConnections() {
SocketChannel accepted;
while (!stopped && (accepted = acceptedQueue.poll()) != null) {
SelectionKey key = null;
try {
// 向该socket注册读事件
key = accepted.register(selector, SelectionKey.OP_READ);
// 创建一个NIOServerCnxn维护session
NIOServerCnxn cnxn = createConnection(accepted, key, this);
key.attach(cnxn);
addCnxn(cnxn);
// 省略中间代码...
}
}
说了这么久,我们说的session究竟是什么还没有解释,session中文翻译是会话,在这里就是zk的server和client维护的一个具有一些特别属性的网络连接,网络连接这里就是socket连接,一些特别的属性包括
- sessionId:唯一标示一个会话
- sessionTimeout:这个连接的超时时间,超过这个时间server就会把连接断开
所以session建立的两步就是
- 建立socket连接
- client发起建立session请求,server建立一个实例来维护这个连接
server收到ConnectRequest之后,按照正常处理io的方式处理这个request,server端的主要操作是
- 反序列化为ConnectRequest
- 根据request中的sessionId来判断是新的session连接还是session重连
- 如果是新连接
- 生成sessionId
- 创建新的SessionImpl并放入org.apache.zookeeper.server.SessionTrackerImpl#sessionExpiryQueue
- 封装该请求为新的request在processorChain中传递,最后交给FinalRequestProcessor处理
- 如果是重连
- 关闭sessionId对应的原来的session
- 关闭原来的socket连接
- sessionImp会在sessionExpiryQueue中由于过期被清理
- 重新打开一个session
- 将原来的sessionId设置到当前的NIOServerCnxn实例中,作为新的连接的sessionId
- 校验密码是否正确密码错误的时候直接返回给客户端,不可用的session
- 密码正确的话,新建SessionImpl
- 返回给客户端sessionId
- 关闭sessionId对应的原来的session
- 如果是新连接
总体流程是
其中有一个session生成算法我们来看下
public static long initializeNextSession(long id) {
// sessionId是long类型,共8个字节,64位
long nextSid;
// 取时间戳的的低40位作为初始化sessionId的第16-55位,这里使用的是无符号右移,不会出现负数
nextSid = (Time.currentElapsedTime() << 24) >>> 8;
// 使用serverId(配置文件中指定的myid)作为高8位
nextSid = nextSid | (id <<56);
// nextSid为long的最小值,这中情况不可能出现,这里只是作为一个case列在这里
if (nextSid == EphemeralType.CONTAINER_EPHEMERAL_OWNER) {
++nextSid; // this is an unlikely edge case, but check it just in case
}
return nextSid;
}
初始化sessionId的组成
myid(1字节)+截取的时间戳低40位(5个字节)+2个字节(初始化都是0)
每个server再基于这个id不断自增,这样的算法就保证了每个server的sessionId是全局唯一的。
总结
session在zk框架中是一个重要概念,很多功能都依赖于session,比如临时节点,session关闭后就自动删除了。本文主要介绍了session的建立过程中client和server各自的处理方式。
zookeeper源码 — 四、session建立的更多相关文章
- Zookeeper 源码(四)Zookeeper 服务端源码
Zookeeper 源码(四)Zookeeper 服务端源码 Zookeeper 服务端的启动入口为 QuorumPeerMain public static void main(String[] a ...
- Zookeeper 源码(三)Zookeeper 客户端源码
Zookeeper 源码(三)Zookeeper 客户端源码 Zookeeper 客户端主要有以下几个重要的组件.客户端会话创建可以分为三个阶段:一是初始化阶段.二是会话创建阶段.三是响应处理阶段. ...
- Zookeeper 源码(五)Leader 选举
Zookeeper 源码(五)Leader 选举 前面学习了 Zookeeper 服务端的相关细节,其中对于集群启动而言,很重要的一部分就是 Leader 选举,接着就开始深入学习 Leader 选举 ...
- Zookeeper源码(启动+选举)
简介 关于Zookeeper,目前普遍的应用场景基本作为服务注册中心,用于服务发现.但这只是Zookeeper的一个的功能,根据Apache的官方概述:"The Apache ZooKeep ...
- zookeeper源码分析之五服务端(集群leader)处理请求流程
leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...
- zookeeper源码分析之四服务端(单机)处理请求流程
上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- Hadoop,HBase,Zookeeper源码编译并导入eclipse
基本理念:尽可能的参考官方英文文档 Hadoop: http://wiki.apache.org/hadoop/FrontPage HBase: http://hbase.apache.org/b ...
- Zookeeper 源码(七)请求处理
Zookeeper 源码(七)请求处理 以单机启动为例讲解 Zookeeper 是如何处理请求的.先回顾一下单机时的请求处理链. // 单机包含 3 个请求链:PrepRequestProcessor ...
随机推荐
- 有关git clone 下载速度变慢的解决方法
使用提示:请注意一下,以下方法是在搭有梯子的情况下进行的,也就是说在有梯子的情况下,下载速度始终很慢,使用了以下方法用梯子下载达到正常速度,并没有尝试修复过后不用梯子下载. 所以,如果使用了以下方法, ...
- Linux异常体系之stubs_offset
转自 http://www.xuebuyuan.com/2208550.html 在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0x00000000,另一个是0xf ...
- stm32之Cortex系统定时器(SysTick)
转载自:http://www.21ic.com/app/mcu/201811/781135.htm SysTick时钟,俗称“嘀嗒定时器”,它能按设定的时间产生一次中断.控制工程代码中随处可见形如 ...
- P2014 选课(树形背包)
P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有 ...
- 《变革之心》读后感——《Scrum实战》第2次课作业
刚读了几篇序言.导言和第一个故事,因此读后感可能不全面,先写一下一点儿感受吧. <变革之心>讲的是组织变革,而组织变革是以个人变革为基础的,本书的观点就是在个人变革上,“目睹--感受--变 ...
- 《Scrum实战》第1次课课后任务
1.必做任务:从知行角度总结T平台 从知行角度总结T平台 头(知识,学习) 做得好的 宣贯会 引入敏捷思想 敏捷宣言 敏捷原则 质量风险前移原则 引入最佳实践 包括了XP的大部分实践 不足 项目管理框 ...
- Django 二——models(admin、ORM),一对一、一对多、多对多操作,all、values、value_list的对比
内容概要 1.关系对象映射ORM 2.admin的配置(选修) 3.all().values().value_list()的对比 4.数据库操作(一对一.一对多.多对多) 5.HttpResponse ...
- 学习正则有感by魔芋(命名问题)
魔芋: 事实上,我是反感一些特殊的名词.一些名词看上去就让人感觉到抗拒. 关于一个概念用不同的名词来定义,简直是太糟糕了. 举个例子: 匹配一个后面带有exp2的exp1的正则. 写法: exp1(? ...
- luogu2865 [USACO06NOV]路障Roadblocks 次短路
注意:如果是这么个写法,堆数组要开成n+m的. 为什么呢?设想一下从1到2有m条长度递减的路,这岂不是要入队m次-- #include <algorithm> #include <i ...
- [解读REST] 0.REST 相关参考资料
Web之父 Tim Berners Lee :https://en.wikipedia.org/wiki/Tim_Berners-Lee 世界上诞生的第一个网站:http://info.cern.ch ...