查看原文
其他

百度新开源的分布式 ID 生成器,太强大了!

点击关注 👉 程序员老鬼 2022-07-01
来源:https://lilinchao.com/archives/1226.html


1、UidGenerator是什么


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

2、UidGenerator的优势


  1. 解决了时钟回调的问题
  2. 使用ringbuffer,无锁进行id的生产与消费,速度非常快
  3. 适用于多线程,不会有单线程瓶颈


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中的依赖不一致的话,就会导致冲突。

Spring Boot 基础就不介绍了,新手请看这个学习教程和示例仓库:https://github.com/javastacks/spring-boot-best-practice

排除冲突的依赖如下:
<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。
@Configurationpublic 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
@Mapperpublic 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实现类
@Servicepublic class WorkerNodeServiceImpl implements IWorkerNodeService { @Resource private UidGenerator uidGenerator;
@Override public long genUid() { Long uu_id = UidGeneratorComponent.uu_id; return uidGenerator.getUID(); }}

3.8 Controller
@RestControllerpublic 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


项目目录结构



往期推荐

美团又摊上事了,道歉!

记一次性能优化,单台 4 核 8G 机器支撑 5 万 QPS

一条 update 语句引起的事故,这回让开发长长记性!!

周末撸了一个简版的 Redis,轻松搞定高性能的 key-value 服务

面试官问:数据库连接池为什么要用threadlocal?不用会怎样?


耗时数月整理出来的技术书籍免费分享给大家
扫描下方二维码,回复关键字【电子书】获取!
喜欢的这里报道
↘↘↘

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存