seata在使用feign-url通過域名調用時分佈式事務不生效問題及時解決 關於seata解決分佈式事務流程的介紹 引入seata後的後遺症 seata問題的一步步剖析及解決

在前幾個月時,我們項目出現了分佈式事務的問題,那麼什麼是分佈式事務問題呢,簡單地說,我們有倆服務A和B,它們對應的數據源分別是a_db和b_db,A服務收到請求在執行到某個操作時,需要調用B服務,在B服務裏繼續執行,B服務裏面的執行牽扯到了對b_db的增刪改操作,等B服務執行完後,A又繼續執行,結果此刻,A發生異常了,由於B在另一個服務,有自己的數據源,它和A也不屬於一個事務,導致a_db自己回滾了,b_db卻沒有回滾,這不就出問題了麼,這也就是我們系統分佈式事務問題的來源。

後來呢,我們引入了springcloud中解決分佈式事務的組件seata,關於分佈式事務seata的介紹安裝各個模式我就不詳細的一一囉嗦的解釋了,當然爲了方便後面大家對問題發生和解決<typo id="typo-336" data-origin="得" ignoretag="true">得</typo>更好理解,我還是會說一下這個seata解決分佈式事務的流程。

關於seata解決分佈式事務流程的介紹

seata分爲三部分,TC事務協調者,TM事務管理器,RM資源管理器。其中TC是一個獨立的seata-sever服務,用來協調整個分佈式事務的,在git_hub上可以自行下載;TM是全局事務的發起者,管理整個全局事務,相當於項目中的調用者服務,在我們項目中就相當於那個A;RM資源管理器可以有多個,在項目中屬於被調用者,在我們項目中相當於那個B。

它們的執行流程如下:

  • 1.TM向TC發起一個請求,說明自己要開啓一個全局事務。
  • 2.TC收到了來自於TM的請求,生成了一個XID作爲這個全局事務的唯一標識,返給了TM。
  • 3.TM開始去調用其他的RM,並將XID一併地傳給了那些RM。
  • 4.RM會接收到XID,知道自己的事務屬於這個全局事務,它會將自己的本地事務註冊到TC作爲這個XID下面的一個分支事務,並把自己的事務執行結果也告訴TC。
  • 5.各個微服務執行完之後,TC就知道這個XID下的各個分支事務的執行結果,當然TM也知道了。
  • 6.TM發現各個分支事務都成功了,就向TC發起請求進行提交,否則就向TC發起請求進行回滾。
  • 7.TC收到請求後,就向XID下的所有的分支事務發起相應的請求。
  • 8.各個微服務收到TC的請求後,執行相應的命令,並把執行結果上報給TC。

下面爲了幫助大家更好地理解,放了一張圖進行演示:

引入seata後的後遺症

我們項目是個不太標準的微服務,分爲服務A和B,但卻沒有註冊中心,使用了springcloud的組件feign進行服務通信調用,但是沒有註冊中心不能通過服務名發現服務,只能使用了feign的url模式進行互通 ,當然這也是爲問題的爆發埋下了種子。

seata其實使用的模式有很多,比如AT了,TCC了,XA了,還有Saga了,當然我們選用的是AT模式,這種是無侵入業務式的模式,官方也比較推薦。至於註冊配置方式也有很多,有file 、nacos 、eureka、redis、zk、consul、etcd3、sofa等,最終由於我們項目沒有註冊中心的特殊性,我們就只好選用了file單機的註冊配置方式。

於是經過下載seata-server並啓動,修改file.conf文件的seata-server地址,並將file.conf和registory.conf配置文件放入到resources下,修改數據源爲代理數據源,在a_db和b_db添加undo_log數據庫表,變A調用端的@Transactional爲@GlobalTransactional,這些操作完成後,一個seata分佈式事務處理框架的引入算是正式完成,於是在本地電腦上啓動了A,B項目,進行模擬單元測試,分佈式事務問題完美處理,於是代碼上測試服務器,過了一段時間上了預生產服務器。

那時候由於工作事多,只在本地模擬了下,服務器上也只能測試中遇到了再看,沒再去關心這件事,畢竟本地已經完美契合了,結果一個月後上了預生產環境,某個業務代碼由於數據原因發生了異常,那個方法還牽扯到了分佈式事務問題,在觀察數據庫數據時,離奇的發現,分佈式事務沒生效,B服務沒有回滾!!

seata問題的一步步剖析及解決

問題發生了後,抓緊在本地模擬了下,發現在自己電腦上分佈式事務仍然是可以生效的,代碼都一樣也沒改過,爲什麼在預生產環境就不行了,真是見了鬼了。

後來就在思考預生產環境和自己在本地上測試有什麼區別麼,本地測試時,兩個服務之間的調用feign裏面的url填寫的是ip:port,直接就去訪問了指定的服務,但是預生產環境中使用的域名,也就是說請求會根據域名通過nginx轉發到對應服務,假設第一次請求的B1服務,第二次轉發請求的是B2服務,不在一個服務上。有同事就提出了,會不會就和這個B服務有集羣有關。但是還是感覺那裏不對勁,因爲聽說我們的預生產環境B雖然做了集羣,但是這幾個B連接的數據源依舊是同一個數據源,不過看起來說的也好像很有道理的樣子。

後來啊,就打算在測試環境的服務器試一下,因爲畢竟測試服務器沒有什麼集羣,也是一個A和一個B,他們之間的調用是通過域名而並非ip:port,本以爲會成功的,結果卻很悲催,在B服務沒有做集羣的情況下,分佈式事務仍然是不生效的,那看起來好像和集羣不集羣沒啥關係啊,畢竟測試環境的服務就是一對一,每次也只會訪問那一個B,仍然還不生效,到底發生了啥呢。

一時間沒有轍了,只有把那個feign的url統一改成了ip:port方式以解決燃眉之急,但是這樣並不好,畢竟這意味着上了生產環境A就固定的去調用其中一個B了,那集羣就沒有任何意義了。

大約過了一段時間,有同事說把nginx的負載均衡策略改成ip_hash方式就沒問題了,但這種方式沒有人去驗證,我也沒這個權限去改服務器的nginx.conf去驗證,因爲在測試環境1對1的情況下都沒辦法保證讓分佈式事務生效,那即使改成ip_hash又能如何呢?

大體上將的就是nginx對帶下劃線的頭請求進行限制,會把它過濾到,這也就是造成nginx轉發時帶“_”的請求會丟失的情況的產生。後來感覺發現了新大陸,seata分佈式事務的事務id的格式是TX_XID,那麼在TM拿到XID去調用RM時,會不會發生XID的丟失,導致RM沒有辦法將自己的分支事務註冊到那個XID所代表的全局事務的下面,因此也不會被管理呢,自然不生效了。所以只要將nginx的那個對下劃線的請求過濾處理掉就好了。

想到後,激動不已,想在服務器上模擬問題發生及解決,但是項目的nginx.conf豈容我這種小開發隨意改動!沒事,反正自己暫時無事,於是在自己電腦上寫了幾個服務demo,轉發用的是我自己虛擬機裏面的nginx,自己也整了個seata-server運行起來,把各項配置都配好,開始模擬,果然,服務器上的問題被我模擬出來了,接下來就是將nginx裏面的對下劃線的過濾去除,去除方式就是在nginx.conf的http部分添加一行:underscores_in_headers on;再次測試,成功,url寫成域名也沒有關係了。

以下是我的模擬情況記錄表:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章