02 — HookPlugin 接入
1. 结论
HookPlugin 是业务系统接入 flare-im-core 主链和旁路事实的标准方式。消息发送权限、内容风控、撤回校验等需要影响主链的逻辑接 Hook;审计、BI、推荐特征、计费等只观察事实的逻辑优先接 Business Probe 或旁路 Hook。
Core 必须保持独立运行:Hook 配置为空时直接跳过;旁路 Hook 默认 fail-open;只有明确影响主链决策的 Hook 才允许 fail-fast。
2. gRPC 契约
- Package:
flare.capability.v1 - Service:
HookPlugin - RPC:
Call(GenericRequest) returns (GenericResponse)
GenericRequest:
| 字段 | 说明 |
|---|---|
operation | 稳定 Hook 操作名,如 flare.hook.v1.pre_send |
payload | google.protobuf.Any,内嵌具体 *HookRequest |
request_id | Hook 本次调用幂等键,建议与 x-request-id 可关联 |
metadata | 静态配置与路由附加 KV |
GenericResponse:
| 字段 | 说明 |
|---|---|
ok | RPC 解码与插件执行是否成功 |
payload | 具体 *HookResponse |
error_code / error_message | 插件内部错误;业务拒绝不要放这里 |
业务拒绝应放在具体 Response 中,例如 PreSendHookResponse.allow = false。
3. Hook 全景
| Hook | operation | 触发点 | 是否可阻断 | 推荐策略 |
|---|---|---|---|---|
| 发前校验 | flare.hook.v1.pre_send | seq 分配和落库前 | 是 | fail_fast,短超时 |
| 发后观测 | flare.hook.v1.post_send | 消息进入主链后 | 否 | ignore / reliable_async |
| 送达观测 | flare.hook.v1.delivery | 下行送达或投递回执后 | 否 | ignore / 采样 |
| 撤回校验/审计 | flare.hook.v1.recall | 撤回命令执行时 | 视策略 | 权限类 fail-fast,审计 fail-open |
| 已读观测 | flare.hook.v1.message_read | 已读水位更新后 | 否 | ignore |
| 消息表态 | flare.hook.v1.message_reaction | 点赞/emoji/业务表态变更 | 可选 | 白名单 fail-fast,统计 fail-open |
| 会话生命周期 | flare.hook.v1.conversation_lifecycle | 创建、更新、归档、解散等 | 否 | ignore / reliable_async |
| 会话成员变更 | flare.hook.v1.conversation_member | 入群、退群、踢人、角色、禁言 | 可选 | 业务投影前可 fail-fast,事实后 fail-open |
| 推送发前 | flare.hook.v1.push_pre_send | 推送任务发送前 | 是 | 模板/通道策略 |
| 推送发后 | flare.hook.v1.push_post_send | 推送任务入队或发送后 | 否 | 统计/计费 |
| 推送送达 | flare.hook.v1.push_delivery | 厂商/长连接送达后 | 否 | 送达率 |
| 在线状态 | flare.hook.v1.presence | presence 变化 | 否 | 风控/在线分析 |
| 自定义扩展 | flare.hook.v1.custom | 业务自定义事件 | 由宿主定义 | 明确 schema |
Rust 公共类型已提供消息和会话核心 Hook trait:
| Rust Trait | 用途 |
|---|---|
PreSendHook | 发信前校验与草稿补充 |
PostSendHook | 发送成功后旁路 |
DeliveryHook | 送达旁路 |
RecallHook | 撤回校验/审计 |
MessageReadHook | 已读观测 |
MessageReactionHook | 消息表态 |
ConversationLifecycleHook | 会话生命周期 |
ConversationMemberHook | 会话成员变更 |
当前 hooks.toml 示例覆盖消息主链的 pre_send/post_send/delivery/recall。message_read、message_reaction、conversation_lifecycle、conversation_member 已作为 Rust trait、registry 执行入口和 proto operation 暴露,具体服务在对应触发点接入 registry 或 HookPlugin 调用即可,不需要业务代码侵入 Core 领域层。
4. 消息 Hook
4.1 pre_send
适合:
- 好友关系、拉黑、陌生人策略。
- 群成员、群禁言、群解散状态。
- 内容安全、敏感词、反垃圾。
- 补充
metadata,例如业务场景、风控标签、审计等级。
禁止:
- 依赖最终
seq。 - 写入消息事实表。
- 长耗时外呼。
拒绝约定:
message PreSendHookResponse {
bool allow = 1;
HookMessageDraft draft = 2;
string deny_reason_code = 3;
string deny_reason_message = 4;
}
allow = false 是业务拒绝;GenericResponse.ok = false 是插件执行失败。
4.2 post_send
适合:
- 审计、归档、BI。
- 推荐/增长特征。
- 计费明细。
- 发信成功后的业务通知。
禁止:
- 再决定是否允许发送。
- 反向修改消息内容和 seq。
幂等键建议:message_id + operation。
4.3 delivery
适合:
- websocket / push / apns / fcm 通道效果分析。
- 端类型、网络质量、区域质量统计。
- 大促或运营消息送达监控。
该 Hook 高 QPS,建议采样、批量、异步写入。
4.4 recall
适合:
- 管理员撤回、群主撤回、时间窗口校验。
- 撤回审计和合规留痕。
权限类可以 fail-fast;审计类应 fail-open。
4.5 message_read
适合:
- 已读率、未读收敛分析。
- 重要消息阅读审计。
- 群公告/工单已读统计。
禁止:
- 修改用户游标。
- 重新计算未读权威状态。
4.6 message_reaction
适合:
- emoji / 点赞 / 业务表态白名单。
- 互动统计、推荐特征。
- 禁止对特定消息类型表态。
如果只是统计,使用 fail-open;如果要拒绝非法 reaction,使用 fail-fast 并返回结构化拒绝码。
5. 会话 Hook
5.1 conversation_lifecycle
触发:
- 会话创建/ensure。
- 名称、头像、公告、置顶、免打扰等资料变更。
- 归档、解散、恢复、冻结。
适合:
- 同步业务目录。
- 触发系统消息。
- 审计群生命周期。
- 对外部 CRM / 工单 / 项目空间做投影。
原则:会话生命周期事实应先由 Core 或业务权威方提交,再通过 Hook / Probe 通知外部系统;旁路失败不能回滚已提交事实。
5.2 conversation_member
触发:
- 邀请、入群、退群、踢人。
- 角色变更。
- 禁言/解除禁言。
- 成员黑名单/访问控制变化。
适合:
- 成员变更审计。
participant_version变化通知。- 业务侧成员画像、推荐、风控特征。
- 外部群目录投影。
如果成员变更由 Social 发起,Social 是业务权威;Core 只投影参与者和 participant_version。如果成员变更由 Core 管理 API 发起,业务 Hook 可作为策略门禁或旁路通知。
6. Context 与安全
Hook 调用必须同时依赖 gRPC metadata 和 HookInvocationContext:
| 字段 | 位置 | 说明 |
|---|---|---|
x-trace-id | gRPC metadata | 链路追踪 |
x-request-id | gRPC metadata | 请求级幂等,不等于 message_id |
x-tenant-id | gRPC metadata | 租户隔离,优先级高于 body |
x-user-id | gRPC metadata | 可信操作用户 |
request_id | GenericRequest | Hook 调用幂等 |
operation | GenericRequest | Hook 类型 |
payload.type_url | Any | 具体请求类型 |
安全规则:
- body 中的
operator_user_id只能作为业务上下文,不能替代认证。 tenant_id冲突时以 metadata / token claims 为准,并记录告警。- 所有可重试 Hook 必须支持幂等。
- Hook 不应保存明文 token、密钥、完整设备指纹等敏感数据。
7. 配置建议
消息主链基础配置见 flare-im-core/config/hooks.business.example.toml。
Social 发信权限配置见 flare-im-core/config/hooks.social.example.toml。
推荐优先级:
| 优先级 | Hook |
|---|---|
>= 100 | 强门禁:关系、群权限、内容风控 |
50-99 | 可选门禁:reaction 白名单、撤回权限补充 |
< 50 | 旁路:审计、BI、推荐、计费 |
推荐超时:
| Hook | 超时 |
|---|---|
pre_send | 100-500ms |
recall | 100-500ms |
post_send | 300-1000ms |
delivery / message_read | 50-300ms |
conversation_lifecycle / conversation_member | 300-1000ms |
8. 实现 Checklist
- 实现
HookPlugin.Call。 - 按
operation分发到具体 handler。 - 解码
Any为具体*HookRequest。 - 从 metadata 还原可信
tenant_id/user_id/request_id/trace_id。 - 按业务语义返回具体
*HookResponse。 - 对
request_id + operation或message_id + operation做幂等。 - 结构化记录拒绝码、耗时、超时、重试次数。
- 注册服务发现名或配置直连 endpoint。
参考实现:flare-social/flare-social-hook。
9. Hook 与 Business Probe 的选择
| 需求 | 推荐 |
|---|---|
| 要拒绝发送 | pre_send |
| 要拒绝非法 reaction | message_reaction |
| 要补充撤回权限 | recall |
| 只做审计/BI/计费 | Business Probe 或 post_send |
| 只观察成员变化 | Business Probe 或 conversation_member |
| 需要调用业务能力并返回结果 | CapabilityService.Dispatch |
详细埋点模型见 10-business-instrumentation.md。