【问题标题】:Duplicated post requests protection, multithreading重复发布请求保护,多线程
【发布时间】:2020-11-09 11:33:34
【问题描述】:

有 Spring Boot 应用程序。 控制器->服务->存储库

问题:如果用户向服务器发送多个 post 请求,每个请求都会创建自己的线程。

并且 @Service 检查如下 - 不起作用:

    @Service
    @Transactional
    public class MyService {
      public List<SingleRecord> save( List<SingleRecord> recs, Integer userId){
    
          List<SingleRecord> recsFromDataBase = singleRecordRepository.findAllByUserId(userId);
          if ( recsFromDataBase.size() > 0 ){
              throw new MyException("User already has records!");
          } else {
              return singleRecordRepository.saveAll(recs);
          }
        
      }
    }

例如:我们有 10 个来自用户的并发请求。 (在连接问题期间,他按了 10 次“发送”按钮)他们都到达singleRecordRepository.saveAll(recs); 并且都坚持到基地。 (因为多线程) 因此服务检查变得毫无用处。

我尝试将 Serializable 用于 @Transactional 隔离级别 - 但在单独的用户使用此服务时会导致大量死锁和错误。

Java 文章作者建议对发布请求使用令牌 - 这看起来不像是优雅的解决方案。

现在我将创建线程安全的 Singleton 来检查用户请求是否重复。这太可怕了……

如何使服务检查正常工作?


https://github.com/PilVadim/FSStackOverflow

与我的应用程序相同的服务示例。 重现错误:

  1. 启动应用程序。打开swagger UI http://localhost:8085/swagger-ui.html#

  2. 对于 Chrome - F12->网络->将“在线”更改为“GPRS”;

  3. 在swagger ui上,打开POST请求,将body中的id改为null,然后按几次'Execute';

  4. 将“GPRS”改回“在线”; 几乎所有请求都会成功完成。 这意味着 - 入住服务无法正常工作。

【问题讨论】:

  • 您应该使您的服务具有幂等性。这意味着您应该能够处理已创建的记录。
  • @Simon Martinelli,但是怎么做?有什么最佳做法吗?
  • 不,这取决于您的要求。什么是单曲记录?它可以通过我的唯一标识符来识别吗?然后你可以使用 Merge 而不是 Insert
  • 是的,它有一个唯一标识符。它是代理键,在用户请求中它们都为空。 Spring如何发现ID为空的对象是一个对象?对于合并工作,我们需要由所有字段组成的复合键。
  • 听起来像是 XY 问题,为什么不直接在第一次按下按钮时禁用它?

标签: java spring multithreading hibernate duplicates


【解决方案1】:

改变了

public List&lt;SingleRecord&gt; save( List&lt;SingleRecord&gt; recs, Integer userId)

public synchronized List&lt;SingleRecord&gt; save( List&lt;SingleRecord&gt; recs, Integer userId)

save 之前添加了同步 现在只有 1 个线程可以访问服务。

一切都开始按计划进行。

【讨论】:

    猜你喜欢
    • 2016-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-19
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多