ZooKeeper 避坑实践:如何调优 jute.maxbuffer

news2024/11/26 9:57:16

作者:子葵

背景

在日常运维 ZooKeeper 中,经常会遇到长时间无法选主,恢复时进程启动又退出,进而导致内存暴涨,CPU飙升,GC频繁,影响业务可用性,这些问题有可能和 jute.maxbuffer 的设置有关。本篇文章就深入 ZooKeeper 源码,一起探究一下ZooKeeper 的 jute.maxbuffer 参数的最佳实践。

在这里插入图片描述
在这里插入图片描述

分析

首先我们通过 ZooKeeper 的官网上看到 jute.maxbuffer 的描述:

jute.maxbuffer :

(Java system property:jute.maxbuffer).

…, It specifies the maximum size of the data that can be stored in a znode. The unit is: byte. The default is 0xfffff(1048575) bytes, or just under 1M.

When jute.maxbuffer in the client side is greater than the server side, the client wants to write the data exceeds jute.maxbuffer in the server side, the server side will get java.io.IOException: Len error

When jute.maxbuffer in the client side is less than the server side, the client wants to read the data exceeds jute.maxbuffer in the client side, the client side will get java.io.IOException: Unreasonable length or Packet len is out of range!

从官网的描述中我们可以知道,jute.maxbuffer 能够限制 Znode 大小,需要在Server端和Client端合理设置,否则有可能引起异常。

但事实并非如此,我们在ZooKeeper的代码中寻找 jute.maxbuffer 的定义和引用:

public static final int maxBuffer = Integer.getInteger("jute.maxbuffer", 0xfffff);

在 org.apache.jute.BinaryInputArchive 类型中通过 System Properties 读取到 jute.maxbuffer的值,可以看到默认值是1M,checkLength 方法引用了此静态值:

// Since this is a rough sanity check, add some padding to maxBuffer to
// make up for extra fields, etc. (otherwise e.g. clients may be able to
// write buffers larger than we can read from disk!)
private void checkLength(int len) throws IOException {
    if (len < 0 || len > maxBufferSize + extraMaxBufferSize) {
        throw new IOException(UNREASONBLE_LENGTH + len);
    }
}

只要参数 len 超过maxBufferSize 和 extraMaxBufferSize的和,就会抛出 Unreasonable length 的异常,在生产环境中这个异常往往会导致非预期的选主或者Server无法启动。

再看一下 extraMaxBufferSize 的赋值:

static {
    final Integer configuredExtraMaxBuffer =
        Integer.getInteger("zookeeper.jute.maxbuffer.extrasize", maxBuffer);
    if (configuredExtraMaxBuffer < 1024) {
        extraMaxBuffer = 1024;
    } else {
        extraMaxBuffer = configuredExtraMaxBuffer;
    }
}

可以看到 extraMaxBufferSize 默认会使用maxBuffer的值,并且最小值为 1024 (这里是为了和以前的版本兼容),因此在默认的情况下,checkLength方法抛出异常的阈值是 1M + 1K。

接着我们看一下 checkLength 方法的引用链:

有两个地方引用到了 checkLength 方法:即 org.apache.jute.BinaryInputArchive 类型的 readString 和 readBuffer方法。

    public String readString(String tag) throws IOException {
        ......
        checkLength(len);
        ......
    }

    public byte[] readBuffer(String tag) throws IOException {
        ......
        checkLength(len);
        ......
    }

而这两个方法在几乎所有的 org.apache.jute.Recod 类型中都有引用,也就是说 ZooKeeper 中几乎所有的序列化对象在反序列化的时候都会进行 checkLength 检查,因此可以得出结论 jute.maxbuffer 不仅仅限制 Znode 的大小,而是所有调用 readString 和 readBuffer 的 Record 的大小。

这其中就包含 org.apache.zookeeper.server.quorum.QuorumPacket 类型。

此类型是在 Server 进行 Proposal 时传输数据使用的序列化类型,包含写请求产生的 Txn 在 Server 之间进行同步时传递数据都是通过此类型进行序列化的,如果事务太大就会导致 checkLength 失败抛出异常,如果是普通的写请求,因为在请求收到的时候就会 checkLength,因此在预处理请求的时候就可以避免产生过大的 QuorumPacket,但是如果是 CloseSession 请求,在这种情况下就可能出现异常。

我们可以通过 PreRequestProcessor的processRequest方法看到生成CloseSessionTxn 的过程:

    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) {
                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.getEphemeralOwner() == request.sessionId) {
                        es.add(c.path);
                    }
                }  
                if (ZooKeeperServer.isCloseSessionTxnEnabled()) {
                    request.setTxn(new CloseSessionTxn(new ArrayList<String>(es)));
                }
                ......
    }

CloseSession 请求很小一般都可以通过 checkLength 的检查,但是 CloseSession 产生的事务却有可能很大,可以通过 org.apache.zookeeper.txn.CloseSessionTxn 类型的定义可知此 Txn 中包含所有此 Session 创建的 ephemeral 类型的 Znode,因此,如果一个Session创建了很多 ephemeral 类型的 Znode,当此 Session 有一个 CloseSession 的请求经过 Server 处理的时候,Leader 向 Follower 进行 proposal 的时候就会出现一个特别大的 QuorumPacket,导致在反序列化的时候进行 checkLength 检查的时候会抛出异常,可以通过 Follower 的 followLeader 方法看到,在出现异常的时候,Follower 会断开和 Leader 的连接:

void followLeader() throws InterruptedException {
        ......
        ......
        ......
                // create a reusable packet to reduce gc impact
                QuorumPacket qp = new QuorumPacket();
                while (this.isRunning()) {
                    readPacket(qp);
                    processPacket(qp);
                }
            } catch (Exception e) {
                LOG.warn("Exception when following the leader", e);
                closeSocket();

                // clear pending revalidations
                pendingRevalidations.clear();
            }
        } finally {
        ......
        ......
    }

当超过半数的 follower 都因为 QuorumPacket 过大而无法反序列化的时候就会导致集群重新选主,并且如果原本的 Leader 在选举中获胜,那么这个 Leader 就会在从磁盘中 load 数据的时候,从磁盘中读取事物日志的时候,读取到刚刚写入的特别大的 CloseSessionTxn 的时候 checkLength 失败,导致 Leader 状态又重新进入 LOOKING 状态,集群又开始重新选主,并且一直持续此过程,导致集群一直处于选主状态

原因

集群非预期选主,持续选主或者server 无法启动,经过以上分析有可能就是在 jute.maxbuffer 设置不合理,连接到集群的某一个 client 创建了特别多的 ephemeral 类型节点,并且当这个 session 发出 closesession 请求的时候,导致 follower 和 Leader 断连。最终导致集群选主失败或者集群无法正常启动。

最佳实践建议

首先如何发现集群是因为jute.maxbuffer 设置不合理导致的集群无法正常选主或者无法正常启动 ?

  1. 在checkLength方法检查失败的时候会抛出异常,关键字是 Unreasonable length,同时 follower 会断开和 Leader的连接,关键字是 Exception when following the leader,可以通过检索关键字快速确认。

在这里插入图片描述

  1. ZooKeeper 在新版本中提供了 last_proposal_size metrics 指标,可以通过此指标监控集群的 proposal 大小数据,当有 proposal 大于 jute.maxbuffer 的值的时候就需要排查问题。

jute.maxbuffer 如何正确设置?

  1. 官方文档首先建议我们在客户端和服务端正确的设置 jute.maxbuffer ,最好保持一致,避免非预期的 checkLength 检查失败。

  2. 官方文档建议 jute.maxbuffer 的值不宜过大,大的 Znode 可能会导致 Server 之间同步数据超时,在大数据请求到达 Server 的时候就被拦截掉。

  3. 在实际的生产环境中,为了保证生产环境的稳定,如果 jute.maxbuffer 的值设置过小,服务端有可能持续不可用,需要需要更改 jute.maxbuffer 的值才能正常启动,因此这个值也不能太小。

  4. Dubbo 低版本存在重复注册问题,当重复注册达到一定的量级,就有可能触发这个阈值(1M),Dubbo 单节点注册的 Path 长度按照 670 字节计算,默认阈值最多容纳 1565 次重复注册,因此在业务侧需要规避重复注册的问题。综上,在使用的 ZooKeeper 的过程中,jute.maxbuffer 的设置还需要考虑到单个 session 创建过多的 ephemeral 节点这一种情况,合理配置 jute.maxbuffer 的值。

在 MSE ZooKeeper 中,可以通过控制台快捷修改 jute.maxbuffer 参数:

在这里插入图片描述

设置 MSE ZooKeeper 选主时间告警以及节点不可用告警。首先进入告警管理页面,创建新的告警:

在这里插入图片描述

分组选择 ZooKeeper 专业版,告警项选择选主时间,然后设置阈值:

在这里插入图片描述

POD 状态告警,分组选择 ZooKeeper 专业版,告警项选择 ZooKeeper 单 POD状态:

在这里插入图片描述

配置节点不可用和选主时间告警,及时发现问题进行排查。

运营活动

重磅推出 MSE 专业版,更具性价比,可直接从基础版一键平滑升级到专业版!

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/48272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kotlin高仿微信-第17篇-单聊-转账

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

深度学习基础知识回顾

1. Dataset调用了什么接口&#xff1f; 回答应该是__len__方法和__getitem__方法。 之前写过一篇关于Dataset和Dataloader的介绍&#xff1a; http://t.csdn.cn/b4x0hhttp://t.csdn.cn/b4x0h 2. 目标检测里面用了哪些损失函数&#xff1f; 我的回答是Focal Lo…

【Linux】权限讲解

一、什么是权限 1、权限概念 权限随处可见&#xff0c;在生活中&#xff0c;腾讯非VIP用户不能观看VIP视频&#xff0c;看小说也需要会员&#xff0c;所以权限是限制人的&#xff0c;一件事是否允许被谁做。在Linux系统中也有许多权限&#xff0c;访问文件需要权限&#xff0c…

Kafka: Windows环境-单机部署和伪集群、集群部署

1. kafka 单机版部署 1.1 zookeeper 安装 &#xff08;1&#xff09;下载安装包 官网&#xff1a;Apache ZooKeeper 我用的是 apache-zookeeper-3.7.1-bin.tar.gz 注意&#xff1a;zookeeper的安装路径不要有中文&#xff0c;建议也不要有空格,比如Program Files这样的路径…

移动跨平台技术方案浅析

随着互联网产品逐渐兴起&#xff0c;越来越多产品体验从线下搬到了线上&#xff0c;尤其是移动互联网产品相关&#xff0c;所以很多企业就会更加重视降本增效&#xff0c;以最快的速度推出质量满意度高、用户体验性好的产品&#xff0c;那么就顺势催生了很多跨端跨平台方案。 …

并发编程九 线程池Executor框架

一 线程 线程是调度CPU资源的最小单位&#xff0c;线程模型分为KLT模型与ULT模型&#xff0c;JVM使用的KLT模型&#xff1b; Java线程与OS线程保持1:1的映射关系&#xff0c;也就是说有一个java线程也会在操作系统里有一个对应的线程。Java线程有多种生命状态 NEW,新建 RUNN…

一文带你深入了解Linux IIO 子系统

【推荐阅读】 一文剖析Linux内核中内存管理 分析linux启动内核源码 关于如何快速学好&#xff0c;学懂Linux内核。内含学习路线 工业场合里面也有大量的模拟量和数字量之间的转换&#xff0c;也就是我们常说的 ADC 和 DAC。而且随着手机、物联网、工业物联网和可穿戴设备的…

[第二十二篇]——Docker 安装 MongoDB

Docker 安装 MongoDB MongoDB 是一个免费的开源跨平台面向文档的 NoSQL 数据库程序。 1、查看可用的 MongoDB 版本 访问 MongoDB 镜像库地址&#xff1a; 。 可以通过 Sort by 查看其他版本的 MongoDB&#xff0c;默认是最新版本 mongo:latest。 你也可以在下拉列表中找到…

高校社团管理系统的设计与实现

摘要 随着互联网技术的高速发展&#xff0c;人们生活的各方面都受到互联网技术的影响。现在的社团成员可以通过互联网技术就能实现不在学校&#xff0c;在家也可以查看社团信息并能进行申请加入&#xff0c;简单、快捷的方便了社团成员的社交生活。同样的&#xff0c;在人们的工…

【Spring项目中的统一处理异常】

目录 1. 统一处理异常的机制 2. 关于统一处理异常的方法 3. 关于处理异常的方法的执行特点 1. 统一处理异常的机制 Spring MVC框架提供了统一处理异常的机制&#xff01;表现为每种类型的异常只需要写一段&#xff08;写一次&#xff09;处理此异常的代码即可&#xff0c;项…

需求:针对同一个表格多次导入是否要做判断(此项目是用得若依)

每次上传表时&#xff0c;将上传的表名与以往上传的表名做对比&#xff0c;如果相同&#xff0c;则提示表已经有记录&#xff0c;是否上传相同表并结束此方法。 实现思路&#xff1a; 首先&#xff0c;每次上传表都要把表明记录一下&#xff0c;可以新建一个表&#xff08;数…

天宇优配|离岸人民币狂拉逾千点!中概股暴涨!B站涨22%

当地时间周二&#xff0c;美股三大指数收盘涨跌纷歧。道指涨0.01%&#xff0c;标普500指数跌0.16%&#xff0c;纳指跌0.59%。 抢手中概股领涨&#xff0c;纳斯达克我国金龙指数大涨5.04%&#xff0c;哔哩哔哩&#xff08;B站&#xff09;涨超22%。大型科技股多数跌落&#xff0…

小程序中的confirm-type设置键盘的确认按钮

详情&#xff1a; confirm-type是很多小程序组件中的一种设置&#xff0c;用于改变输入键盘右下角的确认按钮。比如说&#xff0c;正常情况下&#xff0c;键盘上的默认提示可能是完成&#xff0c;但是你可以通过confirm-type将其设置为发送&#xff0c;搜索等&#xff0c;在特…

间隔不到一年开两店,温州鸿雁全屋智能经销商透露了他的生意经

作者 | 牧之 编辑 | 小沐 出品 | 智哪儿 zhinaer.cn编者按&#xff1a;间隔不到一年&#xff0c;连续开设了两家全屋智能体验店。这是发生在温州的渠道商故事。本期专访&#xff0c;「智哪儿」对话浙江林上智能科技有限公司总经理朱飞隆先生。他为何做智能家居&#xff1f;为何…

翻转单词序列、按之字形顺序打印二叉树、二叉搜索树的第k个节点

1、翻转单词序列 本题考点&#xff1a;子串划分&#xff0c;子串逆置 牛客链接 题目描述&#xff1a; 牛客最近来了一个新员工Fish&#xff0c;每天早晨总是会拿着一本英文杂志&#xff0c;写些句子在本子上。同事Cat对Fish写的内容颇感兴趣&#xff0c;有一天他向Fish借来翻…

Python数据库编程之关系数据库API规范

Python关系数据库API规范 对于关系数据库的访问&#xff0c;Python社区已经制定出一个标准&#xff0c;称为Python Database API Specification。Mysql&#xff0c;Oracal等特定数据库模块遵从这一规范&#xff0c;而且可以添加更多特性。 高级数据库API定义了一组用于连接数…

三十六、Java 泛型

Java 泛型 Java 泛型&#xff08;generics&#xff09;是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制&#xff0c;该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。 假定我们有这…

火山引擎 DataLeap 的 Data Catalog 系统公有云实践

Data Catalog 通过汇总技术和业务元数据&#xff0c;解决大数据生产者组织梳理数据、数据消费者找数和理解数的业务场景。本篇内容源自于火山引擎大数据研发治理套件 DataLeap 中的 Data Catalog 功能模块的实践&#xff0c;主要介绍 Data Catalog 在公有云部署和发布中遇到挑战…

5. LSTM的C++实现

[C 基于Eigen库实现CRN前向推理] 第三部分&#xff1a;TransposedConv2d实现 &#xff08;含dilation&#xff09; 前言&#xff1a;(Eigen库使用记录)第一部分&#xff1a;WavFile.class (实现读取wav/pcm,实现STFT)第二部分&#xff1a;Conv2d实现第三部分&#xff1a;Tran…

你知道不同U盘在ARM+Linux下的读写速率吗?

优秀的产品离不开完善的测试&#xff0c;即使一个简单的USB接口也要确保稳定性及兼容性。不同的U盘在ARMLinux板卡下的兼容性、速率怎么样呢&#xff1f;本文将为大家提供测试参考数据及详细测试步骤&#xff01; 1. 测试准备 主控选用最近发布的64位Cortex-A55核心板&#xff…