Skip to main content

ek_core/
context.rs

1//! `PluginContext` —— 插件的唯一出口(put / ask / notify / 发现四问)。
2//!
3//! 纪律(P-H2 审核修订):不暴露 event_engine() 访问器——插件不许自装 handler
4//! 绕过 worker/熔断/背压;收信口(on_mail)与发信口(ctx)各只有一个。
5//! 0.3.0:宿主零日志 —— write_log 出口移除,业务日志由插件自造自投
6//! (`ctx.put` 自己的载荷,业务链上用 ek-data 的 LogData 到 "eLog" 键)。
7//!
8//! 0.2.0 新增(宿主最小知识原则):plugins / services_of / find_service /
9//! ask_service / publish_services —— 宿主只持【纯路由索引】,
10//! 方法细节住回插件本身,按需现场取(memoize 仅为性能缓存)。
11
12use crate::envelope::{Envelope, OneshotErr, ReplyErr, e_req_type, e_to_type};
13use crate::error::AskError;
14use crate::lifecycle::{EVENT_SRV_CHG, EVENT_SRV_CHG_PREFIX};
15use crate::main_engine::{PluginInfo, PluginState};
16use crate::registry::Registry;
17use crate::service::{ProviderRef, ServiceDesc};
18use ek_event::{Event, EventEngine, PutError};
19use std::any::{Any, type_name};
20use std::sync::Arc;
21use std::time::Duration;
22
23/// 发放到插件的上下文(on_init 传入;`Clone` 供插件自存)。
24#[derive(Clone)]
25pub struct PluginContext {
26    pub(crate) event: Arc<EventEngine>,
27    pub(crate) registry: Arc<Registry>,
28    pub(crate) name: String,
29}
30
31impl PluginContext {
32    pub(crate) fn new(event: Arc<EventEngine>, registry: Arc<Registry>, name: String) -> Self {
33        Self {
34            event,
35            registry,
36            name,
37        }
38    }
39
40    /// 本插件注册名。
41    pub fn name(&self) -> &str {
42        &self.name
43    }
44
45    /// 广播出口:插件自发事件(业务 broadcast / 业务日志 / eTo 均经此)。
46    ///
47    /// 日志纪律(0.3.0):宿主不提供日志载荷类型;要写业务日志,插件自造
48    /// 载荷自理(业务链上用 ek-data 的 LogData 到 "eLog" 键后 put)。
49    pub fn put(&self, event: Event) -> Result<(), PutError> {
50        self.event.put(event)
51    }
52
53    /// 定向通知出口:eTo.<target>,无回包;目标不在线时事件被静默分发为空(尽力而为)。
54    pub fn notify(&self, target: &str, payload: Arc<dyn Any + Send + Sync>) {
55        let _ = self.event.put(Event::with_data(e_to_type(target), payload));
56    }
57
58    // ================ 服务发现四问(0.2.0) ================
59
60    /// 枚举宿主内全部插件(含 Ready;快照含版本)。
61    pub fn plugins(&self) -> Vec<PluginInfo> {
62        let mut out = Vec::new();
63        for name in self.registry.names() {
64            if let Some(info) = self.registry.info_of(&name) {
65                out.push(info);
66            }
67        }
68        out
69    }
70
71    /// 某插件自报的完整服务目录(现场取 + 世代核对 + memoize)。
72    /// Ready 未 Running 亦可查(声明在注册时即静态可用)。
73    /// 该插件不存在 → None。
74    pub fn services_of(&self, name: &str) -> Option<Arc<Vec<ServiceDesc>>> {
75        self.registry.services_of(name)
76    }
77
78    /// 按【服务 id】全宿主查找提供方(有序:注册序;含非 Running 状态 → 供消费方自裁)。
79    /// A 不需要预先知道提供方的注册名。
80    pub fn find_service(&self, svc: &str) -> Vec<ProviderRef> {
81        let mut out = Vec::new();
82        for name in self.registry.index_providers(svc) {
83            let Some(list) = self.registry.services_of(&name) else {
84                continue;
85            };
86            let Some(entry) = self.registry.get(&name) else {
87                continue;
88            };
89            if let Some(desc) = list.iter().find(|d| d.id == svc) {
90                out.push(ProviderRef {
91                    name: entry.name.clone(),
92                    version: entry.plugin.version().into_owned(),
93                    state: entry.state.get(),
94                    service: desc.clone(),
95                });
96            }
97        }
98        out
99    }
100
101    /// 运行期重新申报自己的服务目录:宿主重织索引、作废缓存,双发 `eSrvChg.<名>`(版本串)
102    /// 与 `eSrvChg`(固定键,PlugNotice)。
103    ///
104    /// ⚠ 纪律(DV4 教训):**先换插件 services() 当前返回,再调用本方法** ——
105    /// 宿主细节是现场取的,只申报不改内容会造成索引与 live 细节脱节。
106    ///
107    /// (0.2.0:Rust 无运行期代码热换;「更新」的运行期形态 = 目录随配置/状态改版。)
108    pub fn publish_services(&self, list: Vec<ServiceDesc>) {
109        let name = self.name.clone();
110        let version = self.registry.publish(&name, list);
111        let _ = self.event.put(Event::with_data(
112            format!("{EVENT_SRV_CHG_PREFIX}{name}"),
113            Arc::new(version.clone()),
114        ));
115        let _ = self.event.put(Event::with_data(
116            EVENT_SRV_CHG,
117            Arc::new(crate::service::PlugNotice { name, version }),
118        ));
119    }
120
121    // ================ 请求应答 ================
122
123    /// 请求出口:eReq.<target> + Envelope + std mpsc 一次性信道。
124    ///
125    /// **禁止自呼**:ask 指向自身 target 会占满自己的 worker 直到超时
126    /// (自己阻塞等自己的回包)——需要自身逻辑请直调。
127    ///
128    /// 时序:预检目标 Running → put → recv_timeout。
129    /// - 预检失败(不存在/未 Running/已 Quarantined)→ 立即 `Err(TargetGone)`;
130    /// - 回包 `Ok(Res)` 且契约类型匹配 → `Ok(Res)`;
131    /// - 回包 `Err(InvalidPayload)` **或** 回包类型与本侧契约不一致 → `Err(InvalidPayload)`;
132    /// - 信道断连(handler panic/信封丢失/目标邮箱溢出被丢)→ `Err(TargetGone)`(尽快,不虚耗超时);
133    /// - 等满时限 → `Err(Timeout)`;
134    /// - put 失败(总线已停)→ `Err(BusStopped)`。
135    pub fn ask<Req, Res>(&self, target: &str, req: Req, timeout: Duration) -> Result<Res, AskError>
136    where
137        Req: Any + Send + Sync,
138        Res: Any + Send + Sync,
139    {
140        match self.registry.get(target) {
141            Some(entry) if entry.state.get() == PluginState::Running => {}
142            _ => {
143                return Err(AskError::TargetGone {
144                    target: target.to_string(),
145                });
146            }
147        }
148        self.deliver(target, req, timeout)
149    }
150
151    /// 按服务 id 的请求出口(0.2.0):index 命中 → 现场类型闸门 → 发 eReq。
152    ///
153    /// - 无人提供该服务 → 立即 `Err(UnknownService)`(零成本显错);
154    /// - 有提供方但全都非 Running → 立即 `Err(TargetGone)`;
155    /// - Req 类型与服务声明的 req_type 不符 → 立即 `Err(InvalidPayload)`
156    ///   (把「绕一圈才发现错型」提前到发出之前);
157    /// - 多提供方 → 取注册序第一个 Running 者;后续 failover 由发起方在收不到回包后自定。
158    ///   其余失败面与 [`ask`](Self::ask) 完全一致。
159    pub fn ask_service<Req, Res>(
160        &self,
161        svc: &str,
162        req: Req,
163        timeout: Duration,
164    ) -> Result<Res, AskError>
165    where
166        Req: Any + Send + Sync,
167        Res: Any + Send + Sync,
168    {
169        let providers = self.find_service(svc);
170        if providers.is_empty() {
171            return Err(AskError::UnknownService {
172                service: svc.to_string(),
173            });
174        }
175        let Some(slot) = providers.iter().find(|p| p.state == PluginState::Running) else {
176            return Err(AskError::TargetGone {
177                target: providers[0].name.clone(),
178            });
179        };
180        if slot.service.req_type != type_name::<Req>() {
181            return Err(AskError::InvalidPayload {
182                target: slot.name.clone(),
183            });
184        }
185        self.deliver(&slot.name.clone(), req, timeout)
186    }
187
188    /// ask/ask_service 共用的投递段(Envelope + eReq + recv_timeout + 回包裁决)。
189    fn deliver<Req, Res>(&self, target: &str, req: Req, timeout: Duration) -> Result<Res, AskError>
190    where
191        Req: Any + Send + Sync,
192        Res: Any + Send + Sync,
193    {
194        let (envelope, reply_rx) = Envelope::new(Arc::new(req));
195        let put = self
196            .event
197            .put(Event::with_data(e_req_type(target), Arc::new(envelope)));
198        if put.is_err() {
199            return Err(AskError::BusStopped);
200        }
201        let boxed = reply_rx.recv_timeout(timeout).map_err(|e| match e {
202            OneshotErr::Timeout => AskError::Timeout {
203                target: target.to_string(),
204                // 读数:超时那一刻目标邮箱里排了多少帧(目标已摘 → 0)
205                pending: self
206                    .registry
207                    .get(target)
208                    .map(|e| e.stats.pending.load(std::sync::atomic::Ordering::Acquire))
209                    .unwrap_or(0),
210            },
211            OneshotErr::Closed => AskError::TargetGone {
212                target: target.to_string(),
213            },
214        })?;
215        // 回包为 Box<dyn Any>:downcast 到本侧契约 Result<Res, ReplyErr>
216        match boxed.downcast::<Result<Res, ReplyErr>>() {
217            Ok(inner) => match *inner {
218                Ok(res) => Ok(res),
219                Err(_) => Err(AskError::InvalidPayload {
220                    target: target.to_string(),
221                }),
222            },
223            // 回包类型与本侧契约不同(双方契约错配)→ 协议违约
224            Err(_) => Err(AskError::InvalidPayload {
225                target: target.to_string(),
226            }),
227        }
228    }
229}