背景
我们大致了解常见分布式id有9种解决方案如下:
- Redis自增id
- UUID
- 数据库id
- 多主数据库id
- 分段id
- snowflake
- 基于snowflake的百度 uid-generator
- 基于snowflake的美团 leaf
- 滴滴的分段 tinyId
基于高可用,高性能,简单易用性,使用tinyId和学习tinyId源码.
关于tinyid,并不是滴滴官方产品,只是滴滴拥有的代码。
代码行数统计
简单使用
## git原版
git clone https://github.com/didi/tinyid.git
## cnpmjs 加速版本
git clone https://github.com.cnpmjs.org/didi/tinyid.git
源码分析点
- 项目模块
tinyid-base
tinyid-client
tinyid-server
之间的关系
tinyid-base
1. SegmentId 中定义了号段核心实体,用于获取nextId()
2. 定义公用的 IdGeneratorFactory,IdGenerator,SegmentIdService 接口,让client和server实现其各自方法。
tinyid-server 是部署分布式id服务的
tinyid-server是java client,需要自己install到本地,官方没有发布到maven仓库
- client IdGenerator 是如何保证高性能,高可用的
1. 高性能,不论调用的是nextId还是batch方法,本质是在本地生成的,如果超出了本地生成策略。去服务器获取Segment段的长度. 不必每次发送http请求获取。
2. 高可用,客户端在resource配置 tinyid_client.properties文件,其参数 tinyid.server =>
#(tinyid.server=localhost:9999/gateway,ip2:port2/prefix,...) 用逗号分隔,达到负载均衡的效果。
详情可看源码 com.xiaoju.uemc.tinyid.client.factory.impl.IdGeneratorFactoryClient#init
当获取新的segment时会choose,算法是 random
- 服务器如何搭建集群
为了防止服务端挂掉产生的单点问题。导致服务不可用,遂需要搭建集群,直接搭建多节点就行。
关于mysql的多主数据源问题=> com.xiaoju.uemc.tinyid.base.entity.SegmentId#init
如果你有多个数据源达到数据库的高可用,需要配置数据库 delta=数据库个数
remainder 从0按顺序递增
/**
* 这个方法主要为了1,4,7,10...这种序列准备的
* 设置好初始值之后,会以delta的方式递增,保证无论开始id是多少都能生成正确的序列
* 如当前是号段是(1000,2000],delta=3, remainder=0,则经过这个方法后,currentId会先递增到1002,之后每次增加delta
* 因为currentId会先递增,所以会浪费一个id,所以做了一次减delta的操作,实际currentId会从999开始增,第一个id还是1002
*/
public void init() {
if (isInit) {
return;
}
synchronized (this) {
if (isInit) {
return;
}
long id = currentId.get();
if (id % delta == remainder) {
isInit = true;
return;
}
for (int i = 0; i <= delta; i++) {
id = currentId.incrementAndGet();
if (id % delta == remainder) {
// 避免浪费 减掉系统自己占用的一个id
currentId.addAndGet(0 - delta);
isInit = true;
return;
}
}
}
}
tinyid-server
中是如何建立多数据源的
核心是 => org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
在读取envionment配置时,没见过的一个用法是 RelaxedPropertyResolver,可以根据前缀获取信息
tinyid-server
的maven profiles配置学习
<profiles>
<profile>
<id>online</id>
<properties>
<package.environment>online</package.environment>
</properties>
</profile>
<profile>
<id>offline</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<package.environment>offline</package.environment>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources/${package.environment}</directory>
</resource>
<resource>
<directory>src/main/resources/base</directory>
</resource>
</resources>
</build>
不过貌似一般不这么用V