⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《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 访问接口测试