天天看點

百度開源的分布式 ID 生成器,太強大了!

一、UidGenerator是什麼

UidGenerator是百度開源的一款分布式高性能的唯一ID生成器,是基于snowflake模型的一種ID生成器

二、UidGenerator的優勢

解決了時鐘回調的問題

使用ringbuffer,無鎖進行id的生産與消費,速度非常快

适用于多線程,不會有單線程瓶頸

三、整合

本次通過SpringBoot2.x和MyBatis對百度UidGenerator進行整合。

3.1 UidGenerator引入

一般使用兩種方式引入UidGenerator:

1)從官網下載下傳源碼作為自己項目的一個Module引入到項目中

官網位址:

https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

2)網際網路jar包引入(本文采取的是該種方式)

因為UidGenerator自帶引入了mybatis,和自己項目有沖突的可以進行排除

百度開源的分布式 ID 生成器,太強大了!

排除沖突依賴

uid-generator中依賴了logback和mybatis。一般在項目搭建過程中,springboot中已經有了logback依賴,mybatis會作為單獨的依賴引入。如果版本和uid-generator中的依賴不一緻的話,就會導緻沖突。

排除沖突的依賴如下:

<dependency>
    <groupId>com.xfvape.uid</groupId>
    <artifactId>uid-generator</artifactId>
    <version>0.0.4-RELEASE</version>
    <exclusions>
         <exclusion>
             <groupId>org.slf4j</groupId>
             <artifactId>log4j-over-slf4j</artifactId>
         </exclusion>
         <exclusion>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
         </exclusion>
         <exclusion>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </exclusion>
          <exclusion>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis-spring</artifactId>
         </exclusion>
         <exclusion>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis</artifactId>
         </exclusion>
    </exclusions>
</dependency>      

3.2 建立表

在 MySQL資料庫中建一個名為

WORKER_NODE

的資料表,其 sql如下:

CREATE TABLE WORKER_NODE
(
ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id',
HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name',
PORT VARCHAR(64) NOT NULL COMMENT 'port',
TYPE INT NOT NULL COMMENT 'node type: ACTUAL or CONTAINER',
LAUNCH_DATE DATE NOT NULL COMMENT 'launch date',
MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time',
CREATED TIMESTAMP NOT NULL COMMENT 'created time',
PRIMARY KEY(ID)
)
 COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;      

建表時可能會報錯,原因是該建表語句定義了兩個TIMESTAMP字段,因為mysql 低版本控制比較嚴格

解決辦法:

方式一:

直接把TIMESTAMP改成DATETIME 即可

方式二:

建表前先設定sql_mode:

set sql_mode="NO_ENGINE_SUBSTITUTION";

3.3 将uid-generator核心對象裝配為spring的bean

uid-generator提供了兩種生成器: DefaultUidGenerator、CachedUidGenerator。如對UID生成性能有要求, 請使用CachedUidGenerator。

@Configuration
public class WorkerNodeConfig {

    @Bean("disposableWorkerIdAssigner")
    public DisposableWorkerIdAssigner disposableWorkerIdAssigner(){
        DisposableWorkerIdAssigner disposableWorkerIdAssigner = new DisposableWorkerIdAssigner();
        return  disposableWorkerIdAssigner;
    }

    @Bean("cachedUidGenerator")
    public UidGenerator uidGenerator(DisposableWorkerIdAssigner disposableWorkerIdAssigner){
        CachedUidGenerator cachedUidGenerator = new CachedUidGenerator();
        cachedUidGenerator.setWorkerIdAssigner(disposableWorkerIdAssigner);
        return cachedUidGenerator;
    }
}      

直接建立WorkerNodeConfig類,複制以上代碼。

3.4 重寫WorkerIdAssigner接口

public class DisposableWorkerIdAssigner implements WorkerIdAssigner {

    @Resource
    private WorkerNodeMapper workerNodeMapper;
    @Override
    @Transactional
    public long assignWorkerId() {
        WorkerNode workerNode = buildWorkerNode();

        workerNodeMapper.addWorkerNode(workerNode);

        return workerNode.getId();
    }

    private WorkerNode buildWorkerNode() {
        WorkerNode workNode = new WorkerNode();
        if (DockerUtils.isDocker()) {
            workNode.setType(WorkerNodeType.CONTAINER.value());
            workNode.setHostName(DockerUtils.getDockerHost());
            workNode.setPort(DockerUtils.getDockerPort());
            workNode.setLaunchDate(new Date());
        } else {
            workNode.setType(WorkerNodeType.ACTUAL.value());
            workNode.setHostName(NetUtils.getLocalAddress());
            workNode.setPort(System.currentTimeMillis() + "-" + RandomUtils.nextInt(100000));
            workNode.setLaunchDate(new Date());
        }

        return workNode;
    }
}      

建立一個DisposableWorkerIdAssigner類直接複制以上代碼。

3.5 引入WorkerNodeMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.uidgenerator.mapper.WorkerNodeMapper">
  <resultMap id="BaseResultMap"
             type="com.uidgenerator.entity.WorkerNode">
    <id column="ID" jdbcType="BIGINT" property="id" />
    <result column="HOST_NAME" jdbcType="VARCHAR" property="hostName" />
    <result column="PORT" jdbcType="VARCHAR" property="port" />
    <result column="TYPE" jdbcType="INTEGER" property="type" />
    <result column="LAUNCH_DATE" jdbcType="DATE" property="launchDate" />
    <result column="MODIFIED" jdbcType="TIMESTAMP" property="modified" />
    <result column="CREATED" jdbcType="TIMESTAMP" property="created" />
  </resultMap>

  <insert id="addWorkerNode" useGeneratedKeys="true" keyProperty="id"
          parameterType="com.uidgenerator.entity.WorkerNode">
        INSERT INTO WORKER_NODE
        (HOST_NAME,
         PORT,
         TYPE,
         LAUNCH_DATE,
         MODIFIED,
         CREATED)
        VALUES (
          #{hostName},
          #{port},
          #{type},
          #{launchDate},
          NOW(),
          NOW())
    </insert>

  <select id="getWorkerNodeByHostPort" resultMap="BaseResultMap ">
        SELECT
            ID,
            HOST_NAME,
            PORT,
            TYPE,
            LAUNCH_DATE,
            MODIFIED,
            CREATED
        FROM
            WORKER_NODE
        WHERE
            HOST_NAME = #{host} AND PORT = #{port}
    </select>
</mapper>      

可以通過mybatis生成工具直接生成檔案和實體類,然後将插入和查詢方法複制

注意修改resultMap中的type和insert中的keyProperty為自身項目的實體類位址

在application.yml檔案中增加如下配置

百度開源的分布式 ID 生成器,太強大了!
百度開源的分布式 ID 生成器,太強大了!

3.9 通路接口測試

http://127.0.0.1:8099/test/baidu/uid
百度開源的分布式 ID 生成器,太強大了!
百度開源的分布式 ID 生成器,太強大了!