zookeeper日志中Error KeeperErrorCode NoNode 错误的排查过程

本文主要是介绍zookeeper日志中Error KeeperErrorCode NoNode 错误的排查过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、需求背景

zookeeper日志中出现不少以下类似错误:

2021-05-11 09:03:28,004 [myid:3] - INFO  [ProcessThread(sid:3 cport:-1)::PrepRequestProcessor@648] - Got user-level KeeperException when processing sessionid:0x27918ca11740225 type:create cxid:0x1c3bbb zxid:0x20008db83 txntype:-1 reqpath:n/a Error Path:/dlock/dhy/330922966870720/GL/GL_Acc_Insert5601048 Error:KeeperErrorCode = NoNode for /dlock/dhy/330922966870720/GL/GL_Acc_Insert5601048

二、解决方案

由日志可以得出的信息:

1、dlock是我们项目中分布式锁使用的命名空间

2、dhy/330922966870720/GL/GL_Acc_Insert5601048是我们项目中一个模块中的分布式加锁点 [临时顺序节点]

3、其它加锁点有类似日志信息

分布式锁工具是经过测试并且在生产环境已经使用很长时间了,从功能上来说肯定是没有问题的,只是这条日志信息反馈了可能有实现不太优雅的地方,作为架构师要精益求精,解决方案不但要可行而且要优雅完美。

下面通过 源码分析org.apache.curator.framework获取分布式锁的过程来 寻找产生这条日志的根本原因。

三、代码实现

下面是我几年前写的一把分布式锁工具类(简化版)

/*** 基于zookeeper的分布式锁实现* @author lvaolin* @create 18/3/7 上午10:38*/
public class DlockUtil {private static final Logger logger = LoggerFactory.getLogger(DlockUtil.class);private static final String ZK_LOCK_PATH = "/dhy/";private static CuratorFramework client = null;static {RetryPolicy retryPolicy = new RetryOneTime(60000);client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181").connectionTimeoutMs(5000) .retryPolicy(retryPolicy).namespace("dlock").build();client.start();logger.info("zk client start successfully!");}/*** 获取分布式锁,如果获取失败会立刻返回,不会等待* 注意:调用方需要保留 此方法的 返回值,在finally里调用releaseDLock(lock) 进行锁释放** @param orgId      企业ID* @param moduleName 业务模块名称  英文* @param lockName   锁名称(具体业务操作命名)  英文* @return 返回值为null 代表 获取锁失败,非null代表 获取锁成功*/public static InterProcessMutex getDLock(String orgId, String moduleName, String lockName) {return getDLock(orgId, moduleName, lockName, 0);}/*** 获取分布式锁(可指定获取锁超时时间)* 注意:调用方需要保留 此方法的 返回值,在finally里调用releaseDLock(lock) 进行锁释放* InterProcessMutex.isAcquiredInThisProcess() 可以准确判断是否还持有锁** @param orgId      企业ID* @param moduleName 业务模块名称  英文* @param lockName   锁名称(具体业务操作命名)  英文* @param second     超时时间 :-1代表一直堵塞,直到获取成功;0代表不等待,如果获取失败,立刻返回;大于0 则表示会尝试排队获取锁,直到超时就返回* @return 返回值为null 代表 获取锁失败,非null代表 获取锁成功*/public static InterProcessMutex getDLock(String orgId, String moduleName, String lockName, long second) {if (client == null) {logger.error("client == null,未启用分布式锁DLOCK");return null;}InterProcessMutex lock = new InterProcessMutex(client, ZK_LOCK_PATH + orgId + "/" + moduleName + "/" + lockName);try {if (second == -1) {//-1代表一直堵塞下去直到获取成功logger.info("-1 正在获取分布式锁..." + Thread.currentThread().getName());lock.acquire();logger.info("获取分布式锁成功" + Thread.currentThread().getName());return lock;} else {//获取不到锁就返回获取锁失败logger.info(second + " 正在获取分布式锁..." + Thread.currentThread().getName());if (lock.acquire(second, TimeUnit.SECONDS)) {logger.info("获取分布式锁成功" + Thread.currentThread().getName());return lock;} else {return null;}}} catch (Exception e) {logger.error("获取分布式锁失败:" + e.getMessage(), e);return null;}}/*** 释放分布式锁* 注意:此方法需要在finally里进行调用** @param lock 要释放的锁,来自于getDLock()的返回值*/public static void releaseDLock(InterProcessMutex lock) {try {if (lock != null) {//判断是否还持有锁if (lock.isAcquiredInThisProcess()) {lock.release();//是否是最后一把锁if (!lock.isAcquiredInThisProcess()) {lock = null;}} else {lock = null;}logger.info("释放锁成功:" + Thread.currentThread().getName());} else {logger.info("无需释放" + Thread.currentThread().getName());}} catch (Exception e) {logger.error("释放分布式锁失败:" + e.getMessage(), e);}}
}

关键代码:

//定义一把锁

InterProcessMutex lock = new InterProcessMutex(client, ZK_LOCK_PATH + orgId + "/" + moduleName + "/" + lockName);

//获取这把锁,返回值代表是否成功

boolean result = lock.acquire(second, TimeUnit.SECONDS)  

下面深究 lock.acquire的源代码(由于代码较多,我只贴关键代码了)

关键方法:InterProcessMutex.internalLock(long time, TimeUnit unit)

ffa63175fc118082661b5a7addfd34e4.png

看下internals.attemptLock 方法

532f17ceaef9b8bcd9b6c8e846836a2b.png

日志已经显示了是在创建节点时报的,所以看driver.createsTheLock就对了

关键代码,可以看到创建的是 "临时有序节点类型","自动创建父目录"

44ea8df8aaeca233c5e5e7f0faae26ba.png

4b9cde83790a52b8bf8e4eea87ea1d8c.png

看下protectedPathInForeground方法

e4ea6a763085b69f1441b3af35918da3.png

call方法关键内容如下:

b429139d004909bc9b35af7a3ccfee43.png

在这里我们找到了 NoNodeException的捕获,并且我们知道了是由于父节点不存在才抛出的异常。

ZKPaths.mkdirs 方法会自动创建所有的上级节点,只留末级节点不创建,最后再次创建末级节点一定能成功

至此,我们大致明白了错误的原因了。是因为要锁定的末级目录的父节点不存在造成的,框架自动处理了这种情况,默默的自动创建了父目录,所以在应用层是无感知的,只能在zookeeper的日志里发现此问题。

四、总结

1、zookeeper的原生api是不支持 自动创建父目录的。

2、apache.curator 框架封装了自动创建父目录的过程,应用层无感知,友好。

3、要解决zookeeper中日志打印的问题就需要在创建分布式锁之前判断并创建父目录,或者path中不带任何父目录直接追加末级节点。

4、不要忽略日志中的可疑日志,细究总是能发现问题。

这篇关于zookeeper日志中Error KeeperErrorCode NoNode 错误的排查过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/161471

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

我在移动打工的日志

客户:给我搞一下录音 我:不会。不在服务范围。 客户:是不想吧 我:笑嘻嘻(气笑) 客户:小姑娘明明会,却欺负老人 我:笑嘻嘻 客户:那我交话费 我:手机号 客户:给我搞录音 我:不会。不懂。没搞过。 客户:那我交话费 我:手机号。这是电信的啊!!我这是中国移动!! 客户:我不管,我要充话费,充话费是你们的 我:可是这是移动!!中国移动!! 客户:我这是手机号 我:那又如何,这是移动!你是电信!!

ZooKeeper 中的 Curator 框架解析

Apache ZooKeeper 是一个为分布式应用提供一致性服务的软件。它提供了诸如配置管理、分布式同步、组服务等功能。在使用 ZooKeeper 时,Curator 是一个非常流行的客户端库,它简化了 ZooKeeper 的使用,提供了高级的抽象和丰富的工具。本文将详细介绍 Curator 框架,包括它的设计哲学、核心组件以及如何使用 Curator 来简化 ZooKeeper 的操作。 1

Solr 使用Facet分组过程中与分词的矛盾解决办法

对于一般查询而言  ,  分词和存储都是必要的  .  比如  CPU  类型  ”Intel  酷睿  2  双核  P7570”,  拆分成  ”Intel”,”  酷睿  ”,”P7570”  这样一些关键字并分别索引  ,  可能提供更好的搜索体验  .  但是如果将  CPU  作为 Facet  字段  ,  最好不进行分词  .  这样就造成了矛盾  ,  解决方法

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo