|
| 1 | + |
| 2 | +uid-generator-spring-boot |
| 3 | +========================== |
| 4 | + |
| 5 | +UidGenerator是Java实现的, 基于[Snowflake](https://github.com/twitter/snowflake)算法的支持响应式编程分布式唯一ID生成器。 |
| 6 | + |
| 7 | +基与 [雪花算法](https://github.com/twitter/snowflake),[百度UidGenerator](https://github.com/baidu/uid-generator),[uid-generator-spring-boot-starter](https://github.com/wujun234/uid-generator-spring-boot-starter) |
| 8 | + |
| 9 | + |
| 10 | +* 更改了打包结构,抽出了Worker node id的生成接口, 使其可以自定义实现分配 Worker node id的方法, 本项目中实现了四种数据库存储的方法。 |
| 11 | +* 支持 Spring boot Autoconfigure |
| 12 | +* 支持响应式编程,返回`Mono<Long>`, |
| 13 | + * 在CachedUidGenerator:消耗速度高于填充速度时,等待填充完成后以非阻塞的方式通知订阅者。 |
| 14 | + * 在DefaultUidGenerator不允许使用未来时间(未到达的时间)状态:当前时间ID耗尽等待下一秒后非阻塞的方式通知订阅者 |
| 15 | +* 支持 mybatis jdbc, mybatis r2bc , jap jdbc ,jap r2dbc 四种数据库连接方式 |
| 16 | + |
| 17 | +你以可以根据[接口包](https://github.com/cooperlyt/uid-generator-spring-boot/tree/master/uid-generator-api)自己实现Worker node分配的实现 |
| 18 | + |
| 19 | +## 原理和性能 |
| 20 | + |
| 21 | +请参见[Snowflake](https://github.com/twitter/snowflake)和[百度UidGenerator](https://github.com/baidu/uid-generator) |
| 22 | + |
| 23 | +## 使用 |
| 24 | + |
| 25 | +### Maven |
| 26 | + |
| 27 | +#### spring boot autoconfig 方式 |
| 28 | + |
| 29 | +当前版本:1.0.5 |
| 30 | + |
| 31 | +```xml |
| 32 | + |
| 33 | +<!-- 根据你的项目环境 选择一种 worker node 分配方式 --> |
| 34 | + |
| 35 | +<!-- 以下仅选一种即可,多了会冲突 --> |
| 36 | + |
| 37 | +<!--mybatis jdbc --> |
| 38 | +<dependency> |
| 39 | + <groupId>cooperlyt.github.io</groupId> |
| 40 | + <artifactId>uid-generator-mybatis-jdbc-spring-boot-starter</artifactId> |
| 41 | + <version>${uid.version}</version> |
| 42 | +</dependency> |
| 43 | + |
| 44 | + <!--mybatis r2dbc --> |
| 45 | +<dependency> |
| 46 | +<groupId>cooperlyt.github.io</groupId> |
| 47 | +<artifactId>uid-generator-mybatis-r2dbc-spring-boot-starter</artifactId> |
| 48 | +<version>${uid.version}</version> |
| 49 | +</dependency> |
| 50 | + |
| 51 | + |
| 52 | + <!--jpa jdbc --> |
| 53 | +<dependency> |
| 54 | +<groupId>cooperlyt.github.io</groupId> |
| 55 | +<artifactId>uid-generator-jap-jdbc-spring-boot-starter</artifactId> |
| 56 | +<version>${uid.version}</version> |
| 57 | +</dependency> |
| 58 | + |
| 59 | + <!--jpa r2dbc --> |
| 60 | +<dependency> |
| 61 | +<groupId>cooperlyt.github.io</groupId> |
| 62 | +<artifactId>uid-generator-jpa-r2dbc-spring-boot-starter</artifactId> |
| 63 | +<version>${uid.version}</version> |
| 64 | +</dependency> |
| 65 | + |
| 66 | + <!-- 选择相印的数据库驱动包 --> |
| 67 | + <!-- jdbc驱动 --> |
| 68 | +<dependency> |
| 69 | + <groupId>org.mariadb.jdbc</groupId> |
| 70 | + <artifactId>mariadb-java-client</artifactId> |
| 71 | +</dependency> |
| 72 | + |
| 73 | + <!-- r2dbc驱动 --> |
| 74 | +<dependency> |
| 75 | + <groupId>org.mariadb</groupId> |
| 76 | + <artifactId>r2dbc-mariadb</artifactId> |
| 77 | + <version>1.1.3</version> |
| 78 | +</dependency> |
| 79 | + |
| 80 | +``` |
| 81 | +由于开发时Mybatis官方还不支持r2dbc所以使用了[reactive-mybatis-support](https://github.com/chenggangpro/reactive-mybatis-support) |
| 82 | + |
| 83 | + |
| 84 | +### 数据库(可选) |
| 85 | +如果使用数据库实现的Worker node id分配器,需要先建立表 WORKER_NODE, 脚本: |
| 86 | +```sql |
| 87 | +DROP TABLE IF EXISTS WORKER_NODE; |
| 88 | +CREATE TABLE WORKER_NODE |
| 89 | +( |
| 90 | +ID BIGINT NOT NULL AUTO_INCREMENT COMMENT 'auto increment id', |
| 91 | +HOST_NAME VARCHAR(64) NOT NULL COMMENT 'host name', |
| 92 | +PORT VARCHAR(64) NOT NULL COMMENT 'port', |
| 93 | +TYPE INT NOT NULL COMMENT 'node type: CONTAINER(1), ACTUAL(2), FAKE(3)', |
| 94 | +LAUNCH_DATE DATE NOT NULL COMMENT 'launch date', |
| 95 | +MODIFIED TIMESTAMP NOT NULL COMMENT 'modified time', |
| 96 | +CREATED TIMESTAMP NOT NULL COMMENT 'created time', |
| 97 | +PRIMARY KEY(ID) |
| 98 | +) COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB; |
| 99 | +``` |
| 100 | + |
| 101 | +### spring boot 配置 |
| 102 | + |
| 103 | +#### jdbc 配置 (以mariadb为例) |
| 104 | + |
| 105 | +```yml |
| 106 | +mybatis: |
| 107 | + configuration: |
| 108 | + default-fetch-size: 100 |
| 109 | + default-statement-timeout: 30 |
| 110 | + map-underscore-to-camel-case: true |
| 111 | +spring: |
| 112 | + datasource: |
| 113 | + driver-class-name: org.mariadb.jdbc.Driver |
| 114 | + url: jdbc:mariadb://127.0.0.1:3306/database? |
| 115 | + username: root |
| 116 | + password: **** |
| 117 | +``` |
| 118 | +
|
| 119 | +#### r2dbc 配置 |
| 120 | +
|
| 121 | +```yml |
| 122 | + |
| 123 | +r2dbc: |
| 124 | + mybatis: |
| 125 | + mapper-locations: classpath:mapper/*.xml |
| 126 | + map-underscore-to-camel-case: true |
| 127 | +spring: |
| 128 | + r2dbc: |
| 129 | + mybatis: |
| 130 | + r2dbc-url: r2dbc:mariadb://127.0.0.1:3306/database |
| 131 | + username: root |
| 132 | + password: **** |
| 133 | + pool: |
| 134 | + max-idle-time: PT3M |
| 135 | + validation-query: SELECT 1 FROM DUAL |
| 136 | + initial-size: 1 |
| 137 | + max-size: 3 |
| 138 | + acquire-retry: 3 |
| 139 | + validation-depth: REMOTE |
| 140 | + max-create-connection-time: PT30S |
| 141 | + |
| 142 | +``` |
| 143 | + |
| 144 | +#### 自定义 CachedUidGenerator 拒绝策略 |
| 145 | + |
| 146 | +```java |
| 147 | + // 生成一批ID后由于buffer环已满无法填充时的处理方式, 默认为丢弃并打印日志 |
| 148 | + @Bean |
| 149 | + RejectedPutBufferHandler customPutHandler() { |
| 150 | + return (buffer, id) -> { |
| 151 | + do your |
| 152 | + }; |
| 153 | + } |
| 154 | + |
| 155 | + // 由于消耗过快使用到了未来时间(未到达的时间)时的处理方式, 默认为允许并打印日志 |
| 156 | + @Bean |
| 157 | + TimeIsFutureHandler customFutureTimeHandler() { |
| 158 | + return (futureTime, currentTime) -> { |
| 159 | + do your |
| 160 | + }; |
| 161 | + } |
| 162 | + |
| 163 | +``` |
| 164 | + |
| 165 | +#### ID 生成配置, 如应用在生产环境请确认以下参数,并确保你已经理解每个参数的意义。 |
| 166 | +```yml |
| 167 | +uid: |
| 168 | + timeBits: 30 # 时间位, 默认:30 |
| 169 | + workerBits: 16 # 机器位, 默认:16 |
| 170 | + seqBits: 7 # 序列号, 默认:7 |
| 171 | + epochStr: "2023-02-17" # 初始时间, 默认:"2019-02-20" |
| 172 | + enableFutureTime: false # 允许使用未来时间生成ID,可以使用多少未来时间由 maxBackwardSeconds 控制, 默认: false |
| 173 | + maxBackwardSeconds: 1 # 系统时钟回拨和使用未来时间最长容忍时间(秒), 默认:1 |
| 174 | + CachedUidGenerator: # CachedUidGenerator相关参数 |
| 175 | + boostPower: 3 # RingBuffer size扩容参数, 可提高UID生成的吞吐量, 默认:3 |
| 176 | + paddingFactor: 50 # 指定何时向RingBuffer中填充UID, 取值为百分比(0, 100), 默认为50 |
| 177 | + #scheduleInterval: 60 # 默认:不配置此项, 即不使用Schedule线程定时填充buffer环. 如需使用, 请指定Schedule线程时间间隔, 单位:秒 |
| 178 | +``` |
| 179 | + |
| 180 | +### 使用 |
| 181 | + |
| 182 | +#### |
| 183 | + |
| 184 | +```java |
| 185 | +//实时生成 |
| 186 | +//@Resource |
| 187 | +//private UidGenerator defaultUidGenerator; |
| 188 | + |
| 189 | +//生成一次id之后,按序列号+1生成一批id,缓存,供之后请求 |
| 190 | +@Resource |
| 191 | +private UidGenerator cachedUidGenerator; |
| 192 | + |
| 193 | + |
| 194 | + |
| 195 | +@Test |
| 196 | +public void testSerialGenerate() { |
| 197 | + // Generate UID |
| 198 | + Mono<Long> uid = cachedUidGenerator.getUID(); |
| 199 | + |
| 200 | + // Parse UID into [Timestamp, WorkerId, Sequence] |
| 201 | + // {"UID":"450795408770","timestamp":"2019-02-20 14:55:39","workerId":"27","sequence":"2"} |
| 202 | + System.out.println(cachedUidGenerator.parseUID(uid)); |
| 203 | + |
| 204 | +} |
| 205 | +``` |
| 206 | + |
| 207 | +#### 策略选择 |
| 208 | + |
| 209 | + |
| 210 | +* CachedUidGenerator: |
| 211 | + |
| 212 | + 适合**持续高消耗量**的ID分发,也会保持证生成ID排序准确性,此方式会一定程度上的增加内存和CPU缓存占用。 |
| 213 | + |
| 214 | +* enableFutureTime 为true时的 DefaultUidGenerator: |
| 215 | + |
| 216 | + 适合**偶然突发的消费增加后持续保持低消耗量**的ID分发,此方式在突发高消费时的性能要比CachedUidGenerator还要高,但不应该持续保持高消耗(可消耗的未来时间由maxBackwardSeconds控制,超出后抛出异常),因为过度使用未来时间有可能会造成服务重启后生成重复ID和短时间内的ID排序不精确。 |
| 217 | + |
| 218 | +* enableFutureTime 为false时的 DefaultUidGenerator: |
| 219 | + |
| 220 | + 适合**低消耗量实时**的ID分发,此方式可以保正ID中的时间精确,可满足高精确性的ID排序,一但消耗量高于当前时间的发号量时会返回一个等待响应,等待下一可用时间后发出ID(此方式在消费量大于当前时间可生成ID数量后性能最低),最大等待时间由maxBackwardSeconds控制,超出后抛出异常。 |
| 221 | + |
| 222 | +对于发号性能的测式可使用此 [测试用例](https://github.com/cooperlyt/uid-generator-spring-boot/tree/master/uid-generator-spring-boot-starter/src/test/java/io/github/cooperlyt/cloud/uid) 进行测试,并参见[百度UidGenerator](https://github.com/baidu/uid-generator) |
0 commit comments