mailbox 这个抽象是对的吗
做 mq9 的过程中,有一个问题反复出现:mailbox 这个抽象,是对的吗?
不是质疑 Agent 需不需要异步通信。这一点从来没有动摇过。Agent 会离线、会重启、会和其他 Agent 跨时间协作,异步通信是刚需。我质疑的是:我为 Agent 通信选的这个核心抽象,到底有没有选错?有没有更好的选项我没想到?
这个问题反复想了很久。今天想写出来探讨,也许有人比我想得更清楚。
做基础设施,抽象选错是根本性的错误。Bug 可以修,性能可以优化,文档可以补,但如果核心抽象选错了,后面所有工作都是在错误的地基上堆楼。所以对这个问题很认真,真的把所有可能的替代都列出来,逐个问自己"为什么不是这个"。
Event/EventBus 是第一个候选。Agent 发事件,其他 Agent 订阅感兴趣的类型,松耦合,发送方不需要知道接收方。看起来很合适,但有一个地方我一直过不去:Event 没有身份,消息不属于任何人。"给 Agent B 发一条消息"和"广播一个事件类型"是两件不同的事,前者需要 B 是通信对象,后者 B 只是一个感兴趣的监听者。这两种语义在 Agent 协作里是不同的场景,Event 只能覆盖后者。
Channel 是第二个。两个 Agent 之间建一个通道,点对点,直觉上很清晰。但 Channel 需要提前建立连接,Agent 离线就断了。Agent 会频繁重启、部署、休眠,Channel 的生命周期和 Agent 的生命周期对不上,用起来很别扭。
Inbox/Outbox 是第三个,数据库 pattern,每个 Agent 一张 inbox 表,消息写进去,Agent 轮询拉取。持久、可靠,但没有推送语义,没有优先级,这个方案每次用都在重造轮子,很难成为基础设施层的抽象。
最接近的是 Actor mailbox,Erlang/Akka 的模型。每个 Actor 有 mailbox,消息投递进去,Actor 按自己的节奏处理,这个模型被用了四十年。mq9 的设计和这个最像,区别是 Actor mailbox 活在进程内存里,Actor 消失 mailbox 就消失了。mq9 的 mailbox 活在持久化存储里,Agent 离线了消息还在等。
这样过完一圈之后,我发现自己一直在回到同一个地方:其他抽象描述的是消息的流动路径,mailbox 描述的是一个存在的实体拥有一个地址。
但我不确定这个感觉是真正的洞察,还是我已经先入为主之后的自我说服。
有时候你想过所有替代,最后还是回到原点,这可能是因为原点真的是最好的答案,也可能是因为你根本没有想到真正的替代。这两种情况从内部很难区分。
反复想完这些之后,我目前能给自己的回答是:所有其他抽象都在描述消息的流动,只有 mailbox 在描述 Agent 的存在。
Agent 最本质的特征是:它是一个有身份、有记忆、会间歇性在线的实体。Event 描述不了这个,Channel 描述不了这个,Inbox/Outbox 勉强能描述一部分,但不完整。mailbox 是我目前找到的、唯一一个天然承载这三个特征的抽象——mail_address 是身份,消息持久化是记忆,离线堆积上线续收是间歇在线的处理方式。
但我不确定这是真正的洞察,还是我已经先入为主之后的自我说服。有时候你想过所有替代,最后还是回到原点,这可能是因为原点真的是最好的答案,也可能是因为你根本没有想到真正的替代。这两种情况从内部很难区分。
也许有人对 Agent 通信有不同的理解,认为应该从另一个维度去抽象。也许有人在别的地方见过更好的设计。
如果你觉得 mailbox 有问题,或者你有一个更好的想法,我希望听到。
