详解Spring Boot微服务如何集成fescar解决分布式事务问题

时间:2021-09-20 07:08:16

什么是fescar?

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

详解Spring Boot微服务如何集成fescar解决分布式事务问题

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

详解Spring Boot微服务如何集成fescar解决分布式事务问题

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

spring boot如何集成fescar?

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

  详解Spring Boot微服务如何集成fescar解决分布式事务问题

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

?
1
2
3
druiddatasource datasource = initdatasource(datasourceprops.get("url").tostring(), datasourceprops.get("username").tostring(), datasourceprops.get("password").tostring());
 
datasourceproxy proxy = new datasourceproxy(datasource);

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@component
 
public class requestheaderinterceptor implements requestinterceptor {
 
  @override
 
  public void apply(requesttemplate template) {
 
    string xid = rootcontext.getxid();
 
    if(stringutils.isnotblank(xid)){
 
      template.header("fescar-xid",xid);
 
    }
 
  }
 
}

最后,你需要创建一个http rest请求拦截器,用于把当前上下文中获取到的xid放到rootcontext。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import com.alibaba.fescar.core.context.rootcontext;
 
import org.apache.commons.lang.stringutils;
 
import org.slf4j.logger;
 
import org.slf4j.loggerfactory;
 
import org.springframework.web.filter.onceperrequestfilter;
 
 
 
import javax.servlet.filterchain;
 
import javax.servlet.servletexception;
 
import javax.servlet.http.httpservletrequest;
 
import javax.servlet.http.httpservletresponse;
 
import java.io.ioexception;
 
 
 
public class fescarxidfilter extends onceperrequestfilter {
 
  protected logger logger = loggerfactory.getlogger(fescarxidfilter.class);
 
 
 
  @override
 
  protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception {
 
    string xid = rootcontext.getxid();
 
    string restxid = request.getheader("fescar-xid");
 
    boolean bind = false;
 
    if(stringutils.isblank(xid)&&stringutils.isnotblank(restxid)){
 
      rootcontext.bind(restxid);
 
      bind = true;
 
      if (logger.isdebugenabled()) {
 
        logger.debug("bind[" + restxid + "] to rootcontext");
 
      }
 
    }
 
    try{
 
      filterchain.dofilter(request, response);
 
    } finally {
 
      if (bind) {
 
        string unbindxid = rootcontext.unbind();
 
        if (logger.isdebugenabled()) {
 
          logger.debug("unbind[" + unbindxid + "] from rootcontext");
 
        }
 
        if (!restxid.equalsignorecase(unbindxid)) {
 
          logger.warn("xid in change during http rest from " + restxid + " to " + unbindxid);
 
          if (unbindxid != null) {
 
            rootcontext.bind(unbindxid);
 
            logger.warn("bind [" + unbindxid + "] back to rootcontext");
 
          }
        }
      }
    }
  }
}

这样就完成了fescar的集成。

开始使用吧!

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@bean
 
public fescarxidfilter fescarxidfilter(){
 
  return new fescarxidfilter();
 
}
 
 
 
@bean
 
public globaltransactionscanner scanner(){
 
  globaltransactionscanner scanner = new globaltransactionscanner("fescar-test","my_test_tx_group");
 
  return scanner;
 
}

然后写两个服务,服务a调用服务b,并在a服务的调用方法上打上@globaltransactional标签:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@globaltransactional(timeoutmills = 300000, name = "fescar-test-tx")
 
public void testfescar() throws businessexception {
 
  dictionvo dictionvo = new dictionvo();
 
  dictionvo.setcode("simidatest");
 
  dictionvo.setvalue("1");
 
  dictionvo.setdesc("simidatest");
 
  dictionvo.setappid("sso");
 
  commonservice.creatediction(dictionvo);//远程调用服务b
 
  areamapper.deleteareabysysno(2);//本地事务
 
 
 
  throw new businessexception("主动报错");
 
}

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

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

last thing

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

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

原文链接:http://www.cnblogs.com/DKSL/p/fescar.html