前言
最近公司使用分佈式框架搭建本身的服務(springCloud),就會遇到各位大神都會遇到的神級煩問題:分佈式事務問題,近期研究了一下lcn框架的源碼,以爲很不錯,因而果斷選用lcn控制分佈式事務。選用緣由以下:git
1.徹底開源,能夠在官方的基礎上作本身的各類修改 2.不只支持springCloud,並且支持dubbo 3.數據庫方面,支持mybatis,jdbc等 正如lcn官網所說: LCN並不生產事務,LCN只是本地事務的協調者 官網(http://www.txlcn.org)
情景分析
假如如今你要經過攜程官網買上海到大理的機票,而正好沒有上海直達大理的飛機,所以你須要從昆明中轉。此時偉大的攜程已經給你推薦了一條最優的中轉路線
(東航)上海->昆明 + (南航) 昆明->大理
你以爲滿意,因而屁顛屁顛的準備一鍵下單了....
咱們來分析一下,你點擊下單之後,攜程作了哪些事:github
(ps 若是攜程這樣作: 1.先看東航有沒有上海到昆明的票,有,扣票,付錢,提交事務 2.再看南航有沒有昆明到大理的票,有,扣票,付錢,提交事務 可是,恰好有那麼一次,作完1後,發現2竟然沒票了!! 你就鬱悶了,你原本想到大理,可是你只買了去昆明的票,你確定不樂意 )
首先攜程向東航嘗試性發送扣票請求(不提交扣票的事務),若是有票,好,繼續向南航發送嘗試性扣票請求(不提交扣票的事務),若是都有票,那麼一次性提交兩邊的事務,若是沒有一邊沒票,則回滾兩個事務.spring
綜上,咱們可不能夠經過一個三方,來控制咱們各個模塊的事務呢,答案確定是有的,LCN就是數據庫
下面,先闡述怎麼集成,怎麼使用,而後下一篇文章闡述源碼分析
(5.0.2版本集成:https://segmentfault.com/a/11...)segmentfault
怎麼集成
前提:springCloud服務是使用feign實現內部服務之間的通訊api
pom.xml座標(今天是2018.10.17,最新的是4.1.0版本): (<lcn.last.version>4.1.0</lcn.last.version>) <dependency> <groupId>com.codingapi</groupId> <artifactId>transaction-springcloud</artifactId> <version>${lcn.last.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.codingapi</groupId> <artifactId>tx-plugins-db</artifactId> <version>${lcn.last.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency>
咱們知道,feign是基於RequestTemplate模擬的http發送的請求,若是有研究feign的源碼,咱們能夠發如今SynchronousMethodHandler 類下面,有一個targetRequest(RequestTemplate template)方法,這個方法裏面會循環的調用interceptor.apply(template);
而lcn正是重寫了apply方法,將本身的group-id傳遞到了下一方:
TransactionRestTemplateInterceptor.apply()
而若是咱們項目使用了oauth2或者其餘安全框架,使用feign調用的時候,
就會出現401的問題,意思是沒有權限訪問下一個模塊,所以,咱們須要將token封裝在請求頭裏面,而後再封裝。這個類的完成代碼以下:安全
*****必定要注意,這個類的包路徑,要是這個: package com.codingapi.tx.springcloud.feign ****即在本身項目新建一個這個絕對路徑的類,以此來讓jvm走咱們自定義的類 public class TransactionRestTemplateInterceptor implements RequestInterceptor { private Logger logger = LoggerFactory.getLogger(TransactionRestTemplateInterceptor.class); public TransactionRestTemplateInterceptor() { } public void apply(RequestTemplate requestTemplate) { TxTransactionLocal txTransactionLocal = TxTransactionLocal.current(); String groupId = txTransactionLocal == null ? null : txTransactionLocal.getGroupId(); this.logger.info("LCN-SpringCloud TxGroup info -> groupId:" + groupId); RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); HttpServletRequest request = requestAttributes == null ? null : ((ServletRequestAttributes) requestAttributes).getRequest(); Object attribute = request.getAttribute("OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE"); String token = attribute == null ? null : attribute.toString(); requestTemplate.header("Authorization", "Bearer " + token); if (txTransactionLocal != null) { requestTemplate.header("tx-group", new String[]{groupId}); } } }
這個包路徑下,還要有這兩個類:mybatis
/** * Created by liuliang on 2018/10/10. */ @Service public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{ @Override public String httpGet(String url) { System.out.println("httpGet-start"); String res = HttpUtils.get(url); System.out.println("httpGet-end"); return res; } @Override public String httpPost(String url, String params) { System.out.println("httpPost-start"); String res = HttpUtils.post(url,params); System.out.println("httpPost-end"); return res; }
}app
//----------------------類分割線---------------------
/** * Created by liuliang on 2018/10/10. */ @Service public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{ @Value("${tm.manager.url}") private String url; @Override public String getTxUrl() { System.out.println("load tm.manager.url "); return url; }
}負載均衡
作好這些以後,咱們須要將三方控制事務的代碼(tx-manager)拿過來,啓動並註冊到咱們本身的eureka上面。這邊給出官方code:https://github.com/codingapi/...
咱們拉下來之後,啓動tx-manager,並注意如下三個配置:
#服務端口 server.port=7000 #tx-manager不得修改!!!!!! spring.application.name=tx-manager #eureka 地址(註冊到本身的eureka) eureka.client.service-url.defaultZone=http://127.0.0.1:8880/eureka/
ok,如今咱們啓動了tx-manager,再回頭看咱們本身的項目配置,在本身項目配置(.yml)裏面加上以下:
init-db: true #txmanager地址 tm: manager: url: http://127.0.0.1:7000/tx/manager/ logging: level: com: codingapi: debug ##Ribbon的負載均衡策略 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule MaxAutoRetriesNextServer: 0
最後,咱們還須要加上一個配置文件,加到項目的根目錄下面:
tx.properties
url=http://127.0.0.1:7000/tx/manager/
怎麼使用
//事務發起方:@TxTransaction(isStart = true) /** * 測試分佈式事務 */ @TxTransaction(isStart = true) @Transactional public void testM() { MallUser user = mallUserMapper.selectList(new EntityWrapper<MallUser>()).get(0); Random random = new Random(); int i = random.nextInt(100); user.setName(i + "FF"); mallUserMapper.updateById(user); log.info("i:" + i); MallItem mallItem = itemFeign.getItemById(1 + ""); mallItem.setItemDetail(i + "FF"); Boolean aBoolean = itemFeign.updateItemById(mallItem); log.info("item:" + aBoolean); throw new RuntimeException("333"); } //事務參與方@TxTransaction @Transactional @TxTransaction public Boolean updateItemById(MallItem mallItem){ boolean b = this.updateById(mallItem); // throw new RuntimeException("33"); return b; }
好了,經過以上步驟,分佈式事務框架就集成成功了,固然一次就成功基本上不太可能,若是你們在集成的過程當中,碰到什麼問題,能夠上lcn分佈式事務框架的官網(http://www.txlcn.org)
最後附上上面安裝須要的代碼,配置等:
連接:https://pan.baidu.com/s/1ulBb... 提取碼:cftf