如何设计健壮且可预测的 API 以实现幂等性?
API 如何可能不够健壮且不可预测?
- 网络不可靠。
- 服务器更可靠,但仍可能出现故障。
如何解决这个问题?三个原则:
-
客户端重试以确保一致性。
-
使用幂等性和幂等性键进行重试,以允许客户端传递唯一值。
- 在 RESTful API 中,PUT 和 DELETE 动词是幂等的。
- 然而,POST 可能导致==“双重收费”问题==。因此,我们使用==幂等性键==来识别请求。
- 如果故障发生在服务器之前,则会进行重试,服务器将首次看到该请求,并正常处理。
- 如果故障发生在服务器中,则 ACID 数据库将通过 幂等性键保证事务。
- 如果故障发生在服务器回复之后,则客户端重试,服务器简单地回复成功操作的缓存结果。
-
使用==指数退避和随机抖动==进行重试。要考虑==雷鸣般的群体问题==,即服务器可能处于降级状态,突发的重试可能会进一步损害系统。
例如,Stripe 的客户端重试计算延迟如下...
def self.sleep_time(retry_count)
# 根据到目前为止的尝试次数应用初始网络重试延迟的指数退避。不要让这个数字超过最大网络重试延迟。
sleep_seconds = [Stripe.initial_network_retry_delay * (2 ** (retry_count - 1)), Stripe.max_network_retry_delay].min
# 通过在 (sleep_seconds / 2) 到 (sleep_seconds) 范围内随机化值来应用一些抖动。
sleep_seconds = sleep_seconds * (0.5 * (1 + rand()))
# 但永远不要少于基础睡眠秒数。
sleep_seconds = [Stripe.initial_network_retry_delay, sleep_seconds].max
sleep_seconds
end