1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
use rand::Rng;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::SystemTime;
use uuid::Uuid;
// 数据中心ID和机器ID
const DATA_CENTER_ID: u16 = 0;
const MACHINE_ID: u16 = 0;
// 时间戳左移22位 (10 + 12)
const TIMESTAMP_LEFT_SHIFT: u64 = 22;
// 数据中心ID左移17位 (5 + 12)
const DATA_CENTER_ID_SHIFT: u64 = 17;
// 机器ID左移12位
const MACHINE_ID_SHIFT: u64 = 12;
// 序列号掩码,12位全为1
const SEQUENCE_MASK: u64 = 0xfff;
// 上次生成ID的时间戳
static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
// 序列号
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
/// ID tool, used for generating IDs, including UUID and snowflake algorithm
pub struct IdUtil;
impl IdUtil {
/// 生成UUID
pub fn gen_uuid() -> String {
Uuid::new_v4().to_string()
}
/// 生成UUID,不带中横线,共计32位
pub fn uuid32() -> String {
Uuid::new_v4().to_string().replace("-", "")
}
/// 生成UUID,带中横线,共计36位
pub fn uuid36() -> String {
Uuid::new_v4().to_string()
}
/// 生成指定长度的随机字符串ID
///
/// 该函数使用字母和数字字符集生成随机字符串,适用于生成唯一标识符
///
/// # 参数
/// * `length` - 指定生成字符串的长度
///
/// # 返回值
/// 返回一个包含随机字符的String,长度为指定的length
///
/// # 示例
/// ```
/// let id = random_string_id(128);
/// assert_eq!(id.len(), 128);
/// ```
pub fn random_string_id(length: usize) -> String {
let str_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let mut rng = rand::thread_rng();
let mut result = String::with_capacity(length);
// 循环生成指定长度的随机字符串
for _ in 0..length {
let index = rng.gen_range(0..str_chars.len());
result.push(str_chars.chars().nth(index).unwrap());
}
result
}
/// 生成一个唯一的 tik ID 字符串
///
/// 该函数通过组合三个随机字符串来创建一个格式化的唯一标识符。
/// 生成的格式为: "{2位随机字符串}_{14位随机字符串}_{16位随机字符串}__"
///
/// # 返回值
/// * `String` - 返回生成的唯一 tik ID 字符串
///
/// # 示例
/// ```
/// let id = tik_id();
/// // 示例输出: "ab_cdefghijklmn_opqrstuvwxyzabcdefg__"
/// ```
pub fn tik_id() -> String {
format!(
"{}_{}_{}__",
Self::random_string_id(2),
Self::random_string_id(14),
Self::random_string_id(16)
)
}
/// 使用雪花算法生成ID
///
/// 1位0 + 41位时间戳 + 10位机器ID(5位数据中心ID + 5位机器ID) + 12位序列号 = 64位二进制数字
pub fn snowflake_id() -> u64 {
let mut timestamp = Self::get_current_timestamp();
// 如果当前时间小于上一次生成ID的时间戳,说明系统时钟回退过,抛出异常
let last_timestamp = LAST_TIMESTAMP.load(Ordering::Relaxed);
if timestamp < last_timestamp {
panic!(
"Clock moved backwards. Refusing to generate id for {} milliseconds",
last_timestamp - timestamp
);
}
// 如果是同一毫秒内生成的ID,则序列号递增
if timestamp == last_timestamp {
let sequence = SEQUENCE.fetch_add(1, Ordering::Relaxed) & SEQUENCE_MASK;
// 如果序列号溢出,等待下一毫秒
if sequence == 0 {
timestamp = Self::til_next_millis(last_timestamp);
}
} else {
// 不同毫秒内,序列号重置为0
SEQUENCE.store(0, Ordering::Relaxed);
}
// 记录上一次生成ID的时间戳
LAST_TIMESTAMP.store(timestamp, Ordering::Relaxed);
// 生成雪花ID
(timestamp << TIMESTAMP_LEFT_SHIFT)
| ((DATA_CENTER_ID as u64) << DATA_CENTER_ID_SHIFT)
| ((MACHINE_ID as u64) << MACHINE_ID_SHIFT)
| SEQUENCE.load(Ordering::Relaxed)
}
fn get_current_timestamp() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Time went backwards")
.as_millis() as u64
}
fn til_next_millis(last_timestamp: u64) -> u64 {
let mut timestamp = Self::get_current_timestamp();
while timestamp <= last_timestamp {
timestamp = Self::get_current_timestamp();
}
timestamp
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uuid() {
let uuid = IdUtil::uuid32();
println!("{}", uuid);
let uuid = IdUtil::uuid36();
println!("{}", uuid);
}
#[test]
fn test_random_string_id() {
let id = IdUtil::random_string_id(128);
println!("{}", id)
}
#[test]
fn test_snowflake_id() {
let id = IdUtil::snowflake_id();
println!("{}", id);
println!("{:b}", id);
}
}