《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 lilinchao.com/archives/1226.html 「lilinchao」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

一、UidGenerator是什么

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

二、UidGenerator的优势

1.解决了时钟回调的问题

2.使用ringbuffer,无锁进行id的生产与消费,速度非常快

3.适用于多线程,不会有单线程瓶颈

三、整合

本次通过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,和自己项目有冲突的可以进行排除

<dependency>
<groupId>com.xfvape.uid</groupId>
<artifactId>uid-generator</artifactId>
<version>0.0.4-RELEASE</version>
</dependency>

排除冲突依赖

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提供了两种生成器: DefaultUidGeneratorCachedUidGenerator。如对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文件中增加如下配置

mybatis:
mapper-locations: classpath:mybatis/*.xml

3.6实体类

使用插件生成的实体类

3.7创建Mapper

@Mapper
public interface WorkerNodeMapper {

int addWorkerNode(WorkerNode workerNodeEntity);


WorkerNode getWorkerNodeByHostPort(@Param("host") String host, @Param("port") String port);

}

3.8 Service

IWorkerNodeService接口

public interface IWorkerNodeService {
public long genUid();
}

WorkerNodeServiceImpl实现类

@Service
public class WorkerNodeServiceImpl implements IWorkerNodeService {
@Resource
private UidGenerator uidGenerator;

@Override
public long genUid() {
Long uu_id = UidGeneratorComponent.uu_id;
return uidGenerator.getUID();
}
}

3.8 Controller

@RestController
public class WorkerNodeServiceController {
@Resource
private IWorkerNodeService workerNodeService;
/**
* 集成百度uid-generator生成id
* @return
*/
@GetMapping("/baidu/uid")
public long baiduUid(){
long uid = workerNodeService.genUid();
return uid;
}
}

3.9 访问接口测试

http://127.0.0.1:8099/test/baidu/uid

项目目录结构

文章目录
  1. 1. 一、UidGenerator是什么
  2. 2. 二、UidGenerator的优势
  3. 3. 三、整合
    1. 3.1. 3.1 UidGenerator引入
    2. 3.2. 3.2 创建表
    3. 3.3. 3.3 将uid-generator核心对象装配为spring的bean
    4. 3.4. 3.4 重写WorkerIdAssigner接口
    5. 3.5. 3.5 引入WorkerNodeMapper.xml
    6. 3.6. 3.6实体类
    7. 3.7. 3.7创建Mapper
    8. 3.8. 3.8 Service
    9. 3.9. 3.8 Controller
    10. 3.10. 3.9 访问接口测试