From e6447fdd43449990ffe3a890b5e67c15a919410a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Mon, 3 Jul 2023 22:33:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(serial):=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E5=86=B0=E9=9B=B9ID=E7=94=9F=E6=88=90=E5=99=A8?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=EF=BC=8C=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E8=AE=A1=E5=88=92=E4=B8=AD=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + README.md | 2 +- src/serial_code/hail.rs | 121 ++++++++++++++++++++++++++++++++++++++++ src/serial_code/mod.rs | 2 + 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/serial_code/hail.rs diff --git a/Cargo.toml b/Cargo.toml index 7078dd4..8abf500 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ hmac-sha256 = "1.1.7" hmac-sha512 = "1.1.5" image = "0.24.6" md-5 = "0.10.5" +once_cell = "1.18.0" rand = "0.8.5" rsa = { version = "0.9.2", features = ["sha2"] } sha1 = "0.10.5" diff --git a/README.md b/README.md index 1d767b4..306f8b4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Rust 中可以使用的常用辅助功能工具箱。主要配备以下功能: - [x] MD5 散列算法 - [x] 图像感知散列算法 - 唯一序列号生成器 - - [ ] 冰雹 ID 生成器(短主机精简日期版雪花 ID) + - [x] 冰雹 ID 生成器(短主机精简日期版雪花 ID) - [x] UUID 生成器 - [x] short UUID 生成器 - 签名算法 diff --git a/src/serial_code/hail.rs b/src/serial_code/hail.rs new file mode 100644 index 0000000..6fcbb34 --- /dev/null +++ b/src/serial_code/hail.rs @@ -0,0 +1,121 @@ +use core::time; +use std::sync::{Arc, Mutex}; + +use chrono::NaiveDateTime; +use once_cell::sync::{Lazy, OnceCell}; +use thiserror::Error; + +const HAIL_PERIOD_START: Lazy = Lazy::new(|| { + crate::time::date(2022, 2, 22) + .map(|d| d.and_hms_opt(22, 22, 22)) + .flatten() + .map(|dt| crate::time::attach_asia_shanghai(dt)) + .map(|dt| dt.timestamp()) + .unwrap_or_else(|| NaiveDateTime::MIN.timestamp()) +}); +type TimestampValidator = fn(i64) -> bool; +type TimestampGenerator = fn() -> i64; + +static INSTANCE: OnceCell = OnceCell::new(); + +#[derive(Debug, Error)] +pub enum HailSerialCodeAlgorithmError { + #[error("Algorithm is already initialized")] + AlgorithmAlreadyInitialized, +} + +/// 冰雹序列ID算法。 +/// 缩减了时间戳的位数,相比雪花算法可以额外支持近40年。 +pub struct HailSerialCodeAlgorithm { + validator: Option, + generator: Option, + host_id: i64, + last_timestamp: Arc>, + counter: Arc>, +} + +impl HailSerialCodeAlgorithm { + /// 获取一个算法实例用于获取序列ID。 + pub fn get() -> &'static Self { + INSTANCE.get().unwrap() + } + + /// 初始化整个序列ID算法。 + /// ! 注意,如果选择使用内置的主机独立时间戳生成器和验证器,那么将不能保证多主机状态下的序列ID一致性。可能会存在个别主机时间回拨现象。 + /// + /// - `host_id`:主机ID,取值范围为0~65535。 + /// - `timestamp_generator`:时间戳生成器,用于生成时间戳。如果不提供,则使用算法内置的主机独立时间戳生成器。 + /// - `timestamp_validatoe`:时间戳验证器,用于验证时间戳是否有效。如果不提供,则使用算法内置的主机独立时间戳验证器。 + pub fn initialize_algorithm( + host_id: i64, + timestamp_generator: Option, + timestamp_validatoe: Option, + ) -> Result<(), HailSerialCodeAlgorithmError> { + let algorithm = HailSerialCodeAlgorithm { + validator: timestamp_validatoe, + generator: timestamp_generator, + host_id, + last_timestamp: Arc::new(Mutex::new(0)), + counter: Arc::new(Mutex::new(0)), + }; + INSTANCE + .set(algorithm) + .map_err(|_| HailSerialCodeAlgorithmError::AlgorithmAlreadyInitialized) + } + + /// 生成一个自计时起点以来的时间戳。 + fn generate_timestamp(&self) -> i64 { + let current_time = crate::time::now_asia_shanghai().timestamp(); + current_time - *HAIL_PERIOD_START + } + + /// 判断指定时间戳是否比已经存储的最后一次使用的时间戳要大。否则时间发生了回拨。 + fn validate_timestamp(&self, timestamp: i64) -> bool { + let last_timestamp = self.last_timestamp.clone(); + let last_timestamp = last_timestamp.lock().unwrap(); + timestamp >= *last_timestamp + } + + /// 生成一个64位长整型序列ID。 + pub fn generate_serial(&self) -> i64 { + let last_timestamp = self.last_timestamp.clone(); + let mut last_timestamp = last_timestamp.lock().unwrap(); + let counter = self.counter.clone(); + let mut counter = counter.lock().unwrap(); + loop { + let timestamp = if let Some(generator) = self.generator { + generator() + } else { + self.generate_timestamp() + }; + if let Some(validator) = self.validator { + if !validator(timestamp) { + std::thread::sleep(time::Duration::from_secs(1)); + continue; + } + } else if !self.validate_timestamp(timestamp) { + std::thread::sleep(time::Duration::from_secs(1)); + continue; + } + if *last_timestamp < timestamp { + // 对齐时间戳并重置序列计数器 + *last_timestamp = timestamp; + *counter = 0; + } + *counter += 1; + return (timestamp << 20) | ((self.host_id & 0xFFFF) << 16) | (*counter & 0xFFFF_FFFF); + } + } + + /// 生成一个17位长前补零的序列ID字符串。 + pub fn generate_string_serial(&self) -> String { + let serial = self.generate_serial(); + format!("{:017}", serial) + } + + /// 生成一个带字符串前缀17位长前补零的序列ID字符串。 + pub fn generate_prefixed_string_serial(&self, prefix: &str) -> String { + let serial = self.generate_serial(); + format!("{}{:017}", prefix, serial) + } +} diff --git a/src/serial_code/mod.rs b/src/serial_code/mod.rs index 98ae1f0..dceebe2 100644 --- a/src/serial_code/mod.rs +++ b/src/serial_code/mod.rs @@ -1,3 +1,5 @@ +pub mod hail; + pub mod uuid { pub fn new() -> Box { Box::from(uuid::Uuid::new_v4().to_string())