浅谈 Zookeeper 中的 Session 与 临时节点

本文深入探讨了Zookeeper中的Session与临时节点,包括它们的创建、生命周期管理以及在Zookeeper集群中的工作原理。临时节点在会话消失时被删除,常用于注册中心和分布式锁。文章详细分析了Session的创建、续期、关闭过程,以及在单机和集群模式下的行为。Zookeeper集群通过维护globalSession和localSession确保session和临时节点的持久性,即使在节点重启或网络故障后也能保持一致性。
摘要由CSDN通过智能技术生成

→ zookeeper version 3.6.1

一、临时节点

    相信我们对 zookeeper 的临时节点都不陌生,与持久节点的最大区别就是会随着会话的消失而删除,这种特性常被用来作注册中心分布式锁等,下面来分析下临时节点的实现:

    

    单机模式下由三个 processor 构成了整个处理链路

        PrepRequestProcessor 主要负责请求的反序列化,串行化处理和权限校验等

        SyncRequestProcessor 主要负责数据的持久化

        FinalRequestProcessor 主要负责对内存中数据的更新

    这里主要看下第三个处理器是怎样处理临时节点的

    org.apache.zookeeper.server.DataTree#createNode(java.lang.String, byte[], java.util.List<org.apache.zookeeper.data.ACL>, long, int, long, long, org.apache.zookeeper.data.Stat)

public void createNode(final String path, byte[] data, List<ACL> acl, long ephemeralOwner, int parentCVersion, long zxid, long time, Stat outputStat) throws 	KeeperException.NoNodeException, KeeperException.NodeExistsException {
	// ... 省略部分代码
	// 拿到当前节点的类型
    EphemeralType ephemeralType = EphemeralType.get(ephemeralOwner);
	// 是否是 container 节点
    if (ephemeralType == EphemeralType.CONTAINER) {
        containers.add(path);
	// 是否是带过期时间的节点
    } else if (ephemeralType == EphemeralType.TTL) {
        ttls.add(path);
	// 处理临时节点的逻辑
    } else if (ephemeralOwner != 0) {
        HashSet<String> list = ephemerals.get(ephemeralOwner);
        if (list == null) {
            list = new HashSet<String>();
            ephemerals.put(ephemeralOwner, list);
        }
        synchronized (list) {
            list.add(path);
        }
    }
	// 省略部分代码
}

 

    其实不难看懂,临时节点底层就是维护了一个 Map<Long, <Set<String>>>,key保存了 ephemeralOwner,value 则是临时节点的 path,现在只要看下 ephemeralOwner 是什么就好了,下面看下该方法调用方的入参

public ProcessTxnResult processTxn(TxnHeader header, Record txn, boolean isSubTxn) {
    ProcessTxnResult rc = new ProcessTxnResult();

    try {
        //...省略部分代码
        case OpCode.create:
            CreateTxn createTxn = (CreateTxn) txn;
            rc.path = createTxn.getPath();
            createNode(
                createTxn.getPath(),
                createTxn.getData(),
                createTxn.getAcl(),
				// 判断是否是临时节点,是临时节点就传入 clientId
                createTxn.getEphemeral() ? header.getClientId() : 0,
                createTxn.getParentCVersion(),
                header.getZxid(),
                header.getTime(),
                null);
            break;
        //...省略部分代码
        }
    } catch (KeeperException e) {
        LOG.debug("Failed: {}:{}", header, txn, e);
        rc.err = e.code().intValue();
    } catch (IOException e) {
        LOG.debug("Failed: {}:{}", header, txn, e);
    }
}

    上述代码能发现入参是 clientId,可能这个 id 还是不太明确,接下来去找下构造 TxnHeader 的地方,也就是第一个 processor

    org.apache.zookeeper.server.PrepRequestProcessor#pRequest2Txn

protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize) throws KeeperException, IOException, RequestProcessorException {
    if (request.getHdr() == null) {
        // cxid 是客户端生成的 xid,zxid 是服务端生成的
        request.setHdr(new TxnHeader(request.sessionId, request.cxid, zxid,
                Time.currentWallTime(), type));
    }
    //...省略部分代码
}

    clientId 就是 TxnHeader 的第一个构造参数,也就是 sessionId,其实也不难想象,要想达到这种效果需要跟会话产生关系

二、单机 Session

    这里就简单过一下不会讲的很细,主要回顾下与临时节点有关的特性

    

    1、发送协商请求:这部分主要是对 session 创建以及过期时间的协商,为什么叫协商是因为这两边的任何一方都不能擅自决定这两个参数

        session 的创建:如果请求方发送协商请求的时候 sessionId = 0 ,那么服务端会生成一个 sessionId 返回给请求端,正常情况下初始化都为 0 ,只有当进行重连的时候才会携带上次请求的 sessionId

        过期时间:请求方协商请求的时候也会携带过期时间,服务端启动也会初始化过期时间,如果两端不一致服务端会返回较小的时间作为过期时间

    2、协商结果响应

        主要就是返回协商过后的过期时间与 sessionId

    3、增删改查 与 ping

        其实这里做的主要的就是对 session 的续期,每请求一次都会给 session 续期,每次增删改查都会,期间没有增删改查请求端会记录上次请求的时间,如果发现 session 快过期则会发送 ping 请求

    4、session 的关闭:session 关闭主要可能来自两方面

        zk server 内部线程轮询发现有过期的 session 于是构造 session 过期的请求发送到上面介绍的 3个 处理器中

        请求端发送关闭 session 的请求

    看了以上的逻辑那我们来聊下当 processor 接收到 close 请求的时候会做些什么处理呢

    org.apache.zookeeper.server.PrepRequestProcessor#pRequest2Txn

protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize) throws KeeperException, IOException, RequestProcessorException {
	// ... 省略部分代码
	case OpCode.closeSession:
        long startTime = Time.currentElapsedTime();
        synchronized (zks.outstandingChanges) {
            // 拿到 session 对应的临时节点
            Set<String> es = zks.getZKDatabase().getEphemerals(request.sessionId);
                for (ChangeRecord c : zks.outstandingChanges) {
                    if (c.stat == null) {
                        // Doing a delete
                        es.remove(c.path);
                    } else if (c.stat
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值