javascript
Spring Cloud综合实战 - 基于TCC补偿模式的分布式事务
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
本文通過(guò)使用Spring Cloud和Docker構(gòu)建了一個(gè)常見(jiàn)的Microservice體系.
Spring Cloud為開(kāi)發(fā)者提供了快速構(gòu)建分布式系統(tǒng)中的一些常見(jiàn)工具, 如分布式配置中心, 服務(wù)發(fā)現(xiàn)與注冊(cè)中心, 智能路由, 服務(wù)熔斷及降級(jí), 消息總線等.
而Spring Cloud Sleuth為Spring Cloud提供了分布式追蹤方案, 可視化地分析服務(wù)調(diào)用鏈路和服務(wù)間的依賴關(guān)系
本次實(shí)戰(zhàn)以模擬下單流程作為實(shí)戰(zhàn)演示, 使用Try-Confirm-Cancel即TCC模式為分布式事務(wù)提供最終一致性.
Try Confirm Cancel補(bǔ)償模式
本實(shí)例遵循的是Atomikos公司對(duì)微服務(wù)的分布式事務(wù)所提出的RESTful TCC解決方案
RESTful TCC模式分3個(gè)階段執(zhí)行
系統(tǒng)結(jié)構(gòu)
基礎(chǔ)組件
Zuul Gateway
Zuul在本實(shí)例中僅作為路由所使用, 配置降低Ribbon的讀取與連接超時(shí)上限
Eureka H.A.
多個(gè)對(duì)等Eureka節(jié)點(diǎn)組成高可用集群, 并將注冊(cè)列表的自我保護(hù)的閾值適當(dāng)降低
Config Server
-
如果遠(yuǎn)程配置中有密文{cipher}*, 那么該密文的解密將會(huì)延遲至客戶端啟動(dòng)的時(shí)候. 因此客戶端需要配置AES的對(duì)稱密鑰encrypt.key, 并且客戶端所使用的JRE需要安裝Java 8 JCE, 否則將會(huì)拋出Illegal key size相關(guān)的異常.
spring:cloud:config:server:git:uri: 'https://git.oschina.net/witless/conf-repo.git'clone-on-start: trueencrypt:enabled: falseapplication:name: 'config-server'
(本例中Docker Compose構(gòu)建的容器已經(jīng)安裝了JCE, 如果遠(yuǎn)程配置文件沒(méi)有使用{cipher}*也不必進(jìn)行JCE的安裝)
-
為了達(dá)到開(kāi)箱即用, 選用公開(kāi)倉(cāng)庫(kù)Github或者GitOsc
-
本項(xiàng)目中有兩個(gè)自定義注解
@com.github.prontera.Delay 控制方法的延時(shí)返回時(shí)間@com.github.prontera.RandomlyThrowsException 隨機(jī)拋出異常, 人為地制造異常
默認(rèn)的遠(yuǎn)程配置如下
solar:delay:time-in-millseconds: 0exception:enabled: falsefactor: 7這些自定義配置正是控制方法返回的時(shí)延, 隨機(jī)異常的因子等
我在服務(wù)order, product, account和tcc中的所有Controller上都添加了以上兩個(gè)注解, 當(dāng)遠(yuǎn)程配置的更新時(shí)候, 可以手工刷新/refresh或通過(guò)webhook等方法自動(dòng)刷新本地配置. 以達(dá)到模擬微服務(wù)繁忙或熔斷等情況.
RabbitMQ
原本作為可靠性事件投遞的Broker, 如今被TCC模式所替代. 可為日后的Spring Cloud Steam或Spring Cloud Bus的集成作為基礎(chǔ)組件而保留
監(jiān)控服務(wù)
Spring Boot Admin
此應(yīng)用提供了管理Spring Boot服務(wù)的簡(jiǎn)單UI, 下圖是在容器中運(yùn)行時(shí)的服務(wù)健康檢測(cè)頁(yè)
Hystrix Dashboard
提供近實(shí)時(shí)依賴的統(tǒng)計(jì)和監(jiān)控面板, 以監(jiān)測(cè)服務(wù)的超時(shí), 熔斷, 拒絕, 降級(jí)等行為
Zipkin Server
Zipkin是一款開(kāi)源的分布式實(shí)時(shí)數(shù)據(jù)追蹤系統(tǒng), 其主要功能是聚集來(lái)自各個(gè)異構(gòu)系統(tǒng)的實(shí)時(shí)監(jiān)控?cái)?shù)據(jù), 用來(lái)追蹤微服務(wù)架構(gòu)下的系統(tǒng)時(shí)延問(wèn)題. 下圖是對(duì)order服務(wù)的請(qǐng)求進(jìn)行追蹤的情況
業(yè)務(wù)服務(wù)
首次啟動(dòng)時(shí)通過(guò)Flyway自動(dòng)初始化數(shù)據(jù)庫(kù)
對(duì)spring cloud config server采用fail fast策略, 一旦遠(yuǎn)程配置服務(wù)無(wú)法連接則無(wú)法啟動(dòng)業(yè)務(wù)服務(wù)
account
用于獲取用戶信息, 用戶注冊(cè), 修改用戶余額, 預(yù)留余額資源, 確認(rèn)預(yù)留余額, 撤銷預(yù)留余額
product
用于獲取產(chǎn)品信息, 變更商品庫(kù)存, 預(yù)留庫(kù)存資源, 確認(rèn)預(yù)留庫(kù)存, 撤銷預(yù)留庫(kù)存
tcc coordinator
TCC資源協(xié)調(diào)器, 其職責(zé)如下
- 對(duì)所有參與者發(fā)起Confirm請(qǐng)求
- 無(wú)論是協(xié)調(diào)器發(fā)生的錯(cuò)誤還是調(diào)用參與者所產(chǎn)生的錯(cuò)誤, 協(xié)調(diào)器都必須有自動(dòng)恢復(fù)重試功能, 尤其是在確認(rèn)的階段, 以防止網(wǎng)絡(luò)抖動(dòng)的情況
order
order服務(wù)是本項(xiàng)目的入口, 盡管所提供的功能很簡(jiǎn)單
- 下單. 即生成預(yù)訂單, 為了更好地測(cè)試TCC功能, 在下單時(shí)就通過(guò)Feign向服務(wù)account與product發(fā)起預(yù)留資源請(qǐng)求, 并且記錄入庫(kù)
- 確認(rèn)訂單. 確認(rèn)訂單時(shí)根據(jù)訂單ID從庫(kù)中獲取訂單, 并獲取預(yù)留資源確認(rèn)的URI, 交由服務(wù)tcc統(tǒng)一進(jìn)行確認(rèn), 如果發(fā)生沖突即記錄入庫(kù), 等待人工處理
與其他服務(wù)進(jìn)行通訊, 我們選擇使用Feign
/*** @author Zhao Junjian*/ @FeignClient(name = TccClient.SERVICE_ID, fallback = TccClientFallback.class) public interface TccClient {/*** eureka service name*/String SERVICE_ID = "tcc";/*** api prefix*/String API_PATH = "/api/v1/coordinator";@RequestMapping(value = API_PATH + "/confirmation", method = RequestMethod.PUT, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})void confirm(@RequestBody TccRequest request);@RequestMapping(value = API_PATH + "/cancellation", method = RequestMethod.PUT, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}, consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})void cancel(@RequestBody TccRequest request);}Swagger UI
Swagger的目標(biāo)是為REST APIs 定義一個(gè)標(biāo)準(zhǔn)的, 與語(yǔ)言無(wú)關(guān)的接口, 使人和計(jì)算機(jī)在看不到源碼或者看不到文檔或者不能通過(guò)網(wǎng)絡(luò)流量檢測(cè)的情況下能發(fā)現(xiàn)和理解各種服務(wù)的功能. 當(dāng)服務(wù)通過(guò)Swagger定義, 消費(fèi)者就能與遠(yuǎn)程的服務(wù)互動(dòng)通過(guò)少量的實(shí)現(xiàn)邏輯. 類似于低級(jí)編程接口, Swagger去掉了調(diào)用服務(wù)時(shí)的很多猜測(cè).
運(yùn)行
Docker Compose運(yùn)行
在項(xiàng)目根路徑下執(zhí)行腳本build.sh, 該腳本會(huì)執(zhí)行Maven的打包操作, 并會(huì)迭代目錄下的*-compose.yml進(jìn)行容器構(gòu)建
構(gòu)建完成后需要按照指定的順序啟動(dòng)
啟動(dòng)MySQL, RabbitMQ等基礎(chǔ)組件
? solar git:(feature/cleanup) ? docker-compose -f infrastructure-compose.yml up -d啟動(dòng)Eureka Server與Config Server
? solar git:(feature/cleanup) ? docker-compose -f basic-ms-compose.yml up -d啟動(dòng)監(jiān)控服務(wù)
? solar git:(feature/cleanup) ? docker-compose -f monitor-ms-compose.yml up -d啟動(dòng)業(yè)務(wù)服務(wù)
? solar git:(feature/cleanup) ? docker-compose -f business-ms-compose.yml up -dIDE運(yùn)行
因?yàn)槌绦虮旧戆凑誅ocker啟動(dòng), 所以對(duì)于hostname需要在hosts文件中設(shè)置正確才能正常運(yùn)行
## solar 127.0.0.1 eureka1 127.0.0.1 eureka2 127.0.0.1 rabbitmq 127.0.0.1 zipkin_server 127.0.0.1 solar_mysql 127.0.0.1 gitlab根據(jù)依賴關(guān)系, 程序最好按照以下的順序執(zhí)行
docker mysql > docker rabbitmq > eureka server > config server > zipkin server > 其他微服務(wù)
示例
根據(jù)附表中的服務(wù)字典, 我們通過(guò)Zuul或Swagge對(duì)order服務(wù)進(jìn)行預(yù)訂單生成操作
POST http://localhost:7291/order/api/v1/orders Content-Type: application/json;charset=UTF-8{"product_id": 7,"user_id": 1 }成功后我們將得到預(yù)訂單的結(jié)果
{"data": {"id": 15,"create_time": "2017-03-28T18:18:02.206+08:00","update_time": "1970-01-01T00:00:00+08:00","delete_time": "1970-01-01T00:00:00+08:00","user_id": 1,"product_id": 7,"price": 14,"status": "PROCESSING"},"code": 20000 }此時(shí)我們?cè)俅_認(rèn)訂單
(如果想測(cè)試預(yù)留資源的補(bǔ)償情況, 那么就等15s后過(guò)期再發(fā)請(qǐng)求, 注意容器與宿主機(jī)的時(shí)間)
POST http://localhost:7291/order/api/v1/orders/confirmation Content-Type: application/json;charset=UTF-8{"order_id": 15 }如果成功確認(rèn)則返回如下結(jié)果
{"data": {"id": 15,"create_time": "2017-03-28T18:18:02.206+08:00","update_time": "2017-03-28T18:21:32.78+08:00","delete_time": "1970-01-01T00:00:00+08:00","user_id": 1,"product_id": 7,"price": 14,"status": "DONE"},"code": 20000 }至此就完成了一次TCC事務(wù), 當(dāng)然你也可以測(cè)試超時(shí)和沖突的情況, 這里就不再贅述
拓展
使用Gitlab作為遠(yuǎn)程配置倉(cāng)庫(kù)
本例中默認(rèn)使用Github或GitOsc中的公開(kāi)倉(cāng)庫(kù), 出于自定義的需要, 我們可以在本地構(gòu)建Git倉(cāng)庫(kù), 這里選用Gitlab為例.
將以下配置添加至docker compose中的文件中并啟動(dòng)Docker Gitlab容器
gitlab:image: daocloud.io/daocloud/gitlab:8.16.7-ce.0ports:- "10222:22"- "80:80"- "10443:443"volumes:- "./docker-gitlab/config/:/etc/gitlab/"- "./docker-gitlab/logs/:/var/log/gitlab/"- "./docker-gitlab/data/:/var/opt/gitlab/"environment:- TZ=Asia/Shanghai將項(xiàng)目的config-repo添加至Gitlab中, 并修改config-ms中g(shù)it倉(cāng)庫(kù)的相關(guān)驗(yàn)證等參數(shù)即可
歡迎工作一到五年的Java工程師朋友們加入Java架構(gòu)開(kāi)發(fā):855801563
本群提供免費(fèi)的學(xué)習(xí)指導(dǎo)?架構(gòu)資料?以及免費(fèi)的解答
不懂得問(wèn)題都可以在本群提出來(lái)?之后還會(huì)有職業(yè)生涯規(guī)劃以及面試指導(dǎo)
同時(shí)大家可以多多關(guān)注一下小編 純干貨?大家一起學(xué)習(xí)進(jìn)步
轉(zhuǎn)載于:https://my.oschina.net/u/3959491/blog/2992484
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud综合实战 - 基于TCC补偿模式的分布式事务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ios UITableView顶部向
- 下一篇: 构造数列中的常见变形总结【中阶和高阶辅导