我自己的一个工作流——决定新WhatsApp联系人收到机器人回复还是转给我的那道门——从上周四到周日失败了24次。整整71小时。我是周一早上发现的,当时打开执行列表是为了查一件毫不相关的事。

对于一个靠卖自动化方案吃饭的人来说,这很丢人。所以我没有只修好那一个工作流就完事,而是去把主生产实例上全部225个工作流的错误处理状态量了一遍。以下是实例中保留的所有执行记录——因为开启了清理策略,这是8月18日至24日六天的窗口:

打开网易新闻 查看精彩图片

n8n 2.35.5,队列模式,Postgres数据库。0.21%的失败率没问题。85%才是问题。

错误处理在n8n里是逐工作流手动开启的

n8n的错误处理机制是opt-in(主动选择加入)的,每个工作流单独配置。你需要先建一个触发器为Error Trigger节点的工作流,然后在其他每个工作流里打开Settings → Error Workflow,指向那个处理工作流。没有一个全局开关能一键应用到所有已建好的工作流,或者未来新建的所有工作流。

这是一个每周都在增长的列表上的逐工作流复选框。没有人是故意忘记的。你只是在晚上11点建好第61个工作流,它跑通了,你激活它,然后继续下一件事。一个opt-in安全网的失败模式就是:覆盖率在悄悄衰减,而你脑子里的数字还停留在"嗯,我们有错误处理"。

我的覆盖率已经衰减到了43%。

最长宕机窗口全部属于未设防工作流

我把失败按工作流分组拉出来,加了一列:该工作流是否挂了错误处理器。

看最后一列,别看失败次数。所有超过9小时的窗口都属于未设防的工作流。最长的是70.9小时——一个WhatsApp机器人门卫,三天内失败了24次,而我当时在忙别的事。没有火烧眉毛。没有客户来投诉。机器人只是不回复新联系人,而我发现它的唯一原因是我主动去查了。

设防的工作流也失败了——合计16次。但它们的最长窗口只有1.2小时,因为有东西通知了我。

这就是Error Trigger的全部价值主张,不是"减少失败",而是让失败以小时为单位结束,而不是以天为单位。

错误处理器本身也没人看守

我的工作流里有三个包含Error Trigger节点,它们就是处理器——负责发Telegram告警的东西。下面是每个处理器覆盖了多少个活跃工作流:

每个处理器都是未设防的。而覆盖15个工作流的那个处理器,就是上面表格里同一行:它在同一个六天窗口内失败了13次。

这13次失败意味着,15个工作流完全没有告警,也没有任何途径发现问题。错误处理器无法报告自己的错误,因为报告错误的东西就是错误处理器本身。用YAML说就是:谁来守护守护者?

你也不能通过把处理器A指向处理器B来解决这个问题。那只是把沉默点往后挪一跳,还加了一个你迟早会忘记的循环。捕获者必须从n8n外部被监控。

三个实操建议

1. 只建一个处理器,不要每个项目一个。扇入优于扇出。一个统一的Error Trigger工作流,格式化输出{{ $json.workflow.name }}、{{ $json.execution.id }},比维护多个处理器简单得多,也更容易保证每个处理器都被覆盖到。

2. 给处理器加外部心跳。既然错误处理器无法报告自己的错误,那就让它在每次成功执行时发一个"我还活着"的信号。用一个外部监控服务(比如Healthchecks.io或类似工具)来盯这个心跳。如果心跳停了,说明处理器挂了,这时候你至少知道要去查。

3. 定期审计覆盖率。不要相信记忆。每个月跑一次脚本,列出所有活跃工作流和它们是否挂了错误处理器。我的覆盖率衰减到43%不是一天发生的,是每次深夜新建工作流时一点一点漏掉的。一个简单的审计脚本就能在衰减变成灾难之前抓住它。

这次审计的教训很简单:opt-in的安全网,默认就是会漏的。如果你靠自动化吃饭,花一个小时把错误处理覆盖率拉满,再花一个小时给处理器加个外部心跳。这可能是你今年做的性价比最高的运维投资。