# tinyservices-id
**Repository Path**: java-bigdata/tinyservices-id
## Basic Information
- **Project Name**: tinyservices-id
- **Description**: tinyservices-id 是一个高性能可扩展生成唯一id的服务。目前包含两种生成算法:雪花算法以及自增号段。
部署和使用都很便捷,无论是个人、团队、或是企业,都能够快速的使用 tinyservices-id 来生成唯一id。
- **Primary Language**: Java
- **License**: GPL-3.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 9
- **Created**: 2022-04-27
- **Last Updated**: 2022-04-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# tinyservices-id
## 一、软件介绍
tinyservices-id 是一个高性能可扩展生成唯一id的服务。目前包含两种生成算法:雪花算法以及自增号段。
部署和使用都很便捷,无论是个人、团队、或是企业,都能够快速的使用 tinyservices-id 来生成唯一id。
## 二、软件架构

- tinyservices-id 有两种id生成算法:基于美团leaf的号段算法以及基于推特推出的雪花算法
- 分为三大模块:
- id-server:server端,封装id生成算法以及对外提供http服务,可直接通过http获取id
- id-sdk:sdk包,可直接依赖sdk获取id,省去了获取id的网络开销,性能极佳
- id-portal:管理后台,通过管理后台新增/修改/删除的数据无需重启id-server服务,1分钟后可自动生效。每隔1分钟会自动同步数据到buffer中
- 雪花算法对中间件(MYSQL、ZOOKEEPER)弱依赖,每隔几秒会自动将中间件里的workerId刷到本地文件当中,这样即使中间件挂了的话也不影响id-server的正常使用,因为会自动降级到本地文件,因此也不会影响到业务系统
- 提供灵活配置以及可扩展能力,可自定义配置文件名称甚至无需配置文件,自定义实现类将配置放到配置中心。若雪花算法不希望用MYSQL或ZOOKEEPER,也可以自定义实现类进行实现,比如自定义ETCD等其他方式
## 三、安装教程
### 1、准备工作
#### 1.1 Java
- 版本要求:1.8+,在配置好后可以通过如下命令检查:
```sh
java -version
```
#### 1.2 MySQL
- 版本要求:5.6.5+,连接上MySQL后,可以通过如下命令检查:
```mysql
SHOW VARIABLES WHERE Variable_name = 'version';
```
#### 1.3 下载
- 选择对应的发行版本下载代码,可以在如下链接进行下载:
[https://gitee.com/tinyservices/tinyservices-id/releases](https://gitee.com/tinyservices/tinyservices-id/releases)
### 2、安装步骤
#### 2.1 创建数据库
- 通过MySQL客户端导入如下sql文件即可:
```sql
scripts/sql/ts_id.sql
```
- 导入成功后执行以下sql语句来验证:
```sql
use ts_id;
show tables;
```
- sql执行结果如下则代表正常:
```sql
ts_id_segment
ts_id_token
ts_id_worker_id
```
#### 2.2 配置数据库
- 根据不同环境修改不同的配置值(`dev/test/pre/prod`),所修改文件的位置如下:
```sh
scripts/build.sh
```
- `scripts/build.sh`配置含义如下:
| 配置项 | 配置含义 | 是否必填 | 默认值 |
| :------------------------------: | :----------------------------------------------------------: | -------- | :-----------------------------------: |
| id_server_config_db_driver | jdbc的driver | 必填 | 无 |
| id_server_config_db_url | jdbc的url | 必填 | 无 |
| id_server_config_db_username | mysql用户名 | 必填 | 无 |
| id_server_config_db_password | mysql密码 | 必填 | 无 |
| id_server_config_db_initial_size | 链接池初始化大小,不写则采取默认值 | 非必填 | 0 |
| id_server_config_db_min_idle | 链接池最小空闲连接数,不写则采取默认值 | 非必填 | 0 |
| id_server_config_db_max_active | 链接池最大活跃数,不写则采取默认值 | 非必填 | 8 |
| id_server_config_db_max_wait | 链接池获取连接的最大等待时间,单位ms,不写则采取默认值 | 非必填 | -1 |
| id_snowflake_mode | 雪花算法模式:
LOCAL(本地文件的方式)
MYSQL(对应`ts_id_worker_id`表)
ZOOKEEPER(每次都根据`ip:port`创建一个顺序持久节点)
RECYCLABLE_ZOOKEEPER(如果`ip:port`经常变的话那1023个workerId很容易就被耗光,此种方式是可循环利用workerId的ZOOKEEPER方式) | 非必填 | LOCAL |
| id_snowflake_port | 雪花算法端口,不管采取何种方式,都会根据`ip:port`生成唯一key与workerId一一对应。
注:不会真正启动端口,只是作为唯一标识,所以和项目端口可重叠。 | 非必填 | 8080 |
| id_snowflake_epoch | 雪花算法epoch,起始值,默认值:1648742400000L | 非必填 | 1648742400000L(2022-04-01 00:00:00) |
| id_snowflake_zkConnectionAddr | 如果雪花算法采取ZOOKEEPER或RECYCLABLE_ZOOKEEPER模式,则需要配置ZOOKEEPER地址, | 非必填 | localhost:2181 |
| id_server_log_dir | id-server项目日志目录 | 必填 | 无 |
| id_portal_log_dir | id-portal项目日志目录 | 必填 | 无 |
| id_server_port | id-server项目端口号 | 非必填 | 8080 |
| id_portal_port | id-portal项目端口号 | 非必填 | 8080 |
## 四、部署运行
### 1. 部署运行
#### 1.1 编译打包
- 可直接运行修改后的`scripts/build.sh`文件,比如:
```sh
sh scripts/build.sh dev id-server
sh scripts/build.sh dev id-portal
```
> 可选参数:
>
> $1:dev/test/pre/prod,分别代表开发环境/测试环境/预生产环境/生产环境。
>
> $2:id-server/id-portal,分别代表id-server/id-portal两个moudle,也就是给哪个moudle打包,配置就是上面所输入dev/test/pre/prod所对应的变量。
#### 1.2 运行
- 编译打包完成后,可直接执行启动脚本,如下:
```sh
sh id-server/scripts/startup.sh
sh id-portal/scripts/startup.sh
```
- 验证id-server是否启动成功(也可以通过看日志的方式,日志位置就是`id_server_log_dir`对应的值)
```shell
curl http(s)://ip:port/health/check
```
返回如下代表成功:
```json
{
"code": 0,
"msg": "Processed successfully",
"data": 666
}
```
- 验证id-portal是否启动成功(也可以通过看日志的方式,日志位置就是`id_portal_log_dir`对应的值)
直接浏览器打开`http(s)://ip:port`,会出现如下页面:

## 五、使用说明
### 1. 准备工作
- token是鉴权的唯一方式。所以需要在`id-portal`管理后台创建好token和业务类型的对应关系。
- 如果是号段模式的话,则需要在`id-portal`管理后台创建好Segment。
- 以上两种添加方式操作完无需重启服务,会有定时任务每一分钟去查数据库将其缓存到内存中,这样获取id的时候直接可用。
使用上有两种使用方式,分别是Server和Sdk,Server的使用很简单,直接对外提供获取id的http接口。Sdk则需要业务系统依赖jar包然后调用方法获取id。下面详细介绍下二者使用方式和区别。
### 2. Server
不管获取哪种算法的id,都需要添加如下header,否则会提示-4错误码,代表没权限:
```properties
tinyservice-id-token=token(token由id-portal管理后台Token菜单获取)
```
#### 2.1 获取号段id
````
# 获取单个
GET http(s)://ip:port/segment/{业务类型}
# 批量获取
GET http(s)://ip:port/segment/{业务类型}/{获取的id数量}
````
#### 2.2 获取雪花id
```
# 获取单个
GET http(s)://ip:port/snowflake/{业务类型}
# 批量获取
GET http(s)://ip:port/snowflake/{业务类型}/{获取的id数量}
```
### 3. Sdk
#### 3.1 添加依赖
```xml
com.gitee.tinyservices
id-sdk
1.0.0
```
#### 3.2 添加配置
在`classpath`(`springboot`项目通常为`resources`)下添加名为`tinyservices_id_sdk.properties`的配置文件,包含如下配置项:
```properties
# id-server的地址,多个用英文逗号隔开,会随机选择一个地址使用
tinyservices.id.server=http://localhost:9990
# 要访问业务类型的token。可在id-portal管理后台配置/查看
tinyservices.id.token=U2FsdGVkX18uip/K7pLiM4ZD5RKrHacD64eKWCNfmW4=
```
#### 3.3 获取id
```java
// IdSdkTypeEnum.SEGMENT 号段模式
// IdSdkTypeEnum.SNOWFLAKE 雪花模式
AbstractIdSdk idSdk = IdSdkFactory.getInstance().getIdSdk(IdSdkTypeEnum.SEGMENT).registerConfigSupport();
// 获取id
for (int i = 0; i < 10; i ++) {
Long id = idSdk.getId("业务类型");
}
```
#### 3.4 全部配置
| 配置项 | 配置含义 | 是否必填 | 默认值 |
| :-----------------------------: | :----------------------------------------------------------: | :------: | :-----------------------------------: |
| tinyservices.id.token | token,可从id-portal管理后台配置/查看 | 必填 | 无 |
| tinyservices.id.server | id-server服务列表,多个用逗号隔开,比如:http://localhost:9990 | 必填 | 无 |
| tinyservices.id.readTimeout | sdk和server的超时时间(ms) | 非必填 | 5000ms |
| tinyservices.id.connectTimeout | sdk和server的连接超时时间(ms) | 非必填 | 5000ms |
| tinyservices.id.snowflake.epoch | 雪花算法的开始时间戳 | 非必填 | 1648742400000L(2022-04-01 00:00:00) |
### 4. 使用建议
Segment模式根据自身业务量规划好步长,比如业务QPS是10000,步长写了10,那可能瞬时流量进来的话会获取到少部分id为null的情况,因为步长太小,双buffer都被瞬时流量打满。
如果对性能有极高要求的话,推荐Sdk方式,此方式直接依赖jar包调用本地方法的方式生成id,由于Sdk的方式免去了网络开销,理论上QPS可达千万。
如果对性能要求不高的话可以使用Server方式,直接请求http接口,减少系统依赖。
## 六、FAQ
### 1. 如何在本地运行?
给项目添加对应环境变量即可,IDEA添加环境变量的方式如下:

#### 1.1 id-server
添加如下环境变量:
```properties
spring.datasource.driver=com.mysql.cj.jdbc.Driver;spring.datasource.url=jdbc:mysql://localhost:3306/ts_id?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT;spring.datasource.username=root;spring.datasource.password=12345678;spring.datasource.initial-size=;spring.datasource.min-idle=;spring.datasource.max-active=;spring.datasource.max-wait=;id_server_log_dir=/data/logs/id-server;server_port=9990;id_server_snowflake_mode=MYSQL;id_server_snowflake_port=8888;id_server_snowflake_epoch=1648742400000;id_server_snowflake_zkConnectionAddr=localhost:2181
```
> 数据库连接、用户名、密码、端口等等参数都根据自身环境自行修改即可。
#### 1.2 id-portal
添加如下环境变量:
```properties
spring.datasource.driver=com.mysql.cj.jdbc.Driver;spring.datasource.url=jdbc:mysql://localhost:3306/ts_id?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT;spring.datasource.username=root;spring.datasource.password=12345678;spring.datasource.initial-size=;spring.datasource.min-idle=;spring.datasource.max-active=;spring.datasource.max-wait=;id_portal_log_dir=/data/logs/id-portal;server_port=9990
```
> 数据库连接、用户名、密码、端口等等参数都根据自身环境自行修改即可。
### 2. id-portal添加配置后多久生效?
不管是Segment还是Token方式,新增或修改配置后都是一分钟生效,也就是一分钟后id-server服务可用这份配置。
### 3. 雪花算法如何自定义注册中心?
目前内置四种模式:LOCAL本地文件模式、MYSQL模式、ZOOKEEPER模式以及RECYCLABLE_ZOOKEEPER可回收利用workerId的ZOOKEEPER模式,若想采用其他中间件作为注册中心,那么如下步骤:
- 首先自定义一个`Class`继承`AbstractIdSnowflakeHolder`类且重写相应的方法,可参考其他子类,比如:`ZookeeperIdSnowflakeHolder`
- 其次在枚举类`IdSnowflakeHolderTypeEnum`当中新增相应类型(此类型必须和配置文件里的`tinyservices.id.snowflake.mode`配置一一对应)
- 最后修改工厂类的方法`IdSnowflakeHolderFactory#getIdSnowflakeHolder`,为其新增`case`分支,值为新增的枚举类型
### 4. 雪花算法采取的是RECYCLABLE_ZOOKEEPER模式,启动很慢?
RECYCLABLE_ZOOKEEPER模式首次启动慢正常的,此模式会在启动的时候提前在Zookeeper创建1023个持久节点作为workerId池,利于workerId的复用。因为是持久节点,所以只有首次创建会很慢,之后启动会发现已存在1023个持久节点则不再重复创建。
### 5. MySQL驱动报错问题
如项目启动过程中出现MySQL驱动错误的话,则自行按需修改版本号,目前内置版本是8.0.21,在根`pom.xml`文件中。如下:
```xml
mysql
mysql-connector-java
8.0.21
runtime
```
> 修改后再打包的时候别忘了修改`scripts/build.sh`里的`id_server_config_db_driver`配置。
>
> 若是本地部署代码运行的话则别忘了修改环境变量里的`id_server_config_db_driver`配置。
### 6. Sdk模式配置文件如何修改名称?
如果不想采取默认的配置文件名称`tinyservices_id_sdk.properties`,那可以采取如下方式进行自定义:
```java
AbstractIdSdk idSdk = IdSdkFactory.getInstance().getIdSdk(IdSdkTypeEnum.SNOWFLAKE, "自定义配置文件名称.properties").registerConfigSupport();
// 获取id
for (int i = 0; i < 10; i ++) {
Long id = idSdk.getId("业务类型");
}
```
### 7. Sdk模式如何将配置放到注册中心?
如果不想采取配置文件的方式,公司规范都放到配置中心该怎么办?可以采取如下方式进行自定义:
#### 7.1 自定义配置类
```java
/**
* 可选择性重写如下五个方法:
* String getBizToken();
* String getServerUrl();
* int getReadTimeout();
* int getConnectTimeout();
* long getEpoch();
*/
public class CustomIdConfigSupport extends AbstractIdConfigSupport {
@Override
public String getServerUrl() {
// 可通过配置中心获取
return null;
}
@Override
public String getBizToken() {
// 可通过配置中心获取
return null;
}
// ... 省略其他非必需重写的方法
}
```
#### 7.2 注册配置类
```java
AbstractIdSdk idSdk = IdSdkFactory.getInstance().getIdSdk(IdSdkTypeEnum.SNOWFLAKE).registerConfigSupport(new CustomIdConfigSupport());
// 获取id
for (int i = 0; i < 10; i ++) {
Long id = idSdk.getId("业务类型");
}
```
### 8. Sdk模式如何获取不同业务类型的id?
背景:sdk方式一个配置文件只允许配置一个token,若一个项目想获取不同业务类型的token该如何获取呢?
解答:有两种方式可以解决:
1. 一个业务类型支持配置多个token,可以将不同业务类型都配置一个公共的token,后台目前只允许生成token,不允许自定义token,如需这么做可以二开下`id-portal`,放开token禁止输入的限制,也可以手动修改`ts_id_worker_id`表数据。
2. 或者采取自定义配置文件的方式,比如要获取两个业务类型的id:
第一个业务类型:
```java
AbstractIdSdk idSdk = IdSdkFactory.getInstance().getIdSdk(IdSdkTypeEnum.SEGMENT, "sdk_1.properties").registerConfigSupport();
idSdk.getId("test1");
```
第二个业务类型:
```java
AbstractIdSdk idSdk = IdSdkFactory.getInstance().getIdSdk(IdSdkTypeEnum.SEGMENT, "sdk_2.properties").registerConfigSupport();
idSdk.getId("test2");
```
## 七、鸣谢
- 美团:[https://github.com/Meituan-Dianping/Leaf](https://github.com/Meituan-Dianping/Leaf)
- 滴滴:[https://github.com/didi/tinyid](https://github.com/didi/tinyid)
## 八、结束语
- 若贵司接入了`tinyservices-id`,欢迎私信/评论贵司名称,您的支持是我永恒的动力。
- 如有任何问题请提issue。[https://gitee.com/tinyservices/tinyservices-id/issues](https://gitee.com/tinyservices/tinyservices-id/issues)