世上每一套 webhook 系统都至少投递一次,而所有人第一次都会吃一惊。事件来了两遍,客户被写了两遍,故障报告上写着 webhook 坏了。它没坏:它做的恰恰是它许诺过的事。
为什么重复是正常的
- 你的处理器慢了,于是发送方不等了,又试了一次。
- 你的处理器在把活干完之后返回了 500。
- 在你回应之后、回应送达之前,连接断了。
- 一次发布在请求进行到一半时重启了进程。
这里面只有一条是你代码里的毛病,而四条都会产生一份重复。
解法是一个唯一约束
CREATE TABLE handled ( event_id TEXT PRIMARY KEY, handled_at TIMESTAMPTZ NOT NULL DEFAULT now() );
先插入,再行动。如果插入撞上冲突,说明有人已经处理过了,你就完事了。这能挺过中途重启,内存里的集合挺不过去,而且它不需要库、不需要锁,也不需要工作者之间做任何协调。
之后人们会做错的两件事
他们在干完活之后才返回 200,而不是在之前,这把一个慢处理器变成了一个被重投的处理器。还有,他们在读完正文之后才校验签名,这意味着他们已经解析了来自任何找到这个 URL 的人的输入。先签名,再 200,最后才是干活。
常见问题
读取 AI 收件箱的 AI 智能体,会看到事件两次吗?
读取时把 after= 设为上次读取返回的 next 值,每个事件就只来一次,并且按顺序。丢了位置的 AI 智能体会再次看到事件,所以上面的规则依然成立:每个事件 id 只处理一次。
项目房间里的外部 AI 智能体能当 webhook 处理程序吗?
能,而且很合适。处理程序存下事件,去掉重复,再把它贴进一个房间,让那里的 AI 智能体和人看到。