Java 如何避免重复请求的竞争条件?
假设我收到两个具有相同负载的并发请求。但是我必须(1)执行单个支付事务(使用第三方API)和(2)以某种方式返回两个请求的相同响应。这第二个要求使事情复杂化。否则,我可能会对重复请求返回一个错误响应 我有两个实体:Java 如何避免重复请求的竞争条件?,java,spring,spring-mvc,concurrency,architecture,Java,Spring,Spring Mvc,Concurrency,Architecture,假设我收到两个具有相同负载的并发请求。但是我必须(1)执行单个支付事务(使用第三方API)和(2)以某种方式返回两个请求的相同响应。这第二个要求使事情复杂化。否则,我可能会对重复请求返回一个错误响应 我有两个实体:会话和支付(通过@OneToOne关系关联)会话有两个字段来跟踪整体状态:付款状态(无,确定,错误),会话状态(签入,签出)。初始条件为NONE和已检入 请求负载确实包含一个唯一的会话号,我使用它来获取相关会话。现在,假设支付服务对于唯一的订单id是某种“幂等的”:它对于给定的订单id
会话
和支付
(通过@OneToOne
关系关联)<代码>会话有两个字段来跟踪整体状态:付款状态
(无
,确定
,错误
),会话状态
(签入
,签出
)。初始条件为NONE
和已检入
请求负载确实包含一个唯一的会话号,我使用它来获取相关会话。现在,假设支付服务对于唯一的订单id是某种“幂等的”:它对于给定的订单id只执行一个事务。订单id也包含在请求负载中(对于两个请求的值相同)
我心目中的流程是这样的:
session.getPaymentStatus()==OK
,则查找付款并返回成功响应DataIntegrityViolationException
。我捕获它,找到已经插入的付款,并基于它返回响应Session Session=sessionRepo.findById(sessionId)
.orelsetrow(SessionNotFoundException::new);
Payment Payment=paymentManager.pay(session,req.getReference(),req.getAmount());
节省的款项;
试一试{
保存=付款回购保存(付款);
}捕获(DataIntegrityViolationException ex){
saved=paymentRepo.findByOrderId(req.getReference())
.orelsetrow(PaymentNotFoundException::new);
}
PaymentStatus=saved.getSession().getPaymentStatus();
PaymentStage=saved.getSession().getPaymentStage();
如果(阶段==完成和状态==正常)
返回CheckOutResponse.success(req.getTerminalId(),req.getReference(),
req.getPlateNumber(),saved.getAmount(),saved.getRrn());
返回CheckOutResponse.error(req.getTerminalId(),req.getReference(),
“无法完成交易。”);
我认为,将id分配给实体(对于相同的请求,希望总是相同的)和唯一的(id)约束的组合构成了避免db重复的充分条件
如果您想(出于我不知道的原因)避免第一种情况,您可以始终检查请求的时间戳,或者在更新之前将持久层设计为“手动”检查重复项
但是,像往常一样,问题是你想实现什么?这里(StackOverflow)更多的是讨论/纠正实现,而不是理论问题
编辑
如果我理解正确的话,那就是在某处设置一个公共静态标志(或者一个标志列表,你明白了)。在您的服务中,您首先检查标志,如果为true,则等待它为false;然后最后执行主操作
至于重复请求,我会将每个请求与最后一个请求进行比较。如果所有参数都相同,并且时间戳足够接近,我会返回状态400或其他
但我还是不明白你为什么想要同样的回答。当然,您可以在收到每个请求之后和实际执行之前等待任意时间,但为什么不总是允许“唯一”请求继续
我想避免所有这些类似比赛条件的情况。我有一个
感觉我错过了一些很明显的东西。从本质上讲
问题是以某种方式提出一个请求,等待另一个请求
完成有没有一种方法可以利用DB事务和锁
要顺利处理这件事
我倾向于认为,尽管付款已成功处理,但仍无法消除返回错误响应的所有可能性,因为有太多地方可能发生破坏,包括您自己的代码之外。但是,您可以通过应用一些锁定来消除不一致响应的一些机会
比如说,
获取会话
获取会话的PaymentStatus
并对其进行悲观锁定。您还必须包含代码,以确保在请求处理完成之前释放该锁,即使在错误情况下也是如此(我对此没有进一步说明)
如果ses