107 lines
3.7 KiB
Rust
107 lines
3.7 KiB
Rust
//! 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())
|
||
}
|
||
}
|