使用幂等 API 构建弹性系统
网络不可靠,但我们的系统不能可靠。
什么是幂等性?
幂等性是 API 设计的一个特性,它确保多次发出相同的请求与一次发出相同的结果。换句话说,无论使用同一组参数调用幂等 API 端点多少次,在第一次请求成功后,结果都保持不变。
在 API 设计中,幂等性对于防止意外的副作用并确保 API 的可预测性和可靠性至关重要。它允许客户端安全地重试请求,而不会导致任何数据重复、覆盖或其他不必要的影响。
API 的幂等性取决于它对系统所做的改变,而不是它提供的响应。
为了更好地理解上述陈述,请考虑以下示例:
- 假设一个 API 端点旨在在 Web 应用程序中注册新的用户帐户。如果此 API 是幂等的,则意味着无论使用相同的输入数据(例如,相同的电子邮件和密码)调用该 API 多少次,它都只会创建一次用户帐户,并且任何后续调用都不会产生任何影响。
- API 可能会对第一个请求和后续请求返回成功响应(例如状态码 200),表示用户帐户已存在,但系统状态保持不变。
- 幂等性是根据创建用户帐户的副作用来评估的,而不是根据响应消息。
现实世界用例
-
支付处理:幂等 API 可防止处理支付时出现重复收费,确保计费的一致性和准确性。
-
订单处理:电子商务平台中的幂等 API 可避免重复订单或订单状态的意外更改。
-
文件上传:文件上传的幂等 API 可防止不必要的重复,确保文件只存储一次,即使在重试或网络问题期间也是如此。
-
订阅管理:幂等 API 处理订阅请求,而不会创建重复订阅或对用户偏好进行不必要的更改。
-
分布式系统:分布式系统中的幂等 API 保持一致性并优雅地处理故障,从而实现安全重试而不会出现数据不一致。
API 设计中如何实现幂等性?
-
分配唯一标识符:为每个请求使用 UUID 或其他唯一标识符来跟踪和识别请求。
-
幂等 HTTP 方法:使用 GET、PUT 和 DELETE 等幂等 HTTP 方法设计 API。这些方法可确保多个相同的请求与单个请求具有相同的效果。
-
幂等性密钥的过期时间:为幂等性密钥设置合理的过期时间,确保其仅在一定时间段内有效。
-
响应代码和标头:利用适当的 HTTP 状态代码(例如,200、201、204)和标头(例如,ETag、Last-Modified)来指示幂等性和成功处理。
HTTP 方法和幂等性
幂等方法是那些可以安全地重复多次而不会改变初始操作之外的结果的方法。
-
幂等的 HTTP 方法是 GET、HEAD、PUT 和 DELETE。
-
POST 请求并非幂等的。这是因为每次发出 POST 请求都会在服务器上创建新的资源,导致每次请求的结果都不同。后续的 POST 请求会创建额外的资源,从而改变服务器的状态,使其不具有幂等性。
什么是幂等键?
-
在进行 API 调用之前,客户端会向服务器请求一个随机 ID,作为幂等性密钥。
-
客户端会在以后向服务器发送的所有请求中包含此密钥。服务器会将密钥和请求详细信息存储在其数据库中。
-
当服务器收到请求时,它会使用幂等性密钥检查是否已经处理了该请求。
-
如果是,服务器将忽略该请求。如果不是,它将处理该请求并删除幂等键,以确保只处理一次。
例子
在下面的示例中,服务器生成一个幂等性密钥,并将其作为响应头(Idempotency-Key)返回给客户端。客户端必须在所有后续请求中包含此密钥。服务器会检查是否已使用幂等性密钥处理过该请求,并确保仅处理一次。
注意 - 此示例不适用于生产用途;相反,它用于说明幂等键的基本概念。
Node.js(Express)
const express = require('express');
const app = express();
const { v4: uuidv4 } = require('uuid');
const idempotencyKeys = new Set();
app.use(express.json());
app.post('/api/resource', (req, res) => {
const idempotencyKey = req.header('Idempotency-Key');
if (idempotencyKeys.has(idempotencyKey)) {
return res.status(200).json({ message: 'Request already processed' });
}
const resourceId = uuidv4();
// ... add logic to create the resource
idempotencyKeys.add(idempotencyKey);
return res.status(201).json({ resource_id: resourceId });
});
app.listen(3000, () => {
console.log('Server started on port 9000');
});
Python(Flask):
from flask import Flask, request, jsonify
import uuid
app = Flask(__name__)
idempotency_keys = set()
@app.route('/api/resource', methods=['POST'])
def create_resource():
idempotency_key = request.headers.get('Idempotency-Key')
if idempotency_key in idempotency_keys:
return jsonify({'message': 'Request already processed'}), 200
resource_id = str(uuid.uuid4())
# ... add logic to create the resource
idempotency_keys.add(idempotency_key)
return jsonify({'resource_id': resource_id}), 201
if __name__ == '__main__':
app.run()
结论
幂等 API 在确保系统的可靠性、一致性和效率方面发挥着至关重要的作用。
文章来源:https://dev.to/karishmashukla/building-resilient-systems-with-idempot-apis-5e5p