再谈分布式ID生成方案

前言

昨天沉思君分享了一篇关于分布式ID生成方案的文章《分布式ID常见解决方案》,文中介绍了几种常见的分布式ID生成方案,并讨论了其优缺点。刚好最近沉思君在看李艳鹏老师合著的书《可伸缩服务架构:框架与中间件》,里面也有谈到分布式ID的设计思路,该项目已在GitHub上开源,项目名称为Vesta,今天就简单给大家介绍下Vesta的设计思路。

数据结构

首先介绍下Vesta中ID的数据结构。Vesta的ID类型是Java的long型,也就是二进制的64位。其中根据不同的业务场景,Vesta提供了2种ID类型给使用者,分别是最大峰值型和最小粒度型。每种类型的不同比特位代表的含义略有不同。

  • 最大峰值型:

字段

版本

类型

生成方式

秒级时间

序列号

机器ID

位数

63

62

60-61

30-59

10-29

0-9

因为使用的是秒级时间,所以时间段的位数比较少,而序列号位数相对比较多,着意味着1秒内可以承受的峰值比较高。

  • 最小粒度型:

字段

版本

类型

生成方式

毫秒级时间

序列号

机器ID

位数

63

62

60-61

20-59

10-19

0-9

由于时间精确到毫秒级,故粒度比较小,单位毫秒内生成的序列号比较少,适合用于峰值不高的场景。

其中,版本的默认值是0,其作用是用于扩展位或者扩容时的临时方案,比如当ID快用完的时候,只要将版本号修改为1,又可以使用很多年了。

生成类型表示的是ID生成器的发布模式,Vesta目前支持3种发布模式,分别是:嵌入发布模式,中心服务器发布模式和REST发布模式,一共3种,所以用2个二进制位就可以表示了。嵌入发布模式指的是直接调用本地Jar的方式生成分布式ID,这种方式需要提前配置好本地机器ID,优点是不需要依赖中心服务器。中心服务器发布模式指的是通过远程过程调用的方式调用中心服务器来生成分布式ID,缺点是需要依赖于中心服务器。REST发布模式指的是通过REST API调用中心服务器来生成分布式ID,优点是支持多语言,而前2种方式目前只支持Java客户端。

机器ID指的是生成分布式ID的机器对应的ID,Vesta支持3种机器ID分配方式,分别是属性配置方式、IP映射方式和数据库配置方式。下面将对这几种机器ID配置方式进行说明。

机器ID配置方式

  • 属性配置方式

属性配置方式,顾名思义就是在配置文件或机器的环境变量中配置好机器ID。这种方式适用于机器数量少的情况,如果机器数量多,配置起来是很繁琐的。

  • IP映射方式

IP映射方式,则是将所有需要生成ID的机器IP分别映射成唯一的机器ID,并配置在配置文件里,当服务启动的时候,将IP跟机器ID的映射初始化到Map中,再根据所在机器的IP就可以得到该机器对应的ID了。这种方式配置起来比较简单。

  • 数据库配置方式

数据库配置方式则是将机器IP跟机器ID的映射配置在数据库里,这种方式配置起来也比较简单,缺点是需要依赖外部数据库。

序列号生成方式

接下来讲下Vesta中的序列号生成方式。由于在同一台机器上可能存在多线程并发申请生成ID,所以生成单位时间内的序列号时需要考虑同步。Vesta中有3种生成序列号的方式,分别是基于Synchronized锁方式、基于ReentranLock方式和基于CAS无锁技术方式。

其中,基于Synchronized锁方式和基于基于ReentranLock方式比较简单,就是在递增序列号时加锁来进行同步,在此不赘述。

基于CAS无锁技术的方式使用了原子变量引用实现,因为生成ID时,需要同时更新时间戳和序号,为了实现原子更新,故使用了原子变量引用AtomicReference。该类提供了compareAndSet方法来实现CAS操作。如果CAS操作失败,则进入下一轮循坏,继续尝试更新,直到更新成功为止。

核心代码如下:

public class AtomicIdPopulator implements IdPopulator, ResetPopulator {

    class Variant {

        private long sequence = 0;
        private long lastTimestamp = -1;

    }

    private AtomicReference<Variant> variant = new AtomicReference<Variant>(new Variant());

    public AtomicIdPopulator() {
        super();
    }

    public void populateId(Id id, IdMeta idMeta) {
        Variant varOld, varNew;
        long timestamp, sequence;

        while (true) {

            // Save the old variant
            varOld = variant.get();

            // populate the current variant
            timestamp = TimeUtils.genTime(IdType.parse(id.getType()));
            TimeUtils.validateTimestamp(varOld.lastTimestamp, timestamp);

            sequence = varOld.sequence;

            if (timestamp == varOld.lastTimestamp) {
                sequence++;
                sequence &= idMeta.getSeqBitsMask();
                if (sequence == 0) {
                    timestamp = TimeUtils.tillNextTimeUnit(varOld.lastTimestamp, IdType.parse(id.getType()));
                }
            } else {
                sequence = 0;
            }

            // Assign the current variant by the atomic tools
            varNew = new Variant();
            varNew.sequence = sequence;
            varNew.lastTimestamp = timestamp;

            if (variant.compareAndSet(varOld, varNew)) {
                id.setSeq(sequence);
                id.setTime(timestamp);

                break;
            }

        }
    }

    public void reset() {
        variant = new AtomicReference<Variant>(new Variant());
    }

更多细节可以下载项目源码进行了解。项目地址:

https://github.com/cloudatee/vesta-id-generator

原文发布于微信公众号 - Java架构沉思录(code-thinker)

原文发表时间:2018-05-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏PHP在线

在Mac下使用MAMP Pro环境

以前,我使用Windows作为自己的工作系统,后来,改用Mac作为自己的主要工作系统了。 在Windows下,快速搭建*AMP环境,使用xampp或者WAMP之...

84170
来自专栏JadePeng的技术博客

ELK日志套件安装与使用

1、ELK介绍 ELK不是一款软件,而是elasticsearch+Logstash+kibana三款开源软件组合而成的日志收集处理套件,堪称神器。其中Logs...

32240
来自专栏Java开发者杂谈

关于安装多个版本jdk之后java -version不正确的问题

问题描述: 今天突然想写一个socket通信的小应用,分别采用BIO、NIO、AIO的方式来实现,来复习前面看的关于TCP/UDP通信的知识。于是乎在原来安装了...

33070
来自专栏云加头条

腾讯云负载均衡的使用

负载均衡:暴露给用户的IP只有一个,后端机器故障上线先对用户无感知,后端机器可以根据性能差异调整权重,分配访问量。从创建负载均衡。负载均衡可在云中的多个CVM实...

2.7K20
来自专栏软件开发 -- 分享 互助 成长

代理模式

一、相关介绍 1、代理模式为其他对象提供一种代理以控制对这个对象的访问 2、UML图 ? 3、所属类别:结构型 3、C++程序 1 // 代理模式.cpp :...

22290
来自专栏小白课代表

听说百度限制账号下载速度,用下载器都不好使?

随着各种第三方下载器的流行,百度网盘开始针对下载速度过快的账户进行强制限速,最简单的解决办法当然还是等,只要多等几天,限制自然会消失。

3.2K30
来自专栏PHP技术大全

WEB安全Permeate漏洞靶场挖掘实践

最近在逛码云时候发现permeat靶场系统,感觉界面和业务场景设计的还不错.所以过来分享一下.

60730
来自专栏Java编程技术

Spring之WebFlux

在spring5.0前,web技术栈都是阻塞式的,虽然servlet3.0引入了非阻塞API的使用,但是这种异步是不彻底的,因为比如filter、servlet...

31910
来自专栏Java后端技术栈

从输入URL到页面加载发生了什么

问题:在浏览器中输入URL到整个页面显示在用户面前时这个过程中到底发生了什么。仔细思考这个问题,发现确实很深,这个过程涉及到的东西很多。

23330
来自专栏hrscy

socket的概念与网络通信要素

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端成为一个 socket。

13640

扫码关注云+社区

领取腾讯云代金券