本篇文章仅仅是起一个抛砖迎玉的作用,举一个如何修改源码的例子。文章的灵感来自 ZOOKEEPER-2784

提一个问题先

之前的文章讲过zxid的设计,我们先复习下:

zxid有64位,分成两部分:
高32位是Leader的epoch:选举时钟,每次选出新的Leader,epoch累加1
低32位是在这轮epoch内的事务id:对于用户的每一次更新操作集群都会累加1。

这么设计会存在什么问题?

Zookeeper 的事务 ID 有可能会超过 32 位。

epoch增长非常慢,超过32位需要非常久的时间,几乎可以忽略这个问题,但是事务 ID 似乎不行。我们来算下。

如果我们每秒钟操作1000次 Zookeeper ,即 1k/s ops,那么

2^32/(86400∗1000) ≈ 49.7

49.7天后,事务 ID 就将溢出,那溢出会发生什么,看代码:

src/java/main/org/apache/zookeeper/server/quorum/Leader.java line1037

    /**
     * create a proposal and send it out to all the members
     *
     * @param request
     * @return the proposal that is queued to send to all the members
     */
    public Proposal propose(Request request) throws XidRolloverException {
        /**
         * Address the rollover issue. All lower 32bits set indicate a new leader
         * election. Force a re-election instead. See ZOOKEEPER-1277
         */
        if ((request.zxid & 0xffffffffffL) == 0xffffffffffL) {
            String msg =
                    "zxid lower 32 bits have rolled over, forcing re-election, and therefore new epoch start";
            shutdown(msg);
            throw new XidRolloverException(msg);
        }
        

从上面的代码可以看到,

Zookeeper 的 Leader 节点会throw new XidRolloverException(msg) 强制进行 re-election重新选举,

即服务会停止一段时间,在一些场景下,这种情况过于频繁是不能容忍的,那我们来看看如何解决。

如何解决?

上面说了epoch增长速度慢到可以忽略它溢出的问题,那么可以重新设计 ZXID,

设计成高 24 位用于 epoch,低 40 位用于 事务 ID 增长。

我们再来算一下:

2^40/(86400∗1000) ≈ 12725.8  即 12725.8/365 ≈ 34.9 年

1k/s ops 的情况下, 34.9 年之后才会进行一次强制选举。

设想不错,可以解决我们的问题,那我们继续。

还有一个担心

从操作系统的底层来说,对于32位操作系统,单次操作能处理的最长长度为32bit,而long类型8字节64bit,所以对long的读写都要两条指令才能完成(即每次读写64bit中的32bit)。

为什么说这个,因为也许有人会把这个和 ZXID 的设计联想起来,上面的 ZOOKEEPER-2784里面也提到了这个问题。

However, i thought the ZXID is long type, reading and writing the long type (and double type the same) in JVM, is divided into high 32bit and low 32bit part of the operation, and because the ZXID variable is not modified with volatile and is not boxed for the corresponding reference type (Long / Double), so it belongs to [non-atomic operation]

我大概翻译一下:

ZXID 是 long 类型,32 bit 的 JVM 在对 long 读写时(和 double 类型一样),是分为高 32 位和 低 32 位两部分进行操作的,由于 ZXID 变量没有用 volatile 修饰,且也没有装箱为对应的引用类型(Long / Double),属于非原子操作。

这位老哥担心对 ZXID 重新设计时把高 32 位和 低 32 位改成高 24 位和 低 40 位,可能会存在并发的问题。

会不会有这个问题,我们先来看看源码:

 Iterator<Integer> iterator = servers.iterator();
                  long zxid = Long.valueOf(m.group(2));
                  int count = (int)zxid;// & 0xFFFFFFFFL;
                  int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;

注意这个& 0xFFFFFFFFL,实际上后面的代码还有很多这种按位与的操作,就不贴出来了。

翻了这一块的源码就可以知道,这个担心是多余的,关于ZXID的所有操作都是位操作而不是“=”的赋值操作,它不会造成JVM级别的并发问题。

如何修改

接下来我们就用源码中“位与”的方式,把 32 为改成 40 位。

即:zxid按位于(&)0xffffffffffL(40位)获得zxid的后40位。

注意要把count之前的int类型改为long类型,因为int为32bit,long为64bit,此时count有40位所以换成long。

 Iterator<Integer> iterator = servers.iterator();
            long zxid = Long.valueOf(m.group(2));
         // int count = (int)zxid;// & 0xFFFFFFFFL;
         // int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;
            long count = zxid & 0xffffffffffL;
            int epoch = (int)Long.rotateRight(zxid, 40);// >> 40;

后面还有多处类似的地方要修改,就不一一列出来了,有兴趣的可以看这里github

zookeeper篇到这里就完结了,关于zookeeper大家还有什么想知道的可以留言,我觉得有价值的话会再更新些新的文章。

推荐阅读

大数据需要学什么?
大数据不就是写SQL吗?

zookeeper-如何修改源码-《每日五分钟搞定大数据》的更多相关文章

  1. zookeeper核心-zab协议-《每日五分钟搞定大数据》

    上篇文章<paxos与一致性>说到zab是在paxos的基础上做了重要的改造,解决了一系列的问题,这一篇我们就来说下这个zab. zab协议的全称是ZooKeeper Atomic Bro ...

  2. zookeeper-架构设计与角色分工-《每日五分钟搞定大数据》

    本篇文章阅读时间5分钟左右 点击看<每日五分钟搞定大数据>完整思维导图   zookeeper作为一个分布式协调系统,很多组件都会依赖它,那么此时它的可用性就非常重要了,那么保证可用性的同 ...

  3. HDFS-异常大全-《每日五分钟搞定大数据》

    点击看<每日五分钟搞定大数据>完整思维导图以及所有文章目录 问题1:Decomminssioning退役datanode(即删除节点) 1.配置exclude: <name>d ...

  4. zookeeper-操作与应用场景-《每日五分钟搞定大数据》

    Zookeeper作为一个分布式协调系统提供了一项基本服务:分布式锁服务,分布式锁是分布式协调技术实现的核心内容.像配置管理.任务分发.组服务.分布式消息队列.分布式通知/协调等,这些应用实际上都是基 ...

  5. zookeeper-监控与优化-《每日五分钟搞定大数据》

    本文的命令和配置都是基于zookeeper-3.4.6版本.优化很多时候都是基于监控的,所以把这两个内容写在了一起,慢慢消化. 监控 简单地说,监控无非就是获取服务的一些指标,再根据实际业务情况给这些 ...

  6. zookeeper-分布式锁的代码实现-【每日五分钟搞定大数据】

    本文涉及到几个zookeeper简单的知识点,永久节点.有序节点.watch机制.比较基础,熟悉的就别看了跳过这篇吧 每个线程在/locks节点下创建一个临时有序节点test_lock_0000000 ...

  7. redis- info调优入门-《每日五分钟搞定大数据》

    本文根据redis的info命令查看redis的内存使用情况以及state状态,来观察redis的运行情况以及需要作出的相应优化. info 1.memory used_memory:13409011 ...

  8. 五分钟搞定Go.js

    五分钟搞定Go.js  1.基于html5~因为Go.js是一个依赖于HTML5特性的JavaScript库,所以需要确保您的页面声明它是一个HTML5文档,当然需要加载库 <!DOCTYPE ...

  9. 五分钟搞定Linux容器

    [TechTarget中国原创] Linux容器针对特定工作负载提供了全新的灵活性与可能性.存在很多解决方案,但是没有一个解决方案能够像systemd容器那样进行快速部署.给我五分钟,本文将介绍如何使 ...

随机推荐

  1. Android Studio--gradle:download 过慢甚至超时timeout报错

    问题描述 今天第一次学习安卓,配置环境花了不少时间其他都比较容易的解决了 gradle 因为会从外国的网站下载东西会非常的慢(具体原理我也不清楚),所以最好让下载地址变成国内的 解决方案(最后有升级方 ...

  2. 复杂的字符串数组解析:{"setting":"简单:10:5,一般:5:10,困难:2:20"},使用split多次截取

    "[0,{"id":563,"name":"测试题1","dscr":null,"picId&quo ...

  3. java后台打开浏览器代码

    import java.awt.Desktop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxE ...

  4. oracle测试环境表空间清理

    测试场景下,使用的oralce遇到表空间的占用超大,可以采用如下的方式进行空间的清理 首先使用sqlplus连接数据库sqlplus sys/password@orcl as sysdba 之类进行数 ...

  5. JHipster生成微服务架构的应用栈(五)- 容器编排示例

    本系列文章演示如何用JHipster生成一个微服务架构风格的应用栈. 环境需求:安装好JHipster开发环境的CentOS 7.4(参考这里) 应用栈名称:appstack 认证微服务: uaa 业 ...

  6. LeetCode题解之Sum Root to Leaf Numbers

    1.题目描述 2.问题分析 记录所有路径上的值,然后转换为int求和. 3.代码 vector<string> s; int sumNumbers(TreeNode* root) { tr ...

  7. SQL SERVER 查询表的行数

    SELECT OBJECT_NAME(ii.id) TableName ,rows FROM sysindexes ii INNER JOIN sysobjects oo ON ( oo.id = i ...

  8. Q2Day81

    性能相关 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢. import requests def fetch_async(url): ...

  9. python第一百零九天---Django 4

    session :1. Session 基于Cookie做用户验证时:敏感信息不适合放在cookie中 a. Session原理 Cookie是保存在用户浏览器端的键值对 Session是保存在服务器 ...

  10. stored information about method csdn

    Eclipse编译时保留方法的形参 Window -> Preferences -> Java -> Compiler. 选中Store information about meth ...