经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
详解Spring Boot微服务如何集成fescar解决分布式事务问题
来源:jb51  时间:2019/1/31 9:04:03  对本文有异议

什么是fescar?

关于fescar的详细介绍,请参阅fescar wiki

传统的2PC提交协议,会持有一个全局性的锁,所有局部事务预提交成功后一起提交,或有一个局部事务预提交失败后一起回滚,最后释放全局锁。锁持有的时间较长,会对并发造成较大的影响,死锁的风险也较高。

fescar的创新之处在于,每个局部事务执行完立即提交,释放本地锁;它会去解析你代码中的sql,从数据库中获得事务提交前的事务资源即数据,存放到undo_log中,全局事务协调器在回滚的时候直接使用undo_log中的数据覆盖你提交的数据。

Spring Boot如何集成fescar?

我们可以从官方代码库中看到,fescar目前提供的示例是针对使用dubbo的服务,那Spring Boot的项目如何集成fescar呢?

  

和很多2PC提交协议(如tx_lcn)的解决方案一样,fescar也是在数据源处做了代理,和事务协调器进行通信,来决定本地事务是否回滚。所以,第一步,在你的spring boot项目中,首先应使用fescar提供的代理数据源作为你的数据源,例如:

  1. DruidDataSource dataSource = initDataSource(dataSourceProps.get("url").toString(), dataSourceProps.get("username").toString(), dataSourceProps.get("password").toString());
  2.  
  3. DataSourceProxy proxy = new DataSourceProxy(dataSource);

然后,你需要创建一个Feign拦截器,把RootContext中的XID(XID用于标识一个局部事务属于哪个全局事务,需要在调用链路的上下文中传递)传递到上层调用链路。

  1. @Component
  2.  
  3. public class RequestHeaderInterceptor implements RequestInterceptor {
  4.  
  5. @Override
  6.  
  7. public void apply(RequestTemplate template) {
  8.  
  9. String xid = RootContext.getXID();
  10.  
  11. if(StringUtils.isNotBlank(xid)){
  12.  
  13. template.header("Fescar-Xid",xid);
  14.  
  15. }
  16.  
  17. }
  18.  
  19. }

最后,你需要创建一个Http Rest请求拦截器,用于把当前上下文中获取到的XID放到RootContext。

  1. import com.alibaba.fescar.core.context.RootContext;
  2.  
  3. import org.apache.commons.lang.StringUtils;
  4.  
  5. import org.slf4j.Logger;
  6.  
  7. import org.slf4j.LoggerFactory;
  8.  
  9. import org.springframework.web.filter.OncePerRequestFilter;
  10.  
  11.  
  12. import javax.servlet.FilterChain;
  13.  
  14. import javax.servlet.ServletException;
  15.  
  16. import javax.servlet.http.HttpServletRequest;
  17.  
  18. import javax.servlet.http.HttpServletResponse;
  19.  
  20. import java.io.IOException;
  21.  
  22.  
  23. public class FescarXidFilter extends OncePerRequestFilter {
  24.  
  25. protected Logger logger = LoggerFactory.getLogger(FescarXidFilter.class);
  26.  
  27.  
  28. @Override
  29.  
  30. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
  31.  
  32. String xid = RootContext.getXID();
  33.  
  34. String restXid = request.getHeader("Fescar-Xid");
  35.  
  36. boolean bind = false;
  37.  
  38. if(StringUtils.isBlank(xid)&&StringUtils.isNotBlank(restXid)){
  39.  
  40. RootContext.bind(restXid);
  41.  
  42. bind = true;
  43.  
  44. if (logger.isDebugEnabled()) {
  45.  
  46. logger.debug("bind[" + restXid + "] to RootContext");
  47.  
  48. }
  49.  
  50. }
  51.  
  52. try{
  53.  
  54. filterChain.doFilter(request, response);
  55.  
  56. } finally {
  57.  
  58. if (bind) {
  59.  
  60. String unbindXid = RootContext.unbind();
  61.  
  62. if (logger.isDebugEnabled()) {
  63.  
  64. logger.debug("unbind[" + unbindXid + "] from RootContext");
  65.  
  66. }
  67.  
  68. if (!restXid.equalsIgnoreCase(unbindXid)) {
  69.  
  70. logger.warn("xid in change during http rest from " + restXid + " to " + unbindXid);
  71.  
  72. if (unbindXid != null) {
  73.  
  74. RootContext.bind(unbindXid);
  75.  
  76. logger.warn("bind [" + unbindXid + "] back to RootContext");
  77.  
  78. }
  79. }
  80. }
  81. }
  82. }
  83. }

这样就完成了fescar的集成。

开始使用吧!

首先在项目中初始化两个Bean:

  1. @Bean
  2.  
  3. public FescarXidFilter fescarXidFilter(){
  4.  
  5. return new FescarXidFilter();
  6.  
  7. }
  8.  
  9.  
  10. @Bean
  11.  
  12. public GlobalTransactionScanner scanner(){
  13.  
  14. GlobalTransactionScanner scanner = new GlobalTransactionScanner("fescar-test","my_test_tx_group");
  15.  
  16. return scanner;
  17.  
  18. }

然后写两个服务,服务A调用服务B,并在A服务的调用方法上打上@GlobalTransactional标签:

  1. @GlobalTransactional(timeoutMills = 300000, name = "fescar-test-tx")
  2.  
  3. public void testFescar() throws BusinessException {
  4.  
  5. DictionVO dictionVO = new DictionVO();
  6.  
  7. dictionVO.setCode("simidatest");
  8.  
  9. dictionVO.setValue("1");
  10.  
  11. dictionVO.setDesc("simidatest");
  12.  
  13. dictionVO.setAppId("sso");
  14.  
  15. commonService.createDiction(dictionVO);//远程调用服务B
  16.  
  17. areaMapper.deleteAreaBySysNo(2);//本地事务
  18.  
  19.  
  20. throw new BusinessException("主动报错");
  21.  
  22. }

最后,两个项目中添加application.conf文件,用于告诉客户端如何与分布式协调器通信,官方示例中有这个文件,就不在此贴代码啦,application.conf传送门

启动事务协调器,sh fescar-server.sh 8091 ~/dksl/git/fescar/data,启动你的项目,开始测试吧!

last thing

分布式事务作为微服务应用中的老大难问题,在现有的解决方案中,个人认为fescar是目前最轻量并且代价最小的一种解决方案。目前的版本,事务协调器还不能分布式部署,官方给出的路线图是在三月底会有第一个生产可用版本。让我们一起参与到fescar的社区中,共同推动fescar生态建设,让落地微服务不必再担心分布式事务的问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号