monotonicUlid()// 01BX5ZZKBKACTAV9WEVGEMMVRZmonotonicUlid()// 01BX5ZZKBKACTAV9WEVGEMMVS0
溢出错误处理从技术实现上来看 , 26
个字符的Base32
编码字符串可以包含130 bit
信息,而ULID
只包含128 bit
信息,所以该编码算法是能完全满足ULID
的需要 。基于Base32
编码能够生成的最大的合法ULID
其实就是7ZZZZZZZZZZZZZZZZZZZZZZZZZ
,并且使用的时间戳为epoch time
的281474976710655
或者说2 ^ 48 - 1
。对于任何对大于此值的ULID
进行解码或编码的尝试都应该被所有实现拒绝,以防止溢出错误 。
二进制布局二进制布局的多个部分被编码为16 byte
,每个部分都以最高字节优先(网络字节序,也就是big-endian
)进行编码,布局如下:
0123 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|32_bit_uint_time_high|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|16_bit_uint_time_low|16_bit_uint_random|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|32_bit_uint_random|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|32_bit_uint_random|+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ULID使用对于script
标签引用:
<script src="http://img.zhejianglong.com/231018/1443595207-2.jpg"></script><script>ULID.ulid()</script>
NPM
安装:
npm install --save ulid
TypeScript
, ES6+
, Babel
, Webpack
, Rollup
等等下使用:
// importimport { ulid } from 'ulid'ulid()// CommonJS envconst ULID = require('ulid')ULID.ulid()
后端Maven
项目中使用需要引入依赖,这里选用ulid-creator
实现:
<dependency><groupId>com.github.f4b6a3</groupId><artifactId>ulid-creator</artifactId><version>5.0.2</version></dependency>
然后调用UlidCreator#getUlid()
系列方法:
// 常规Ulid ulid = UlidCreator.getUlid();// 单调排序Ulid ulid = UlidCreator.getMonotonicUlid();
实现ULID前面已经提到ULID
的规范,其实具体实现ULID
就是对着规范里面的每一个小节进行编码实现 。先看二进制布局,由于使用128 bit
去存储,可以借鉴UUID
那样,使用两个long
类似的成员变量存储ULID
的信息,看起来像这样:
public final class ULID {/** The most significant 64 bits of this ULID.**/private final long msb;/** The least significant 64 bits of this ULID.**/private final long lsb;public ULID(long msb, long lsb) {this.msb = msb;this.lsb = lsb;}}
按照ULID
的组成来看,可以提供一个入参为时间戳和随机数字节数组的构造:
public ULID(long timestamp, byte[] randomness) {if ((timestamp & TIMESTAMP_MASK) != 0) {throw new IllegalArgumentException("Invalid timestamp");}if (Objects.isNull(randomness) || RANDOMNESS_BYTE_LEN != randomness.length) {throw new IllegalArgumentException("Invalid randomness");}long msb = 0;long lsb = 0;// 时间戳左移16位,低位补零准备填入部分随机数位,即16_bit_uint_randommsb |= timestamp << 16;// randomness[0]左移0位填充到16_bit_uint_random的高8位,randomness[1]填充到16_bit_uint_random的低8位msb |= (long) (randomness[0x0] & 0xff) << 8;// randomness[1]填充到16_bit_uint_random的低8位msb |= randomness[0x1] & 0xff;// randomness[2] ~ randomness[9]填充到剩余的bit_uint_random中,要左移相应的位lsb |= (long) (randomness[0x2] & 0xff) << 56;lsb |= (long) (randomness[0x3] & 0xff) << 48;lsb |= (long) (randomness[0x4] & 0xff) << 40;lsb |= (long) (randomness[0x5] & 0xff) << 32;lsb |= (long) (randomness[0x6] & 0xff) << 24;lsb |= (long) (randomness[0x7] & 0xff) << 16;lsb |= (long) (randomness[0x8] & 0xff) << 8;lsb |= (randomness[0x9] & 0xff);this.msb = msb;this.lsb = lsb;}
这是完全按照规范的二进制布局编写代码 , 可以像UUID
的构造那样精简一下:
long msb = 0;long lsb = 0;byte[] data = https://www.huyubaike.com/biancheng/new byte[16];byte[] ts = ByteBuffer.allocate(8).putLong(0, timestamp << 16).array();System.arraycopy(ts, 0, data, 0, 6);System.arraycopy(randomness, 0, data, 6, 10);for (int i = 0; i < 8; i++)msb = (msb << 8) | (data[i] & 0xff);for (int i = 8; i < 16; i++)lsb = (lsb << 8) | (data[i] & 0xff);
接着可以简单添加下面几个方法:
public long getMostSignificantBits() {return this.msb;}public long getLeastSignificantBits() {return this.lsb;}// 静态工厂方法,由UUID实例生成ULID实例public static ULID fromUUID(UUID uuid) {return new ULID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());}// 实例方法,当前ULID实例转换为UUID实例public UUID toUUID() {return new UUID(this.msb, this.lsb);}
推荐阅读
- vite vue3 规范化与Git Hooks
- 钩子 【pytest官方文档】解读-插件开发之hooks 函数
- 中 ?打造企业自己代码规范IDEA插件
- HashMap底层原理及jdk1.8源码解读
- JS 模块化-05 ES Module & 4 大规范总结
- 上 ?打造企业自己代码规范IDEA插件
- Go 源码解读|如何用好 errors 库的 errors.Is 与 errors.As() 方法
- 高二化学重要知识难点解读
- 2022年装修全流程保姆级解读 85平米的房子装修需要多少钱
- 国家安全用电规范 国家安全用电常识