小编Mul*_*ure的帖子

REST幂等性实现-已处理请求后如何回滚?

我正在努力实现的目标

我们有一个使用Spring Boot,JPA和Hibernate构建的REST API。使用API​​的客户端对网络的访问不可靠。为了避免给最终用户带来太多错误,我们使客户端重试不成功的请求(例如,发生超时后)。

由于我们无法确定再次发送请求时服务器尚未处理该请求,因此我们需要使POST请求成为幂等。也就是说,发送两次相同的POST请求一定不能两次创建相同的资源。

到目前为止我做了什么

为此,我做了以下工作:

  • 客户端正在使用自定义HTTP标头发送UUID和请求。
  • 当客户端重新发送相同的请求时,将发送相同的UUID。
  • 服务器第一次处理请求时,对请求的响应与UUID一起存储在数据库中。
  • 第二次接收到相同的请求时,将从数据库中检索结果,并做出响应而无需再次处理该请求。

到目前为止,一切都很好。

问题

我在同一数据库上有多个服务器实例,并且请求是负载平衡的。结果,任何实例都可以处理请求。

在我当前的实现中,可能会发生以下情况:

  1. 该请求由实例1处理,需要很长时间
  2. 由于花费的时间太长,客户端将中止连接并重新发送相同的请求
  3. 第二个请求由实例2处理
  4. 第一个请求处理完成,结果按实例1保存在数据库中
  5. 第二个请求处理完成。当实例2尝试将结果存储在数据库中时,结果已存在于数据库中。

在这种情况下,该请求已被处理两次,这是我要避免的事情。

我想到了两种可能的解决方案:

  1. 当已经存储了相同请求的结果时回滚请求2,并将保存的响应发送到客户端。
  2. 实例1开始处理请求时,通过将请求ID保存在数据库中来防止处理请求2。由于超时关闭了客户端和实例1之间的连接,因此该解决方案不起作用,这使得客户端无法实际接收实例1处理的响应。

尝试解决方案1

我正在使用Filter来检索和存储响应。我的过滤器大致如下所示:

@Component
public class IdempotentRequestFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException     {

        String requestId = getRequestId(request);


        if(requestId != null) { 

            ResponseCache existingResponse = getExistingResponse(requestId);

            if(existingResponse != null) {
                serveExistingResponse(response, existingResponse);
            }
            else {

                filterChain.doFilter(request, response);

                try {
                    saveResponse(requestId, response);
                    serve(response);
                }
                catch (DataIntegrityViolationException …
Run Code Online (Sandbox Code Playgroud)

rest spring hibernate transactions idempotent

5
推荐指数
1
解决办法
1632
查看次数

标签 统计

hibernate ×1

idempotent ×1

rest ×1

spring ×1

transactions ×1