• 全局唯一ID生成器(Snowflake ID组成)


    Snowflake ID组成

    Snowflake ID有64bits长,由以下三部分组成:

    • time—42bits,精确到ms,那就意味着其可以表示长达(2^42-1)/(1000360024*365)=139.5年,另外使用者可以自己定义一个开始纪元(epoch),然后用(当前时间-开始纪元)算出time,这表示在time这个部分在140年的时间里是不会重复的,官方文档在这里写成了41bits,应该是写错了。另外,这里用time还有一个很重要的原因,就是可以直接更具time进行排序,对于twitter这种更新频繁的应用,时间排序就显得尤为重要了。

    • machine id—10bits,该部分其实由datacenterId和workerId两部分组成,这两部分是在配置文件中指明的。

      • datacenterId的作用(个人看法)

        1.方便搭建多个生成uid的service,并保证uid不重复,比如在datacenter0将机器0,1,2组成了一个生成uid的service,而datacenter1此时也需要一个生成uid的service,从本中心获取uid显然是最快最方便的,那么它可以在自己中心搭建,只要保证datacenterId唯一。如果没有datacenterId,即用10bits,那么在搭建一个新的service前必须知道目前已经在用的id,否则不能保证生成的id唯一,比如搭建的两个uid service中都有machine id为100的机器,如果其server时间相同,那么产生相同id的情况不可避免。

        2.加快server启动速度。启动一台uid server时,会去检查zk同workerId目录中其他机器的情况,如其在zk上注册的id和向它请求返回的work_id是否相同,是否处同一个datacenter下,另外还会检查该server的时间与目前已有机器的平均时间误差是否在10s范围内等,这些检查是会耗费一定时间的。将一个datacenter下的机器数限制在32台(5bits)以内,在一定程度上也保证了server的启动速度。

      • workerId是实际server机器的代号,最大到32,同一个datacenter下的workerId是不能重复的。它会被注册到zookeeper上,确保workerId未被其他机器占用,并将host:port值存入,注册成功后就可以对外提供服务了。

    • sequence id —12bits,该id可以表示4096个数字,它是在time相同的情况下,递增该值直到为0,即一个循环结束,此时便只能等到下一个ms到来,一般情况下4096/ms的请求是不太可能出现的,所以足够使用了。

    Snowflake ID便是通过这三部分实现了UID的产生,策略也并不复杂。下面我们来看看它的一些关键源码

    /**
    * 核心代码就是毫秒级时间41位+机器ID 10位+毫秒内序列12位
    * */

    public class IdWorker {


    private long workerId;
    private long datacenterId;
    private long sequence = 0L;

    private long twepoch = 1288834974657L;

    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;

    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public IdWorker(long workerId, long datacenterId) {
    // sanity check for workerId
    if (workerId > maxWorkerId || workerId < 0) {
    throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
    }
    if (datacenterId > maxDatacenterId || datacenterId < 0) {
    throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
    }
    this.workerId = workerId;
    this.datacenterId = datacenterId;
    }

    public synchronized long nextId() {
    long timestamp = timeGen();

    if (timestamp < lastTimestamp) {
    System.out.println("clock is moving backwards. Rejecting requests until "+lastTimestamp);
    throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
    }

    if (lastTimestamp == timestamp) {
    sequence = (sequence + 1) & sequenceMask;
    if (sequence == 0) {
    timestamp = tilNextMillis(lastTimestamp);
    }
    } else {
    sequence = 0L;
    }

    lastTimestamp = timestamp;

    return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
    }

    protected long tilNextMillis(long lastTimestamp) {
    long timestamp = timeGen();
    while (timestamp <= lastTimestamp) {
    timestamp = timeGen();
    }
    return timestamp;
    }

    protected long timeGen() {
    return System.currentTimeMillis();
    }
    }

  • 相关阅读:
    css3:让IE兼容background-size的方法
    判断浏览器版本是否是ie9以下浏览器,提示升级
    正则表达式
    H5页面内容较少时如何让页面全屏在手机显示呢
    页面滚动指定高度时添加样式或动画
    使用js实现导航切换效果变化(收集案例)
    单行文字定时滚动(收集案例)
    使用zepto.js完成的手机相册
    Repaint 、Reflow 的基本认识和优化
    Web-[强网杯 2019]随便注
  • 原文地址:https://www.cnblogs.com/dayhand/p/3851248.html
Copyright © 2020-2023  润新知