Mul*_*ure 5 rest spring hibernate transactions idempotent
我们有一个使用Spring Boot,JPA和Hibernate构建的REST API。使用API的客户端对网络的访问不可靠。为了避免给最终用户带来太多错误,我们使客户端重试不成功的请求(例如,发生超时后)。
由于我们无法确定再次发送请求时服务器尚未处理该请求,因此我们需要使POST请求成为幂等。也就是说,发送两次相同的POST请求一定不能两次创建相同的资源。
为此,我做了以下工作:
到目前为止,一切都很好。
我在同一数据库上有多个服务器实例,并且请求是负载平衡的。结果,任何实例都可以处理请求。
在我当前的实现中,可能会发生以下情况:
在这种情况下,该请求已被处理两次,这是我要避免的事情。
我想到了两种可能的解决方案:
我正在使用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 e) {
// Here perform rollback somehow
existingResponse = getExistingResponse(requestId);
serveExistingResponse(response, existingResponse);
}
}
}
else {
filterChain.doFilter(request, response);
}
}
...
Run Code Online (Sandbox Code Playgroud)
我的请求将按以下方式处理:
@Controller
public class UserController {
@Autowired
UserManager userManager;
@RequestMapping(value = "/user", method = RequestMethod.POST)
@ResponseBody
public User createUser(@RequestBody User newUser) {
return userManager.create(newUser);
}
}
@Component
@Lazy
public class UserManager {
@Transactional("transactionManager")
public User create(User user) {
userRepository.save(user);
return user;
}
}
Run Code Online (Sandbox Code Playgroud)
Filter上面显示的开始事务,提交事务或回滚事务?这是一个好习惯吗?@Transactional("transactionManager")。使用过滤器启动或回滚事务时会发生什么?注意:我对spring,hibernate和JPA还是比较陌生,并且对事务和过滤器背后的机制了解有限。
Based on
To avoid having too many errors for the end user, we made the client retry unsuccessful requests
you seem to have full control of the client code (great!) as well as the server.
It is, however, not clear whether the problem with the client's network is flakiness (the connection often randomly drops and requests are aborted) or slowness (timeouts), since you've mentioned both. So let's analyse both!
The first things that I'd recommend are:
However:
then the standard request-response scheme is probably not suitable.
In this case, instead of having the client wait for a response you could perhaps send back an immediate acknowledgement Request received and send the actual response via some TCP socket? Any following attempts would receive either a message saying that the Request is being processed, or the final response, if the operation is complete (this is where the idempotence of your operation would help).
If the client network is flaky and prone to frequent failures, the above proposed solution, where requests and responses are uncoupled, should work too!
Request in progress,您可以尝试再次监听套接字。如果无法使用套接字,则可以使用轮询。轮询不是很好,但就我个人而言,我很可能仍然选择轮询而不是回滚,特别是如果服务器操作很慢 - 这将允许在重试之前有适当的暂停。
回滚的问题在于,他们会尝试使用代码从故障中恢复,这本身并不是万无一失的。如果回滚时出现问题怎么办?您能否确保回滚是原子的和幂等的,并且在任何情况下都不会让系统处于未定义的状态?除此之外,它们的实现并不简单,并且会带来额外的复杂性和额外的测试和维护代码。
如果您不拥有客户端代码,您将会遇到更多麻烦,因为 API 的使用者可以随意对您的服务器进行大量任意调用。在这种情况下,我肯定会锁定幂等操作并返回响应,表示正在处理请求,而不是尝试使用回滚来恢复任何内容。想象一下有多个并发请求和回滚!如果您对斯坦尼斯拉夫的提议不满意(The queue will get longer and longer, making the whole system slower, reducing the capacity of the system to serve requests.),我相信这种情况会更糟。
| 归档时间: |
|
| 查看次数: |
1632 次 |
| 最近记录: |