自动化可以使业务流程更快且更一致。
新订单可以自动更新库存。
成功的付款可以自动生成收据。
完成的请求可以自动通知负责的员工。
新记录可以自动启动跨不同系统的多个操作。
但随着这些流程的增长,有一个问题变得越来越重要:
如果相同的事件两次到达系统,会发生什么?
起初,这可能看起来是一个小的技术问题。
但事实并非如此。
重复事件可能导致客户被重复收费,库存被减少两次,发送两条消息,或相同的请求被处理多次。
困难之处在于,即使每个系统正常工作,重复事件也可能发生。
因此,可靠的自动化流程需要考虑到这种可能性。
为什么相同的事件可能会两次到达
想象一下,一个支付系统告诉公司的系统,付款已完成。
公司的系统接收到消息并开始处理。
但在支付系统收到消息成功处理的确认之前,连接被中断。
支付系统不知道消息是否已被处理。
它再次发送相同的消息。
现在公司的系统收到了相同的事件两次。
从发送者的角度来看,再次发送是合理的。
从接收系统的角度来看,同一指令出现了两次。
如果系统将每个消息视为一个全新的事件,它可能会执行两次操作。
这就是为什么仅仅说 “处理每个事件” 对于一个可靠的自动化过程来说是不够的。
系统还需要确定它是否已经处理过该事件。
给每个事件一个独特的身份
解决这个问题的最简单方法之一是给每个事件一个唯一的标识符。
例如,表示已完成支付的事件可能包含一个对该事件唯一的标识符。
当系统接收到它时,可以检查该标识符是否已经被处理。
如果没有,系统执行所需的操作。
如果有,系统则不重复该操作。
相反,它可以安全地将消息视为重复。
这创建了一个简单的规则:
同一事件应该只产生一次相同的结果。
重要的是,系统需要一种可靠的方法来识别事件。
仅使用客户的姓名或支付金额等信息通常是不够的,因为不同的事件可能具有相同的信息。
标识符需要代表事件本身。
为什么简单的数据库检查并不总是足够
一种常见的实现是:
检查事件是否存在于数据库中。
如果不存在,则执行操作。
将事件保存为已处理。
当两个相同事件的副本几乎同时到达时,问题就出现了。
两个进程可能在任何一个保存事件之前检查数据库。
两个都看到它不存在。
两个都继续。
该操作被执行了两次。
这就是为什么重复保护必须作为实际操作的一部分进行设计,而不是作为简单的检查添加在其之前。
系统需要一种方法,使检查和事件记录在多个请求同时到达时也能可靠。
该操作本身需要保护
还有另一个重要问题。
假设系统成功识别重复事件。
这很有用,但实际的业务操作也必须小心处理。
想象一个自动化过程,它接收一个订单事件,然后减少库存。
如果事件在库存更新成功之前被记录为已处理,那么在错误时刻的失败可能会造成不同的问题。
系统可能会认为事件已完成,即使实际操作从未发生。
如果它仅在操作成功后记录事件,如果操作成功但系统在记录成功之前失败,可能会出现另一个问题。
因此,可靠的自动化需要对事件和业务操作进行仔细处理。
不同的操作需要不同的保护
并非每个自动化操作都有相同的风险。
重复发送电子邮件是令人烦恼的。
对客户进行双重收费可能是严重的。
重复创建相同的员工记录可能会导致其他系统出现问题。
减少库存两次可能会导致库存不准确。
因此,设计时应考虑如果某个操作被重复执行会发生什么。
例如,将客户的状态从“待处理”更新为“已批准”可能是安全的重复操作,因为最终结果是相同的。
向账户余额添加100个单位是不同的。重复该操作会改变结果。
这种区别在设计可靠的自动化时非常重要。
使用唯一记录来保护一个操作
一种实用的方法是使重要操作依赖于唯一的业务参考。
假设一个订单有一个唯一的订单参考。
当系统接收到处理该订单的事件时,可以使用该参考来确保相关操作不能被创建两次。
这对于以下操作特别有用:
创建发票
记录付款
创建发货
创建维护请求
创建员工账户
更新库存
数据库可以强制执行参考的唯一性,而不仅仅依赖于应用程序代码。
这为该过程提供了另一层保护。
如果过程在中途停止怎么办?
一个可靠的自动化过程还需要考虑部分完成的情况。
想象一下,一个订单事件触发三个操作:
更新库存
创建发票
通知客户
第一个动作成功。
第二个动作成功。
系统在发送通知之前停止。
当流程再次启动时,不应盲目地再次执行所有三个动作。
系统需要知道哪些工作已经完成。
这可以通过记录流程的状态并设计每个动作以便可以安全地重试来处理。
关键思想是 临时故障不应强迫整个流程从头开始.
重试是可靠自动化的一部分
故障是不可避免的。
远程服务可能不可用。
网络连接可能会失败。
服务器可能会重启。
数据库可能会暂时拒绝请求。
因此,自动化流程通常需要重试失败的动作。
但重试会产生另一个重复风险。
如果一个动作成功但系统没有收到确认,它可能会再次尝试该动作。
这意味着重试机制必须与重复保护相结合。
系统应该能够询问:
“这个动作实际上已经发生过吗?”
如果答案是肯定的,它不应再次执行该动作。
这是一个仅在正常条件下工作与设计为在出现问题时保持可靠之间的主要区别之一。
记录发生的事情
一个可靠的流程应该留下清晰的记录。
对于每个重要事件,系统应该能够确定:
事件何时被接收
这是哪个事件
它是否已经被处理过
哪些操作已完成
哪些操作失败
是否重试过某个操作
最终结果是什么
这使得问题的调查变得更加容易.
没有这样的记录,员工可能会看到某件事情发生了两次,但无法理解原因.
有了清晰的记录,公司可以识别原因是重复事件、重试、系统故障,还是流程本身的错误.
围绕失败进行设计,而不是假设成功
构建自动化流程时,一个常见的错误是仅为成功路径设计.
该过程被设想为:
事件到达 → 操作发生 → 过程完成.
真实系统并不是那么简单.
一个更现实的过程是:
事件到达 → 操作开始 → 可能会失败 → 操作可能需要重试 → 事件可能再次到达 → 系统仍然必须产生正确的最终结果.
从一开始就为这些条件设计使得自动化更加可靠.
这也使得未来的更改变得更容易,因为该过程已经有了处理意外情况的明确规则.
重复保护最重要的地方
并不是每个自动化过程都需要相同级别的保护.
通常应优先考虑对业务或财务有直接影响的操作.
示例包括:
付款和退款
库存变更
客户账户变更
员工记录
发票
订单
交付请求
服务请求
访问变更
重复通知可能很容易纠正。
重复的财务交易可能就不容易了。
理解重复的后果有助于确定需要更强保护的地方。
使自动化值得信赖
重复保护的真正目的不仅仅是防止技术错误。
而是使自动化值得信赖。
员工应该能够依赖系统,而不必不断检查某个操作是否发生过一次、两次或根本没有发生。
客户不应该因为自动化过程向他们收取了两次费用而联系公司。
管理者应该能够信任库存和财务记录。
维护团队不应该因为同一机器事件被重复而收到多个相同的请求。
因此,可靠的自动化不仅仅是减少手动工作。
而是确保自动化工作在出现通信问题、重试和系统故障时仍能产生正确的结果。
设计自动化流程的更好方法
在设计新的自动化流程时,正常的成功路径不应是唯一考虑的因素。
设计还应询问:
如果同一事件到达两次会发生什么?
如果操作成功但确认丢失,会发生什么?
如果过程在中途停止,会发生什么?
如果重试相同的操作,会发生什么?
系统如何知道已经发生了什么?
哪些操作是安全的可以重复?
哪些操作绝不能发生两次?
这些问题可能对最终用户不可见。
然而,它们正是将简单的自动化工作流与可靠的工作流区分开来的因素。
一个设计良好的过程并不假设消息会准确到达一次,或者每个系统总是会正确响应。
它假设问题会发生,并确保最终结果保持正确。
这就是可靠的事件驱动自动化的基础。
目标不仅仅是让一个过程在没有人参与的情况下运行。
目标是让它 在没有人监视每一步时也能安全可信.