当前位置: 首页 > news >正文

网站的建设和编程网站制作学习

网站的建设和编程,网站制作学习,用jsp做一网站的流程,wordpress企业网站建设一、背景 ZooKeeper#xff08;ZK#xff09;是一个诞生于2007年的分布式应用程序协调服务。尽管出于一些特殊的历史原因#xff0c;许多业务场景仍然不得不依赖它。比如#xff0c;Kafka、任务调度等。特别是在 Flink 混合部署 ETCD 解耦 时#xff0c;业务方曾要求绝对…一、背景 ZooKeeperZK是一个诞生于2007年的分布式应用程序协调服务。尽管出于一些特殊的历史原因许多业务场景仍然不得不依赖它。比如Kafka、任务调度等。特别是在 Flink 混合部署 ETCD 解耦 时业务方曾要求绝对的稳定性并强烈建议不要使用自建的 ZooKeeper。出于对稳定性的考量采用了阿里的 MSE-ZK。自从 2022 年 9 月份开始使用至今我们没有遇到任何稳定性问题SLA 的可靠性确实达到了 99.99%。 在 2023 年部分业务使用了自建的 ZooKeeperZK集群然后使用过程中 ZK 出现了几次波动随后得物 SRE 开始接管部分自建集群并进行了几轮稳定性加固的尝试。接管过程中我们发现ZooKeeper在运行一段时间后内存占用率会不断增加容易导致内存耗尽OOM的问题。我们对这一现象非常好奇因此也参与了解决这个问题的探索过程。 二、探索分析 确定方向 在排查问题时我们非常幸运地发现了一个测试环境的故障现场该集群中的两个节点恰好处于OOM的边缘状态。 有了故障现场那么一般情况下距离成功终点只剩下50%。 内存偏高按以往的经验来看要么是非堆要么是堆内有问题。从火焰图和jstat 都能证实是堆内的问题。 如图所示说明 JVM 堆内存在某种资源占用了大量的内存并且FGC都无法释放。 内存分析 为了探究 JVM 堆中内存占用分布我们立即做了一个JVM堆Dump。分析发现 JVM 内存被 childWatches 和 dataWatches 大量占用。 dataWatches跟踪 znode 节点数据的变化。 childWatches跟踪 znode 节点结构(tree)的变化。 childWatches和dataWatches同源于WatcherManager。 经过资料排查我们发现 WatcherManager 主要负责管理 Watcher。ZooKeeperZK客户端首先将 Watcher 注册到 ZooKeeper 服务器上然后由 ZooKeeper 服务器使用 WatcherManager 来管理所有的 Watcher。当某个 Znode 的数据发生变更时WatchManager 将触发相应的 Watcher并通过与订阅该 Znode 的 ZooKeeper 客户端的 socket 进行通信。随后客户端的 Watch 管理器将触发相关的 Watcher 回调以执行相应的处理逻辑从而完成整个数据发布/订阅流程。 进一步分析WatchManager成员变量 Watch2Path、WatchTables 内存占比高达 (18.889.47)/31.82 90%。 而 WatchTables、Watch2Path 存储的是 ZNode 与 Watcher 正反映射关系存储结构图所示 WatchTables【正向查询表】 HashMapZNode, HashSetWatcher 场景某个ZNode发生变化订阅该ZNode的Watcher会收到通知。 逻辑用该ZNode通过 WatchTables 找到对应的所有 Watcher 列表然后逐个发通知。 Watch2Paths【逆向查询表】 HashMapWatcher, HashSet 场景统计某个 Watcher 到底订阅了哪些ZNode 逻辑用该Watcher通过 Watch2Paths 找到对应的所有 ZNode 列表 Watcher 本质是 NIOServerCnxn可以理解成一个连接会话。 如果ZNode、和 Watcher 的数量都比较多并且客户端订阅 ZNode 也比较多甚至全量订阅。这两张Hash表记录的关系就会呈指数增长最终会是一个天量 当全订阅时如图演示 当 ZNode数量3Watcher 数量2   WatchTables 和 Watch2Paths 会各有 6 条关系 当 ZNode数量4Watcher 数量3   WatchTables 和 Watch2Paths 会各有 12 条关系 通过监控我们发现异常的ZK-Node。ZNode数量大概有20WWatcher数量是5000。而Watcher与ZNode的关系条数达到了1亿。 如果存储每条关系的需要1个 HashMapNode(32Byte)由于是两个关系表double一下。那么其它都不要计算光是这个壳就需要 2*10000^2*32/1024^3 5.9GB 的无效内存开销。 分析到这里大家应该明白了。为什么我们的ZK内存总是在走“钢丝”经常OOM。 意外发现 既然已经确定了问题的原因接下来我们应该考虑如何解决它。 通过上面的分析可以得知我们需要避免客户端出现对所有 ZNode 进行全面订阅的情况。然而实际情况是许多业务代码确实存在这样的逻辑从 ZTree 的根节点开始遍历所有 ZNode并对它们进行全面订阅。 我们或许能够说服一部分业务方进行改进但无法强制约束所有业务方的使用方式。因此我们解决这个问题的思路在于监控和预防。然而遗憾的是ZK 本身并不支持这样的功能这就需要对 ZK 源码进行修改。 通过对源码的跟踪和分析我们发现问题的根源又指向了 WatchManager并且我们仔细研究了这个类的逻辑细节。经过深入理解后我们发现这段代码的质量似乎像是由应届毕业生编写的存在大量线程和锁的不恰当使用问题。通过查看 Git 记录我们发现这个问题可以追溯到 2007 年。然而令人振奋的是在这一段时间内出现了 WatchManagerOptimized2018通过搜索 ZK 社区的资料我们发现了 [ZOOKEEPER-1177]即在 2011 年ZK 社区就已经意识到了大量 Watch 导致的内存占用问题并最终在 2018 年提供了解决方案。正是这个WatchManagerOptimized 的功劳看来ZK社区早就进行了优化。 有趣的是ZK默认情况下并未启用这个类即使在最新的 3.9.X 版本中默认仍然使用 WatchManager。也许是因为 ZK 年代久远渐渐地人们对其关注度降低了。通过询问阿里的同事我们确认了 MSE-ZK 也启用了 WatchManagerOptimized这进一步证实了我们关注的方向是正确的。因此我们认为有必要深入挖掘一下这个类的潜力。 优化探索 锁的优化 在默认版本中使用的 HashSet 是线程不安全的。在这个版本中相关操作方法如 addWatch、removeWatcher 和 triggerWatch 都是通过在方法上添加了 synchronized 重型锁来实现的。而在优化版中我们采用了 ConcurrentHashMap 和 ReadWriteLock 的组合以更精细化地使用锁机制。这样一来在添加 Watch 和触发 Watch 的过程中能够实现更高效的操作。 存储优化 这是我们关注的重点。从 WatchManager 的分析可以看出使用 WatchTables 和 Watch2Paths 存储效率并不高。如果 ZNode 的订阅关系较多将会额外消耗大量无效的内存。 令我们感到惊喜的是WatchManagerOptimized 在这里使用了“黑科技” - 位图。 利用位图将关系存储进行了大量的压缩实现了降维优化。 Java BitSet 主要特点 空间高效BitSet 使用位数组存储数据比标准的布尔数组需要更少的空间。 处理快速进行位操作如 AND、OR、XOR、翻转通常比相应的布尔逻辑操作更快。 动态扩展BitSet 的大小可以根据需要动态增长以容纳更多的位。 BitSet 使用一个 long[] words 来存储数据long 类型占 8 字节64位。数组中每个元素可以存储 64 个数据数组中数据的存储顺序从左到右从低位到高位。 比如下图中的 BitSet 的 words 容量为 4words[0]从低位到高位分别表示数据 0~63是否存在words[1] 的低位到高位分别表示数据 64~127 是否存在以此类推。其中 words[1] 8对应的二进制第 8 位为 1说明此时 BitSet 中存储了一个数据 {67}。 WatchManagerOptimized 使用 BitMap来存储所有的 Watcher。这样即便是存在1W的 Watcher。位图的内存消耗也只有8Byte*1W/64/10241.2KB。如果换成 HashSet 则至少需要 32Byte*10000/1024305KB存储效率相差近300倍。 WatchManager.java:private final MapString, SetWatcher watchTable new HashMap();private final MapWatcher, SetString watch2Paths new HashMap(); WatchManagerOptimized.java:private final ConcurrentHashMapString, BitHashSet pathWatches new ConcurrentHashMapString, BitHashSet();private final BitMapWatcher watcherBitIdMap new BitMapWatcher(); ZNode到 Watcher 的映射存储由 Mapstring, set 换成了 ConcurrentHashMapstring, BitHashSet。也就是说不再存储 Set而是用位图来存储位图索引值。 我们用 1W的ZNode1W的Watcher极端点走全订阅所有的Watcher订阅所有的ZNode做存储效率PK 可以看到 11.7MB PK 5.9GB内存的存储效率相差516倍。 逻辑优化 添加监视器两个版本都能够在常数时间内完成操作但是优化版通过使用ConcurrentHashMap提供了更好的并发性能。 删除监视器默认版可能需要遍历整个监视器集合来找到并删除监视器导致时间复杂度为O(n)。而优化版利用BitSet和ConcurrentHashMap在大多数情况下能够快速定位和删除监视器O(1)。 触发监视器默认版的复杂度较高因为它需要对每个路径上的每个监视器进行操作。优化版通过更高效的数据结构和减少锁的使用范围优化了触发监视器的性能。 三、性能压测 JMH 微基准测试 zookeeper 3.6.4 源码编译 JMH micor 压测 WatchBench。 pathCount表示测试中使用的ZNode路径数目。 watchManagerClass表示测试中使用的 WatchManager 实现类。 watcherCount表示测试中使用的观察者Watcher数目。 Mode表示测试的模式这里是 avgt表示平均运行时间。 Cnt表示测试运行的次数。 Score表示测试的得分即平均运行时间。 Error表示得分的误差范围。 Units表示得分的单位这里是毫秒/操作ms/op。 ZNode与Watcher 100 万条订阅关系默认版本使用 50MB优化版只需要 0.2MB而且不会线性增加。 添加Watch优化版0.406 ms/op比 默认版2.669 ms/op提升 6.5 倍。 大量触发Watch 优化版17.833 ms/op比 默认版84.455 ms/op提升 5 倍。 性能压测 接下来我们在一台机器(32C 60G) 搭建一套 3节点 zookeeper 3.6.4 使用优化版与默认版进行容量压测对比。 场景一20W znode 短路径 Znode 短路径: /demo/znode1  场景二20W znode 长路径 Znode 长路径: /sentinel-cluster/dev/xx-admin-interfaces/lock/_c_bb0832d5-67a5-48ab-8fe0-040b9ddea-lock/12 Watch 内存占用跟 ZNode 的 Path 长度有关。 Watch 的数量在默认版是线性上涨在优化版中表现非常好这对内存占用优化来说改善非常明显。 灰度测试 基于前面的基准测试和容量测试优化版在大量 Watch 场景内存优化明显接下来我们开始对测试环境的 ZK 集群进行灰度升级测试观察。 第一套 zookeeper 集群  收益 默认版 优化版 效果收益 election_time (选举耗时)降低 60% fsync_time (事务同步耗时)降低 75% 内存占用降低 91% 第二套 zookeeper 集群  收益 效果收益 内存变更前 JVM Attach 响应无法响应采集数据失败。 election_time选举耗时降低 64%。 max_latency读延迟降低 53%。 proposal_latency选举处理提案延迟1400000 ms -- 43 ms。 propagation_latency数据的传播延迟1400000 ms -- 43 ms。 第三套 zookeeper 集群 收益 默认版 优化版 效果收益 内存节省 89% election_time选举耗时降低42% max_latency读延迟降低 95% proposal_latency选举处理提案延迟679999 ms -- 0.3 ms propagation_latency数据的传播延迟928000  ms-- 5 ms 四、总结 通过之前的基准测试、性能压测以及灰度测试我们发现了 Zookeeper 的 WatchManagerOptimized。这项优化不仅节省了内存还通过锁的优化显著提高了节点之间的选举和数据同步等指标从而增强了 Zookeeper 的一致性。我们还与阿里MSE的同学进行了深度交流各自在极端场景模拟压测并达成了一致的看法WatchManagerOptimized 对 Zookeeper 的稳定性提升显著。总体而言这项优化使得 Zookeeper 的 SLA 提升了一个数量级。 ZooKeeper有许多配置选项但大部分情况下不需要调整。为提升系统稳定性我们建议进行以下配置优化 将 dataDir数据目录和 dataLogDir事务日志目录分别挂载到不同的磁盘上并使用高性能的块存储。 对于 ZooKeeper 3.8 版本建议使用 JDK 17 并启用 ZGC 垃圾回收器而对于 3.5 和 3.6 版本可使用 JDK 8 并启用 G1 垃圾回收器。针对这些版本只需要简单配置 -Xms 和 -Xmx 即可。 将 SnapshotCount 参数默认值 100,000 调整为 500,000这样可以在高频率 ZNode 变动时显著降低磁盘压力。 使用优化版的 Watch 管理器 WatchManagerOptimized。 Ref https://issues.apache.org/jira/browse/ZOOKEEPER-1177 https://github.com/apache/zookeeper/pull/590 *文/Bruce 本文属得物技术原创更多精彩文章请看得物技术官网 未经得物技术许可严禁转载否则依法追究法律责任
http://www.zqtcl.cn/news/560529/

相关文章:

  • 北京的制作网站的公司在哪里软件程序员
  • 企业网站怎么扣费的网站建设合同的性质
  • 聚美优品一个专注于做特价的网站如何制作个人网页兼职
  • 滨州做网站的公司最好wordpress主题
  • 福州网站设计软件公司dw网站开发流程
  • 合肥网站搭建公司哪家好深圳二维码网站建设
  • 东莞微信网站开发免费html模板素材网站
  • 海淀专业企业网站建设青岛平面设计公司
  • 北京正规网站建设比较wordpress cookies因预料之外的输出被阻止
  • 自助微信网站设计什么叫一级域名二级域名
  • 上海 顶尖 网站设计wordpress多站点不同主题
  • asp c 网站开发wordpress 动静分离
  • 服装网站建设规定wordpress禁止自动升级
  • 如何在网站上做社交的链接毕设给学校做网站
  • 网页设计与网站建设指标点您身边的网站建设顾问
  • 个人网站的制作广州网站优化招聘
  • 做网站产生的流量费怎么算软件开发前景和收入
  • 网站空间 .de单页型网站
  • 网站建设com品牌建设的作用
  • 优质作文网站柳州做网站去哪家公司好
  • 呼和浩特网站建设价格网站建设服务器
  • 做的比较好的电商网站西安有那些做网站的公司好
  • 哪个网站可以做英语语法题智慧云建筑信息平台
  • 网站怎么做百度才会收录金乡县网站开发
  • 深圳移动网站建站网站如何做播放线路
  • 深圳网站建设q.479185700惠哪个网站可以免费设计房子
  • 迁西网站开发网站建设技术网站建
  • 网站建设与管理课程报告能够做外贸的网站有哪些
  • 浅析社区网站的建设如何建立企业网站
  • 网站建设尺寸像素是多少广州商城型网站建设