https://blog.csdn.net/jiangyu_gts/article/details/79470240html
微服務倡導將複雜的單體應用拆分爲若干個功能簡單、鬆耦合的服務,這樣能夠下降開發難度、加強擴展性、便於敏捷開發。當前被愈來愈多的開發者推崇,不少互聯網行業巨頭、開源社區等都開始了微服務的討論和實踐。Hailo有160個不一樣服務構成,NetFlix有大約600個服務。國內方面,阿里巴巴、騰訊、360、京東、58同城等不少互聯網公司都進行了微服務化實踐。當前微服務的開發框架也很是多,比較著名的有Dubbo、SpringCloud、thrift 、grpc等。java
雖然微服務如今如火如荼,但對其實踐其實仍處於探索階段。不少中小型互聯網公司,鑑於經驗、技術實力等問題,微服務落地比較困難。如著名架構師Chris Richardson所言,目前存在的主要困難有以下幾方面:spring
1)單體應用拆分爲分佈式系統後,進程間的通信機制和故障處理措施變的更加複雜。sql
2)系統微服務化後,一個看似簡單的功能,內部可能須要調用多個服務並操做多個數據庫實現,服務調用的分佈式事務問題變的很是突出。docker
3)微服務數量衆多,其測試、部署、監控等都變的更加困難。數據庫
隨着RPC框架的成熟,第一個問題已經逐漸獲得解決。例如dubbo能夠支持多種通信協議,springcloud能夠很是好的支持restful調用。對於第三個問題,隨着docker、devops技術的發展以及各公有云paas平臺自動化運維工具的推出,微服務的測試、部署與運維會變得愈來愈容易。apache
而對於第二個問題,如今尚未通用方案很好的解決微服務產生的事務問題。分佈式事務已經成爲微服務落地最大的阻礙,也是最具挑戰性的一個技術難題。 爲此,本文將深刻和你們探討微服務架構下,分佈式事務的各類解決方案,並重點爲你們解讀阿里巴巴提出的分佈式事務解決方案—-GTS。該方案中提到的GTS是全新一代解決微服務問題的分佈式事務互聯網中間件。segmentfault
交易中間件與數據庫經過 XA 接口規範,使用兩階段提交來完成一個全局事務, XA 規範的基礎是兩階段提交協議。
第一階段是表決階段,全部參與者都將本事務可否成功的信息反饋發給協調者;第二階段是執行階段,協調者根據全部參與者的反饋,通知全部參與者,步調一致地在全部分支上提交或者回滾。以下圖所示:服務器
兩階段提交方案應用很是普遍,幾乎全部商業OLTP數據庫都支持XA協議。可是兩階段提交方案鎖定資源時間長,對性能影響很大,基本不適合解決微服務事務問題。restful
TCC方案在電商、金融領域落地較多。TCC方案實際上是兩階段提交的一種改進。其將整個業務邏輯的每一個分支顯式的分紅了Try、Confirm、Cancel三個操做。Try部分完成業務的準備工做,confirm部分完成業務的提交,cancel部分完成事務的回滾。基本原理以下圖所示:
事務開始時,業務應用會向事務協調器註冊啓動事務。以後業務應用會調用全部服務的try接口,完成一階段準備。以後事務協調器會根據try接口返回狀況,決定調用confirm接口或者cancel接口。若是接口調用失敗,會進行重試。
TCC方案讓應用本身定義數據庫操做的粒度,使得下降鎖衝突、提升吞吐量成爲可能。 固然TCC方案也有不足之處,集中表如今如下兩個方面:
上述緣由致使TCC方案大多被研發實力較強、有迫切需求的大公司所採用。微服務倡導服務的輕量化、易部署,而TCC方案中不少事務的處理邏輯須要應用本身編碼實現,複雜且開發量大。
消息一致性方案是經過消息中間件保證上、下游應用數據操做的一致性。基本思路是將本地操做和發送消息放在一個事務中,保證本地操做和消息發送要麼二者都成功或者都失敗。下游應用向消息系統訂閱該消息,收到消息後執行相應操做。以下圖所示:
消息方案從本質上講是將分佈式事務轉換爲兩個本地事務,而後依靠下游業務的重試機制達到最終一致性。基於消息的最終一致性方案對應用侵入性也很高,應用須要進行大量業務改造,成本較高。
GTS是一款分佈式事務中間件,由阿里巴巴中間件部門研發,能夠爲微服務架構中的分佈式事務提供一站式解決方案。
更多GTS資料請訪問研發團隊微博。
性能超強
GTS經過大量創新,解決了事務ACID特性與高性能、高可用、低侵入不可兼得的問題。單事務分支的平均響應時間在2ms左右,3臺服務器組成的集羣能夠支撐3萬TPS以上的分佈式事務請求。
應用侵入性極低
GTS對業務低侵入,業務代碼最少只須要添加一行註解(@TxcTransaction)聲明事務便可。業務與事務分離,將微服務從事務中解放出來,微服務關注於業務自己,再也不須要考慮反向接口、冪等、回滾策略等複雜問題,極大下降了微服務開發的難度與工做量。
完整解決方案
GTS支持多種主流的服務框架,包括EDAS,Dubbo,Spring Cloud等。
有些狀況下,應用須要調用第三方系統的接口,而第三方系統沒有接入GTS。此時須要用到GTS的MT模式。GTS的MT模式能夠等價於TCC模式,用戶能夠根據自身業務需求自定義每一個事務階段的具體行爲。MT模式提供了更多的靈活性,可能性,以達到特殊場景下的自定義優化及特殊功能的實現。
容錯能力強
GTS解決了XA事務協調器單點問題,實現真正的高可用,能夠保證各類異常狀況下的嚴格數據一致。
GTS可應用在涉及服務調用的多個領域,包括但不限於金融支付、電信、電子商務、快遞物流、廣告營銷、社交、即時通訊、手遊、視頻、物聯網、車聯網等,詳細介紹能夠閱讀 《GTS–阿里巴巴分佈式事務全新解決方案》一文。
GTS包括客戶端(GTS Client)、資源管理器(GTS RM)和事務協調器(GTS Server)三個部分。GTS Client主要用來界定事務邊界,完成事務的發起與結束。GTS RM完成事務分支的建立、提交、回滾等操做。GTS Server主要負責分佈式事務的總體推動,事務生命週期的管理。GTS和微服務集成的結構圖以下所示,GTS Client須要和業務應用集成部署,RM與微服務集成部署。
GTS目前有三種輸出形式:公有云輸出、公網輸出、專有云輸出。
這種輸出形式面向阿里雲用戶。若是用戶的業務系統已經部署到阿里雲上,能夠申請開通公有云GTS。開通後業務應用便可經過GTS保證服務調用的一致性。這種使用場景下,業務系統和GTS間的網絡環境比較理想,達到很好性能。
這種輸出形式面向於非阿里雲的用戶,使用更加方便、靈活,業務系統只要能鏈接互聯網便可享受GTS提供的雲服務(與公有云輸出的差異在於客戶端部署於用戶本地,而不在雲上)。
在正常網絡環境下,以包含兩個本地事務的全局事務爲例,事務完成時間在20ms左右,50個併發就能夠輕鬆實現1000TPS以上分佈式事務,對絕大多數業務來講性能是足夠的。在公網環境,網絡閃斷很難徹底避免,這種狀況下GTS仍能保證服務調用的數據一致性。
具體使用樣例使用參見4.7節GTS的工程樣例。
這種形式主要面向於已建設了本身專有云平臺的大用戶,GTS能夠直接部署到用戶的專有云上,爲專有云提供分佈式事務服務。目前已經有10多個特大型企業的專有云使用GTS解決分佈式事務難題,性能與穩定性通過了用戶的嚴格檢測。
GTS對應用的侵入性很是低,使用也很簡單。下面以訂單存儲應用爲例說明。訂單業務應用經過調用訂單服務和庫存服務完成訂單業務,服務開發框架爲Dubbo。
在業務函數外圍使用@TxcTransaction註解便可開啓分佈式事務。Dubbo應用經過隱藏參數將GTS的事務xid傳播到服務端。
@TxcTransaction(timeout = 1000 * 10) public void Bussiness(OrderService orderService, StockService stockService, String userId) { //獲取事務上下文 String xid = TxcContext.getCurrentXid(); //經過RpcContext將xid傳到一個服務端 RpcContext.getContext().setAttachment("xid", xid); //執行本身的業務邏輯 int productId = new Random().nextInt(100); int productNum = new Random().nextInt(100); OrderDO orderDO = new OrderDO(userId, productId, productNum, new Timestamp(new Date().getTime())); orderService.createOrder(orderDO); //經過RpcContext將xid傳到另外一個服務端 RpcContext.getContext().setAttachment("xid",xid); stockService.updateStock(orderDO); }
更新庫存方法
public int updateStock(OrderDO orderDO) { //獲取全局事務ID,並綁定到上下文 String xid = RpcContext.getContext().getAttachment("xid"); TxcContext.bind(xid,null); //執行本身的業務邏輯 int ret = jdbcTemplate.update("update stock set amount = amount - ? where product_id = ?",new Object[]{orderDO.getNumber(), orderDO.getProductId()}); TxcContext.unbind(); return ret; }
GTS目前已經在淘寶、天貓、阿里影業、淘票票、阿里媽媽、1688等阿里各業務系統普遍使用,經受了16年和17年兩年雙十一海量請求的考驗。某線上業務系統最高流量已達十萬TPS(每秒鐘10萬筆事務)。
GTS在公有云和專有云輸出後,已經有了100多個線上用戶,不少用戶經過GTS解決SpringCloud、Dubbo、Edas等服務框架的分佈式事務問題。業務領域涉及電力、物流、ETC、菸草、金融、零售、電商、共享出行等十幾個行業,獲得用戶的一致承認。
上圖是GTS與SpringCloud集成,應用於某共享出行系統。業務共享出行場景下,經過GTS支撐物聯網系統、訂單系統、支付系統、運維繫統、分析系統等系各統應用的數據一致性,保證海量訂單和數千萬流水的交易。
GTS的公有云樣例可參考阿里雲網站。在公網環境下提供sample-txc-simple和sample-txc-dubbo兩個樣例工程。
該樣例是GTS的入門sample,案例的業務邏輯是從A帳戶轉帳給B帳戶,其中A和B分別位於兩個MySQL數據庫中,使用GTS事務保證A和B帳戶錢的總數始終不變。
1) 準備數據庫環境
安裝MySQL,建立兩個數據庫db1和db2。在db1和db2中分別建立txc_undo_log表(SQL腳本見4.7.3)。在db1庫中建立user_money_a表,在db2庫中建立user_money_b表。
2) 下載樣例
將sample-txc-simple文件下載到本地,樣例中已經包含了GTS的SDK。
3) 修改配置
打開sample-txc-simple/src/main/resources目錄下的txc-client-context.xml,將數據源的url、username、password修改成實際值。
4) 運行樣例
在sample-txc-simple目錄下執行build.sh編譯本工程。編譯完成後執行run.sh。
本案例模擬了用戶下訂單、減庫存的業務邏輯。客戶端(Client)經過調用訂單服務(OrderService)建立訂單,以後經過調用庫存服務(StockService)扣庫存。其中訂單服務讀寫訂單數據庫,庫存服務讀寫庫存數據庫。由 GTS 保證跨服務事務的一致性。
1) 準備數據庫環境
安裝MySQL,建立兩個數據庫db1和db2。在db1和db2中分別建立txc_undo_log表。在db1庫中建立orders表,在db2庫中建立stock表。
2) 下載樣例
將樣例文件sample-txc-dubbo下載到本地機器,樣例中已經包含了GTS的SDK。
3) 修改配置
打開sample-txc-dubbo/src/main/resources目錄,將dubbo-order-service.xml、dubbo-stock-service.xml兩個文件中數據源的url、username、password修改成實際值。
4) 運行樣例
a. 編譯程序
在工程根目錄執行 build.sh 命令,編譯工程。編譯後會在 sample-txc-dubbo/client/bin 目錄下生成 order_run.sh、stock_run.sh、client_run.sh 三個運行腳本對應訂單服務、庫存服務以及客戶端。
b. 運行程序
在根目錄執行run.sh,該腳本會依次啓動order_run.sh(訂單服務)、stock_run.sh(庫存服務)和client_run.sh(客戶端程序)。
樣例使用Multicast註冊中心的聲明方式。若是本機使用無線網絡,dubbo服務在綁定地址時有可能獲取ipv6地址,能夠經過jvm啓動參數禁用。
方法是配置jvm啓動參數 -Djava.net.preferIPv4Stack=true。
CREATE TABLE txc_undo_log
(
id
bigint(20) NOT NULL AUTO_INCREMENT COMMENT ‘主鍵’,
gmt_create
datetime NOT NULL COMMENT ‘建立時間’,
gmt_modified
datetime NOT NULL COMMENT ‘修改時間’,
xid
varchar(100) NOT NULL COMMENT ‘全局事務ID’,
branch_id
bigint(20) NOT NULL COMMENT ‘分支事務ID’,
rollback_info
longblob NOT NULL COMMENT ‘LOG’,
status
int(11) NOT NULL COMMENT ‘狀態’,
server
varchar(32) NOT NULL COMMENT ‘分支所在DB IP’,
PRIMARY KEY (id
),
KEY unionkey
(xid
,branch_id
)
) ENGINE=InnoDB AUTO_INCREMENT=211225994 DEFAULT CHARSET=utf8 COMMENT=’事務日誌表’;
CREATE TABLE user_money_a
(
id
int(11) NOT NULL AUTO_INCREMENT,
money
int(11) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE user_money_b
(
id
int(11) NOT NULL AUTO_INCREMENT,
money
int(11) DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE orders
(
id
bigint(20) NOT NULL AUTO_INCREMENT,
user_id
varchar(255) NOT NULL,
product_id
int(11) NOT NULL,
number
int(11) NOT NULL,
gmt_create
timestamp NOT NULL,
PRIMARY KEY (id
)
) ENGINE=MyISAM AUTO_INCREMENT=351 DEFAULT CHARSET=utf8
CREATE TABLE stock
(
product_id
int(11) NOT NULL,
price
float NOT NULL,
amount
int(11) NOT NULL,
PRIMARY KEY (product_id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8