V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hansonwang99
V2EX  ›  程序员

再来安利一款优秀的开源全局唯一流水号 ID 生成器: Vesta

  •  
  •   hansonwang99 ·
    hansonwang99 · 2018-11-22 06:42:48 +08:00 · 6387 次点击
    这是一个创建于 2180 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文内容脑图如下:

    本文内容脑图

    文章共 760 字,阅读大约需要 2 分钟 !


    概 述

    在前一篇文章 《 Spring Boot 工程集成全局唯一 ID 生成器 UidGenerator 》 中给大家推荐了一款由百度开发的基于 Snowflake 算法实现的全局唯一 ID 生成器 UidGenerator,而本文则给大家再度推荐一款优秀的全局唯一 ID 生成器,名叫 Vesta。

    Vesta 是艳鹏大佬的开源作品,基于 Java 开发,其体验地址 在此。Vesta 是一款通用的 ID 产生器,互联网俗称统一发号器,其具有几大很具有优势的特性:

    • 全局唯一
    • 粗略有序
    • 可反解
    • 可制造
    • 分布式

    而且支持三种发布模式:

    • 嵌入式发布模式
    • 中心服务器发布模式
    • REST 发布模式

    根据业务的性能需求,它可以产生 最大峰值型最小粒度型 两种类型的 ID,它的实现架构使其具有高性能,高可用和可伸缩等互联网产品需要的质量属性,是一款通用的高性能的发号器产品。

    本文就在 Spring Boot 项目中将 Vesta 耍起来!


    基础工程搭建

    Spring Boot 基础工程的搭建我不再赘述,创建好工程后 pom中需要加入如下依赖:

            <dependency>
                <groupId>com.robert.vesta</groupId>
                <artifactId>vesta-service</artifactId>
                <version>0.0.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.robert.vesta</groupId>
                <artifactId>vesta-intf</artifactId>
                <version>0.0.1</version>
            </dependency>
    

    对应的 Jar 包去编译一下 Vesta 源码即可获得,源码在此


    Vesta 配置导入

    • 在项目resources目录中加入 Vesta 的配置文件

    引入vesta-rest.properties,配置如下:

    vesta.machine=1021  # 机器 ID
    vesta.genMethod=0   # 生成方式,0 表示使用嵌入发布模式
    vesta.type=1        # ID 类型,1 表示最小粒度型
    

    引入 vesta-rest-main.xml,配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
      <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations" value="classpath:ext/vesta/vesta-rest.properties"/>
      </bean>
    
      <bean id="idService" class="com.robert.vesta.service.factory.IdServiceFactoryBean"
        init-method="init">
        <property name="providerType" value="PROPERTY"/>
        <property name="type" value="${vesta.type}"/>
        <property name="genMethod" value="${vesta.genMethod}"/>
        <property name="machineId" value="${vesta.machine}"/>
      </bean>
    
    </beans>
    

    好,接下来我们创建一个 Config 配置类来将 vesta-rest-main.xml配置文件加载进项目

    • 创建 UidConfig 配置类
    @Configuration
    @ImportResource( locations = { "classpath:ext/vesta/vesta-rest-main.xml" } )
    public class UidConfig {
    }
    

    编写 Vesta Service

    这里面包含的是和 ID 生成器相关的几个重要工具接口,主要有:

    • genId 生成全局唯一 ID 号
    • explainId 反解全局唯一 ID 号,得到可以解释 ID 号含义的 JSON 数据
    • makeId 手工制造 ID

    来看代码吧

    @Service
    public class UidService {
    
        @Resource
        private IdService idService;
    
        public long genId() {
            return idService.genId();
        }
    
        public Id explainId( long id ) {
            return idService.expId(id);
        }
    
        public long makeId( long version, long type, long genMethod, long machine, long time, long seq ) {
    
            long madeId = -1;
            if (time == -1 || seq == -1)
                throw new IllegalArgumentException( "Both time and seq are required." );
            else if (version == -1) {
                if (type == -1) {
                    if (genMethod == -1) {
                        if (machine == -1) {
                            madeId = idService.makeId(time, seq);
                        } else {
                            madeId = idService.makeId(machine, time, seq);
                        }
                    } else {
                        madeId = idService.makeId(genMethod, machine, time, seq);
                    }
                } else {
                    madeId = idService.makeId(type, genMethod, machine, time, seq);
                }
            } else {
                madeId = idService.makeId(version, type, genMethod, time,
                        seq, machine);
            }
    
            return madeId;
        }
    
    }
    

    编写测试 Controller

    我们针对上述 UidService中提供的三个工具接口来各自编写一个测试接口:

    @RestController
    public class UidController {
    
        @Autowired
        private UidService uidService;
    
        @RequestMapping("/genid")
        public long genId() {
            return uidService.genId();
        }
    
        @RequestMapping("/expid")
        public Id explainId(@RequestParam(value = "id", defaultValue = "0") long id) {
            return uidService.explainId( id );
        }
    
        @RequestMapping("/makeid")
        public long makeId(
                @RequestParam(value = "version", defaultValue = "-1") long version,
                @RequestParam(value = "type", defaultValue = "-1") long type,
                @RequestParam(value = "genMethod", defaultValue = "-1") long genMethod,
                @RequestParam(value = "machine", defaultValue = "-1") long machine,
                @RequestParam(value = "time", defaultValue = "-1") long time,
                @RequestParam(value = "seq", defaultValue = "-1") long seq) {
    
            return uidService.makeId( version, type, genMethod, machine, time, seq );
        }
    }
    

    实验验证

    • 实验一

    首先我们用浏览器调用接口 genid,来返回生成的全局唯一 ID 流水号,一切都是那么的简单优雅:

    生成全局唯一流水号

    • 实验二

    由于 Vesta 生成的全局唯一流水号具有 可反解 的优良特性,因此我们可以先生成一个流水号,然后调用 expid接口来反解出流水号所代表的意义:

    全局唯一流水号的反解效果


    后 记

    由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!



    14 条回复    2018-11-27 21:36:41 +08:00
    li27962278
        1
    li27962278  
       2018-11-22 08:33:17 +08:00
    学习 mark
    hanxiV2EX
        2
    hanxiV2EX  
       2018-11-22 08:36:55 +08:00 via Android
    额。。。uuid
    hansonwang99
        3
    hansonwang99  
    OP
       2018-11-22 08:38:44 +08:00 via iPhone
    @hanxiV2EX UUID 的短板很明显的
    Antihank
        4
    Antihank  
       2018-11-22 09:12:01 +08:00
    还不了解这个意义,战略 mark。
    SuperNovaSonic
        5
    SuperNovaSonic  
       2018-11-22 09:13:56 +08:00
    有大佬简单说下怎么实现的吗?
    baicheng10
        6
    baicheng10  
       2018-11-22 09:14:28 +08:00
    还不了解这个意义,战略 mark。
    inreality
        7
    inreality  
       2018-11-22 09:22:27 +08:00
    想知道算法用的什么,和 snowflake 比有什么优势?
    fy
        8
    fy  
       2018-11-22 09:41:04 +08:00
    我记得 Snowflake 是 Twitter 推出的算法。

    @SuperNovaSonic #5
    @inreality #7

    参考实现:

    ObjectID ( MongoDB 的算法,在 Snowflake 之后实现)
    https://github.com/fy0/slim/blob/master/slim/utils/myobjectid.py

    一个修改版(调整了字段位置)
    https://github.com/fy0/slim/blob/master/slim/utils/customid.py

    要问 Snowflake 这个算法比 uuid 强在哪,我认为是长度。
    “ uuid 短板很多”甚至不算是个说法。
    Tengdw
        9
    Tengdw  
       2018-11-22 09:52:49 +08:00
    @fy 还有一个 SnowFlake 对数据库索引比较友好
    cyhulk
        10
    cyhulk  
       2018-11-22 10:07:51 +08:00
    都算是类 snowflake 吧,我比较好奇的是它的序号是怎么生成的,文档中没有写,机器 id 也没有写,不如 mac 地址+random 安全吧,我比较关心的是序列号的生成方式,random ???
    passerbytiny
        11
    passerbytiny  
       2018-11-22 10:23:37 +08:00
    这么长的序号,反解意义不大,我觉得在“业务>性能”的前提下,还不如一个 findByID 接口。

    粗略有序性这一块应该大有看头,不过楼主并没有实验。

    最后再提一点偏见,程序员不应该使用脑图。
    iyangyuan
        12
    iyangyuan  
       2018-11-22 10:38:19 +08:00
    具体实现不清楚,不过这个序列号太长了。
    建议底层使用位操作,不要直接用字符串或者十进制表示信息,太过浪费。
    还有,时间戳可以用相对值(偏移量),这样可以大大节省存储空间。
    passerbytiny
        13
    passerbytiny  
       2018-11-22 10:47:48 +08:00   ❤️ 1
    看了一下 snowflake 算法,跟 UUID 相比,最主要的优势就是“有序”了,“有序”了自然可读性就高了、索引就好弄了。长度优势( 64bit vs 128bit)并不重要,而且 64bit 并不是算法要求的,而是习惯要求的(正好等于一个 Long )。

    因为要“有序”,劣势也是有的:算法复杂,生成方式易用性和生成速度上不及 UUID ;系统内全局唯一,但出了系统并不是全球唯一。在不需要“有序”的地方,还是用 UUID 更好。
    xiaojinmaolove
        14
    xiaojinmaolove  
       2018-11-27 21:36:41 +08:00 via iPhone
    Mark 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2637 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 03:49 · PVG 11:49 · LAX 19:49 · JFK 22:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.