幂等性API表示,无论该API使用相同的请求体/参数被成功调用多少次,数据/系统状态都将保持相同。
对于一个遵循良好原则的RESTful API来说,除了POST和PATCH之外,其他所有方法都是幂等的。但在某些特定情况下(例如,创建支付、上传文件、微服务通信……),如果我们希望让API更健壮,实现幂等性至关重要。
在这篇文章中,我们将提出一种不仅关注API性能,还具有幂等性的机制。我们将讨论以下几点:—— 为什么我们需要API具有幂等性—— 如何提高API性能—— 提出一种高效且实用的幂等性机制。
(如果您想了解如何在Java Spring Boot应用中实现这个机制,请参阅我的另一篇文章:https://medium.com/gitconnected/spring-boot-3-build-the-efficient-idempotent-api-by-redis-8d8ef70d2574)
为什么需要幂等的API?在软件工程界,我们被告知网络并不总是那么可靠。一个简单的做法就是在请求失败后重试。然而随之而来的问题是:不仅发送请求也不那么可靠,接收响应同样不稳定。
如上图所示,有两种不同的情况会导致服务器1无法收到响应。然而,对于服务器2中的数据状态而言,情况则完全不同。当请求未能发送时,服务器2不会接收到任何数据,此时资源尚未创建。在这种情况下,可以安全地重试。相反,如果问题是响应未正确返回到服务器1,服务器2已经生成了资源。在这种情况下,如果没有适当的保护措施,直接重试是不合适的。
没有适当的保护措施就进行重试可能导致严重问题,比如向客户重复收费、在服务器上生成重复的文件或导致数据不一致等问题。
一种建立安全的重试机制的解决方案,实现幂等的 API,确保提供一个健壮的 API 和一个具有弹性的系统。
如果你觉得这篇文章有价值,请考虑通过请买我一杯啤酒 🍺来支持我的工作。别忘了分享和点赞这篇文章,也可以在Medium上关注我,以获取更多未来的技术故事。感谢您的支持,祝您读得开心!
您的慷慨支持让我更有动力创作更多系统设计方面的内容。
如何提升性能一个具有幂等性的 API 完全依靠数据库(DB)来检查请求是否已被处理过,这会增加数据库的负担,从而可能影响我们 API 的性能。
考虑常见的一些重试场景,主要包括请求超时后的重试和由异步消息消费者发起的重试等。大多数重试一般会在短时间内发生。
因此,我们建议添加一个额外的内存缓存服务器(比如 Redis),通过缓存这些数据,从而优化该机制的性能,从而提升其效率。
高效的幂等(执行一次或多次效果相同的操作)机制我提出的机制流程如下。
在这个机制中,我们有 8个进程和5个决策点。请注意,整个 流程必须放在auth-filter之后,以防止未经授权的请求访问缓存数据。现在,我们来逐一看看(P代表流程;D代表决策)。
主要流程- P1: 带有
**rid**
的请求
在调用幂等性API时,需要提供一个唯一的rid
,通常由时间戳、用户ID和一个随机值组合而成。 - D1: 检查缓存中是否已有
**rid**
服务器的首个操作是检查缓存中是否已有rid
,根据是否有rid
来决定后续步骤。
- P4: 使用
**rid**
初始化缓存并设置is-done
为false
该操作应在同一个事务内执行,以避免发生竞态条件,可能使用 Java Redis 客户端中的setIfAbsent
方法或 Redis 中的setnx
方法。 - D3: 检查数据库中是否存在
**rid**
****
在真正处理请求之前,仍需检查请求是否已被处理过,以防止重复处理。 - P5: 如果没有缓存或数据库中的记录,执行原始 API 过程
应执行原始过程以处理请求。 - D4: 根据处理结果采取不同的行动
在处理请求后,成功执行将导致缓存更新并响应调用方。否则,需要进行详细的错误检查以决定下一步该做什么。 - D5: 适当的异常处理对错误分类至关重要
可重试错误包括对第三方 API 或其他数据源的网络错误,文件系统等。不可重试错误,如验证错误、内部服务器错误等,需要修改代码或调整设置才能成功重试。 - P6: 当得到明确的结果(成功或不可重试的错误)时,更新缓存
将实际响应更新到缓存中,并将is-done
设置为true
。 - P7: 对于可重试的错误,应该删除缓存
以准备处理重试请求。 - P8: 不论处理结果怎样,都需要向调用方返回响应
- D2: 已完成状态
获取缓存中的is-done
属性值,并检查该属性,它是在子流1中被设置或更新的。 - P2: 返回缓存响应
如果缓存中的is-done
属性为true
,表示这是一个之前完成的请求,应立即返回该缓存响应。 - P3: 返回正在处理中的错误
如果缓存中的is-done
属性为false
,表示请求正在被处理,为防止竞态条件或其他多线程问题的发生,应返回一个错误代码,告知调用者请求正在处理中,并建议稍后再试。
这篇文章介绍了一种与缓存服务器协作的幂等机制,以增强性能为目的。在实现过程中需要注意一些关键点。
- 唯一请求标识符(
rid
):
客户端必须在其请求中包含一个 rid,以确保每个请求都具有唯一标识符。 - 置于认证过滤器之后:
为了安全考虑,幂等性处理流程应置于认证过滤器之后。 - 原子缓存操作:
检查缓存是否存在以及创建缓存条目应原子性地进行,以确保一致性保持。 - 不存在缓存时的数据库集成:
尽管使用了缓存机制,当缓存不存在时,需要数据库来检查其存在性。 - 利用额外属性:
在缓存条目中添加类似is-done
这样的额外属性,以表示请求的处理状态。 - 异常处理的重试性定义:
定义在请求处理过程中遇到的每种异常的重试性,以帮助决定是否进行缓存更新或删除。
然后我打算试试在我的event-sourcing poc项目(事件源POC项目)实现这个机制,下次聊。
参考共同学习,写下你的评论
评论加载中...
作者其他优质文章