Files
chongming-worker-rs/src/lib.rs
T
2026-08-31 17:18:03 +08:00

107 lines
3.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! chongming-worker-rs —— 进程插件 Rust SDK
//!
//! 语言无关线协议(docs/plugin-wire-protocol.md)的 Rust 实现:
//! 1. 解析 config.toml 与端点 TOML,构造路由描述符并发布到 `chongming/routes/**`
//! 2. liveliness token 注册 `worker/<type>/<id>`(服务发现)
//! 3. Queryable 声明 `worker/<type>/<id>/<subject>`,解析请求信封并分派 handler
//! 4. 每 10s 重发布描述符(自愈),SIGTERM/SIGINT 优雅退出时 DELETE 摘除
//!
//! 与 Python SDK(utils/worker/)行为等价,可同 type 混布参与同一套负载均衡。
pub mod admin;
pub mod cache;
pub mod config;
#[cfg(feature = "database")]
pub mod database;
pub mod descriptor;
pub mod envelope;
pub mod error;
pub mod gen;
pub mod publisher;
pub mod runtime;
pub mod serve;
pub use runtime::WorkerPool;
pub use serve::{serve, ServeOptions};
/// 常用类型统一导出
pub mod prelude {
pub use crate::envelope::Request;
pub use crate::error::{error_response, stream_error_block};
pub use crate::publisher::{announce_loop, RoutePublisher, ANNOUNCE_INTERVAL_SECS};
#[cfg(feature = "database")]
pub use crate::database::Database;
pub use crate::{HandlerRegistry, Reply};
}
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use futures_util::stream::BoxStream;
use serde_json::Value;
pub use envelope::Request;
/// 异步函数指针(handler 与 announce 循环等共用)
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
/// 端点响应:单块 JSON,或流式多块(SSE 逐块转发)
pub enum Reply {
/// 普通响应:单个回复,载荷为业务结果 JSON 对象
Json(Value),
/// 流式响应:逐块多回复;每块 `Ok(v)` 作为独立回复块,
/// `Err(msg)` 以 `{"error": msg}` 收尾块结束流(与 Python SDK 一致)
Stream(BoxStream<'static, Result<Value, String>>),
}
/// 端点 handler:解析后的请求信封 → 业务结果
pub type Handler = dyn Fn(Request) -> BoxFuture<Result<Reply, String>> + Send + Sync;
/// RPC 端点注册表(按 subject + HTTP 方法分派)
#[derive(Default)]
pub struct HandlerRegistry {
/// subject -> [(method, handler)](同一 subject 可挂多个方法)
map: HashMap<String, Vec<(String, Arc<Handler>)>>,
}
impl HandlerRegistry {
pub fn new() -> Self {
Self::default()
}
/// 注册端点。`f` 为 async handler:`fn(Request) -> Result<Reply, String>`。
pub fn register<F, Fut>(&mut self, subject: &str, method: &str, f: F)
where
F: Fn(Request) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<Reply, String>> + Send + 'static,
{
let entry = self
.map
.entry(subject.to_string())
.or_default();
entry.push((
method.to_uppercase(),
Arc::new(move |req: Request| {
Box::pin(f(req)) as Pin<Box<dyn Future<Output = Result<Reply, String>> + Send>>
}) as Arc<Handler>,
));
}
/// 该 subject+method 是否有 handler(发布前的契约自检用)
pub fn has(&self, subject: &str, method: &str) -> bool {
self.lookup(subject, method).is_some()
}
/// 按 subject+method 查 handler;无精确匹配时降级到该 subject 的第一个方法
/// (与 Python runtime.py 的匹配逻辑一致)。
pub fn lookup(&self, subject: &str, method: &str) -> Option<Arc<Handler>> {
let methods = self.map.get(subject)?;
if let Some((_, h)) = methods.iter().find(|(m, _)| m == &method.to_uppercase()) {
return Some(h.clone());
}
methods.first().map(|(_, h)| h.clone())
}
}