通过Zookeeper学习在分布式系统中生成全局唯一ID

Session是Zookeeper中的会话实体,代表了一个客户端会话。SessionID用来唯一标识一个会话,因此Zookeeper必须保证sessionID的全局唯一性,在每次客户端向服务端发起"会话创建"请求时,服务端都会为其分配一个sessionID。那么Zookeeper是如何实现的呢?

在SessionTracker初始化的时候,会调用initializeNextSession方法来生成一个初始化的sessionID,之后在Zookeeper的正常运行过程中,会在该sessionID的基础上为每个会话进行分配。

Java代码  

  1. /**
  2. * Generates an initial sessionId. High order byte is serverId, next 5
  3. * 5 bytes are from timestamp, and low order 2 bytes are 0s.
  4. */
  5. public static long initializeNextSession(long id) {
  6. long nextSid = 0;
  7. nextSid = (System.currentTimeMillis() << 24) >>> 8; //这里的版本是3.4.6
  8. nextSid =  nextSid | (id <<56);
  9. return nextSid;
  10. }

上面这个方法就是Zookeeper初始化的算法。可以看出sessionID的生成可以分为以下5个步骤:

  1. 获取当前时间的毫秒表示。我们假设System.currentTimeMillis()取出的值是1380895182327,其64位二进制表示是:0000000000000000000000010100000110000011110001000100110111110111 其中红色部分表示高24位,下划线部分表示低40位。
  2. 左移24位。将步骤1中的数值左移24位,得到如下二进制表示的数值:0100000110000011110001000100110111110111000000000000000000000000
  3. 无符号右移8位。再将步骤2中的数值无符号右移8位,得到如下二进制表示的数值:0000000001000001100000111100010001001101111101110000000000000000
  4. 添加机器标识SID.在initializeNextSession方法中,出现了一个id变量,该变量就是当前Zookeeper服务器的SID值.SID就是当时配置在myid文件中的值,该值通常是一个整数。我们以2为例子。2的64位表示如下:0000000000000000000000000000000000000000000000000000000000000010。高56位都是0,将其左移56位,可以得到如下二进制表示的数值:0000001000000000000000000000000000000000000000000000000000000000
  5. 将步骤3和步骤4得到的64位数值进行“|”操作:0000001001000001100000111100010001001101111101110000000000000000。这样就完成了一个sessioID的初始化。我们就可以得到一个单机唯一的序列号。算法概括为:高8位确定了所在机器,后56位使用当前时间的毫秒表示进行随机。

上面的算法就算完美吗?

答案是否定的,在zookeeper3.5.0以上的版本中对这个方法进行的修改。3.5.0版本的方法如下:

Java代码  

  1. /**
  2. * Generates an initial sessionId. High order byte is serverId, next 5
  3. * 5 bytes are from timestamp, and low order 2 bytes are 0s.
  4. */
  5. public static long initializeNextSession(long id) {
  6. long nextSid;
  7. nextSid = (<span style="color: #ff0000;">Time.currentElapsedTime()</span> << 24) >>> 8;
  8. nextSid =  nextSid | (id <<56);
  9. return nextSid;
  10. }

currentTimeMillis方法换成了另外一个方法。那么这个方法是什么呢?

Java代码  

  1. /**
  2. * Returns time in milliseconds as does System.currentTimeMillis(),
  3. * but uses elapsed time from an arbitrary epoch more like System.nanoTime().
  4. * The difference is that if somebody changes the system clock,
  5. * Time.currentElapsedTime will change but nanoTime won‘t. On the other hand,
  6. * all of ZK assumes that time is measured in milliseconds.
  7. * @return  The time in milliseconds from some arbitrary point in time.
  8. */
  9. public static long currentElapsedTime() {
  10. return System.nanoTime() / 1000000;
  11. }

使用了System.nanoTime()方法。原因是如果一些人去主动修改系统的时间,这样可能会出现问题。

Zookeeper使用了2行代码完成了生成全局唯一值的方法。然而我们直接使用jdk1.5以上自带的UUID不是也能生成全局唯一的值吗?

下面我们看看UUID和Zookeeper自己实现方法的一些比较:

  • 根据UUID类的注释,A UUID represents a 128-bit value.一个UUID代表一个128位的值。然而Zookeeper使用64就产生了一个全局唯一的值。从资源占用上节省了一半。
  • 从代码复杂程度上看,UUID的生成是严格按照国际标准实现的。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成。代码的复杂程度要比Zookeeper自身实现的高。复杂度高意味着在执行过程中需要消耗的资源就会增多。
  • UUID的生成不需要外界因素的参与。直观的将就是不需要传递任何参数就可以完成。而Zookeeper自身实现的方法需要一个id,这个id就是我们在Zookeeper中配置的myid值。它的生成时需要条件的。

总结:

  • 没有最好只有适合。适合自身的需要并且能够考虑到性能、资源的使用等方面才能设计出好的策略。
  • 底层的运算使用位运算比较理想。很多地方都是这样运用的。毕竟位运算的速度是最快的。

源码来源:minglisoft.cn/technology

时间: 2024-10-12 23:24:03

通过Zookeeper学习在分布式系统中生成全局唯一ID的相关文章

高并发分布式系统中生成全局唯一Id汇总

高并发分布式系统中生成全局唯一Id汇总 (转自:http://www.cnblogs.com/baiwa/p/5318432.html) 数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求:   1 不能有单点故障.   2 以时间为序,或者ID里包含时间.这样一是可以少一个索引,二是冷热数据容易分离.   3 可以控制ShardingId.比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易.   

如何在高并发分布式系统中生成全局唯一Id(转)

http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了个MSSQL参数化语法生成器,会在9月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也

如何在高并发分布式系统中生成全局唯一Id

我了解的方案如下-------------------------- 1.  使用数据库自增Id 优势:编码简单,无需考虑记录唯一标识的问题. 缺陷: 1)         在大表做水平分表时,就不能使用自增Id,因为Insert的记录插入到哪个分表依分表规则判定决定,若是自增Id,各个分表中Id就会重复,在做查询.删除时就会有异常. 2)         在对表进行高并发单记录插入时需要加入事物机制,否则会出现Id重复的问题. 3)         在业务上操作父.子表(即关联表)插入时,需要

游戏服务器生成全局唯一ID的几种方法

在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使用全局的唯一id,在游戏服务器中,全局唯一的id可以用于将来合服方便,不会出现键冲突.也可以将来在业务增长的情况下,实现分库分表,比如某一个用户的物品要放在同一个分片内,而这个分片段可能是根据用户id的范围值来确定的,比如用户id大于1000小于100000的用户在一个分片内.目前常用的有以下几种:

SnowFlake 生成全局唯一id

public class SnowFlakeUtil { private long workerId; private long datacenterId; private long sequence = 0L; private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT 标记时间 用来计算偏移量,距离当前时间不同,得到的数据的位数也不同 private long workerIdBits = 5L; // 物理

python 中md5 和 sha1 加密, md5 + os.urandom 生成全局唯一ID

首先先来介绍一下md5 和 sha1 的概念 MD5 MD5的全称是Message-Digest Algorithm 5(信息-摘要算法).128位长度.目前MD5是一种不可逆算法. 具有很高的安全性.它对应任何字符串都可以加密成一段唯一的固定长度的代码. SHA1 SHA1的全称是Secure Hash Algorithm(安全哈希算法) .SHA1基于MD5,加密后的数据长度更长, 它对长度小于264的输入,产生长度为160bit的散列值.比MD5多32位. 因此,比MD5更加安全,但SHA

Java生成全局唯一ID代码演示

看了GitHub上的两个生成唯一ID的算法程序(一个出自百度,一个出自美团),打算运行着试试看,至于原理什么的文档上讲得很详细了,此处不再一一粘贴了,此处只演示代码 https://github.com/baidu/uid-generator https://github.com/zhuzhong/idleaf 百度UID生成器 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns=&q

Java中SnowFlake 雪花算法生成全局唯一id中的问题,时间不连续全为偶数解决

package com.example.springbootshardingjdbc.util; import java.io.FileOutputStream; /** * 描述: Twitter的分布式自增ID雪花算法snowflake (Java版) * * @author * @create 2018-03-13 12:37 **/ public class SnowFlake { /** * 起始的时间戳 */ private final static long START_STMP

生成全局唯一id

http://littcai.iteye.com/blog/1900018 http://www.oschina.net/question/575914_90957 http://www.dengchuanhua.com/132.html twitter/snowflake https://github.com/twitter/snowflake https://twitter.github.io/twitter-server/