feat(ids): 实现基于Snowflake的分布式ID生成功能

新增rs-snowflake依赖并实现分布式ID生成工具
在utils模块中添加ids子模块,提供业务ID生成与解析功能
替换原有UUID生成方式为分布式ID生成器
This commit is contained in:
2025-09-23 00:22:06 +08:00
parent 89baf9a96b
commit cadd336dee
8 changed files with 102 additions and 4 deletions

7
backend/Cargo.lock generated
View File

@ -2552,6 +2552,12 @@ dependencies = [
"cc",
]
[[package]]
name = "rs-snowflake"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e60ef3b82994702bbe4e134d98aadca4b49ed04440148985678d415c68127666"
[[package]]
name = "rsa"
version = "0.9.8"
@ -3911,6 +3917,7 @@ dependencies = [
"reqwest",
"rhai",
"rquickjs",
"rs-snowflake",
"sea-orm",
"serde",
"serde_json",

View File

@ -43,6 +43,8 @@ percent-encoding = "2.3"
rquickjs = "0.9.0"
# 新增: 用于将 mpsc::Receiver 封装为 StreamSSE
tokio-stream = "0.1.17"
# 新增: 分布式ID生成Snowflake
rs-snowflake = "0.6.0"
[dependencies.migration]
path = "migration"

View File

@ -64,6 +64,9 @@ async fn main() -> anyhow::Result<()> {
let redis_pool = redis::init_redis().await?;
redis::set_redis_pool(redis_pool)?;
// 初始化分布式ID生成器读取 ID_MACHINE_ID / ID_NODE_ID
crate::utils::init_from_env();
// run migrations
migration::Migrator::up(&db, None).await.expect("migration up");

View File

@ -44,7 +44,7 @@ pub struct CreateRunLogInput {
pub async fn create(db: &Db, input: CreateRunLogInput) -> anyhow::Result<i64> {
let am = flow_run_log::ActiveModel {
id: Default::default(),
id: Set(crate::utils::generate_flow_run_log_id()),
flow_id: Set(input.flow_id),
flow_code: Set(input.flow_code),
input: Set(input.input),

View File

@ -98,7 +98,7 @@ pub async fn create(db: &Db, req: FlowCreateReq) -> anyhow::Result<FlowDoc> {
let _parsed: FlowDSL = serde_yaml::from_str(yaml).context("invalid flow yaml")?;
info!(target: "udmin", "flow.create: yaml parsed ok");
}
let id = uuid::Uuid::new_v4().to_string();
let id = crate::utils::generate_flow_id();
let name = req
.name
.clone()

View File

@ -17,7 +17,7 @@ pub struct CreateLogInput {
pub async fn create(db: &Db, input: CreateLogInput) -> anyhow::Result<i64> {
let am = request_log::ActiveModel {
id: Default::default(),
id: Set(crate::utils::generate_request_log_id()),
path: Set(input.path),
method: Set(input.method),
request_params: Set(input.request_params),

83
backend/src/utils/ids.rs Normal file
View File

@ -0,0 +1,83 @@
use std::sync::Mutex;
use once_cell::sync::Lazy;
// 采用 rs-snowflake 提供的 SnowflakeIdGenerator
// machine_id 与 node_id 取值范围建议 < 32
use snowflake::SnowflakeIdGenerator;
// 全局生成器(按需初始化),以避免重复构造与时序问题
static GENERATOR: Lazy<Mutex<SnowflakeIdGenerator>> = Lazy::new(|| {
let (machine_id, node_id) = read_ids_from_env();
Mutex::new(SnowflakeIdGenerator::new(machine_id, node_id))
});
fn read_ids_from_env() -> (i32, i32) {
let machine_id = std::env::var("ID_MACHINE_ID").ok().and_then(|s| s.parse::<i32>().ok()).unwrap_or(1);
let node_id = std::env::var("ID_NODE_ID").ok().and_then(|s| s.parse::<i32>().ok()).unwrap_or(1);
// 保护:按 0..32 范围裁剪,避免越界(生成器要求 machine_id/node_id < 32
let clamp = |v: i32| if v < 0 { 0 } else if v > 31 { 31 } else { v };
(clamp(machine_id), clamp(node_id))
}
/// 可选:在启动时主动初始化并打印日志,便于观测
pub fn init_from_env() {
let (machine_id, node_id) = read_ids_from_env();
// 触发 Lazy 初始化(不获取锁,避免非绑定锁 lint
Lazy::force(&GENERATOR);
tracing::info!(target: "udmin", "snowflake init: machine_id={} node_id={}", machine_id, node_id);
}
/// 业务ID生成器按示例主业务16位子业务8位Snowflake保留低39位 => 共63位最高符号位为0
pub struct BizIdConfig {
pub main_id: u16, // 16 bits
pub sub_id: u8, // 8 bits
}
impl BizIdConfig {
pub const fn new(main_id: u16, sub_id: u8) -> Self { Self { main_id, sub_id } }
}
/// 生成带业务前缀的分布式ID返回 i64
pub fn generate_biz_id(cfg: BizIdConfig) -> i64 {
let mut g = GENERATOR.lock().expect("gen snowflake id");
let base_id = g.real_time_generate();
// 只保留低 39 位
let snowflake_bits = base_id & ((1i64 << 39) - 1);
let main_bits = (cfg.main_id as i64) << (39 + 8);
let sub_bits = (cfg.sub_id as i64) << 39;
main_bits | sub_bits | snowflake_bits
}
/// 解析业务ID -> (main_id, sub_id, base_id)
pub fn parse_biz_id(id: i64) -> (u16, u8, i64) {
let main_id = ((id >> (39 + 8)) & 0xFFFF) as u16;
let sub_id = ((id >> 39) & 0xFF) as u8;
let base_id = id & ((1i64 << 39) - 1);
(main_id, sub_id, base_id)
}
// --- 具体业务场景Flow 使用的一组常量(可按需扩展/调整) ---
// 你可以把这些常量提到配置或用枚举维护各业务的 main/sub 编码
const FLOW_MAIN_ID: u16 = 1;
const FLOW_SUB_ID: u8 = 1;
/// 生成 Flow 的 ID返回十进制字符串便于与原先 string 类型主键兼容
pub fn generate_flow_id() -> String {
let id = generate_biz_id(BizIdConfig::new(FLOW_MAIN_ID, FLOW_SUB_ID));
id.to_string()
}
// --- 日志类 ID 的业务位定义与生成 ---
const FLOW_RUN_LOG_MAIN_ID: u16 = 2;
const FLOW_RUN_LOG_SUB_ID: u8 = 1;
pub fn generate_flow_run_log_id() -> i64 {
generate_biz_id(BizIdConfig::new(FLOW_RUN_LOG_MAIN_ID, FLOW_RUN_LOG_SUB_ID))
}
const REQUEST_LOG_MAIN_ID: u16 = 3;
const REQUEST_LOG_SUB_ID: u8 = 1;
pub fn generate_request_log_id() -> i64 {
generate_biz_id(BizIdConfig::new(REQUEST_LOG_MAIN_ID, REQUEST_LOG_SUB_ID))
}

View File

@ -1 +1,4 @@
pub mod password;
pub mod ids;
pub use ids::{init_from_env, generate_biz_id, parse_biz_id, generate_flow_id, BizIdConfig, generate_flow_run_log_id, generate_request_log_id};