feat(ids): 实现基于Snowflake的分布式ID生成功能
新增rs-snowflake依赖并实现分布式ID生成工具 在utils模块中添加ids子模块,提供业务ID生成与解析功能 替换原有UUID生成方式为分布式ID生成器
This commit is contained in:
7
backend/Cargo.lock
generated
7
backend/Cargo.lock
generated
@ -2552,6 +2552,12 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rs-snowflake"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e60ef3b82994702bbe4e134d98aadca4b49ed04440148985678d415c68127666"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
@ -3911,6 +3917,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"rhai",
|
"rhai",
|
||||||
"rquickjs",
|
"rquickjs",
|
||||||
|
"rs-snowflake",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@ -43,6 +43,8 @@ percent-encoding = "2.3"
|
|||||||
rquickjs = "0.9.0"
|
rquickjs = "0.9.0"
|
||||||
# 新增: 用于将 mpsc::Receiver 封装为 Stream(SSE)
|
# 新增: 用于将 mpsc::Receiver 封装为 Stream(SSE)
|
||||||
tokio-stream = "0.1.17"
|
tokio-stream = "0.1.17"
|
||||||
|
# 新增: 分布式ID生成(Snowflake)
|
||||||
|
rs-snowflake = "0.6.0"
|
||||||
|
|
||||||
[dependencies.migration]
|
[dependencies.migration]
|
||||||
path = "migration"
|
path = "migration"
|
||||||
|
|||||||
@ -64,6 +64,9 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
let redis_pool = redis::init_redis().await?;
|
let redis_pool = redis::init_redis().await?;
|
||||||
redis::set_redis_pool(redis_pool)?;
|
redis::set_redis_pool(redis_pool)?;
|
||||||
|
|
||||||
|
// 初始化分布式ID生成器(读取 ID_MACHINE_ID / ID_NODE_ID)
|
||||||
|
crate::utils::init_from_env();
|
||||||
|
|
||||||
// run migrations
|
// run migrations
|
||||||
migration::Migrator::up(&db, None).await.expect("migration up");
|
migration::Migrator::up(&db, None).await.expect("migration up");
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ pub struct CreateRunLogInput {
|
|||||||
|
|
||||||
pub async fn create(db: &Db, input: CreateRunLogInput) -> anyhow::Result<i64> {
|
pub async fn create(db: &Db, input: CreateRunLogInput) -> anyhow::Result<i64> {
|
||||||
let am = flow_run_log::ActiveModel {
|
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_id: Set(input.flow_id),
|
||||||
flow_code: Set(input.flow_code),
|
flow_code: Set(input.flow_code),
|
||||||
input: Set(input.input),
|
input: Set(input.input),
|
||||||
|
|||||||
@ -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")?;
|
let _parsed: FlowDSL = serde_yaml::from_str(yaml).context("invalid flow yaml")?;
|
||||||
info!(target: "udmin", "flow.create: yaml parsed ok");
|
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
|
let name = req
|
||||||
.name
|
.name
|
||||||
.clone()
|
.clone()
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pub struct CreateLogInput {
|
|||||||
|
|
||||||
pub async fn create(db: &Db, input: CreateLogInput) -> anyhow::Result<i64> {
|
pub async fn create(db: &Db, input: CreateLogInput) -> anyhow::Result<i64> {
|
||||||
let am = request_log::ActiveModel {
|
let am = request_log::ActiveModel {
|
||||||
id: Default::default(),
|
id: Set(crate::utils::generate_request_log_id()),
|
||||||
path: Set(input.path),
|
path: Set(input.path),
|
||||||
method: Set(input.method),
|
method: Set(input.method),
|
||||||
request_params: Set(input.request_params),
|
request_params: Set(input.request_params),
|
||||||
|
|||||||
83
backend/src/utils/ids.rs
Normal file
83
backend/src/utils/ids.rs
Normal 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))
|
||||||
|
}
|
||||||
@ -1 +1,4 @@
|
|||||||
pub mod password;
|
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};
|
||||||
Reference in New Issue
Block a user