您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)Seata RPC模塊的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
RPC 模塊是我最初研究 Seata 源碼開始的地方,因此我對 Seata 的 RPC 模塊有過一些深刻研究,在我研究了一番后,發(fā)現(xiàn) RPC 模塊中的代碼需要進(jìn)行優(yōu)化,使得代碼更加優(yōu)雅,交互邏輯更加清晰易懂,本著 “讓天下沒有難懂的 RPC 通信代碼” 的初衷,我開始了 RPC 模塊的重構(gòu)之路。
這里建議想要深入了解 Seata 交互細(xì)節(jié)的,不妨從 RPC 模塊的源碼入手,RPC 模塊相當(dāng)于 Seata 的中樞,Seata 所有的交互邏輯在 RPC 模塊中表現(xiàn)得淋漓盡致。
這次 RPC 模塊的重構(gòu)將會使得 Seata 的中樞變得更加健壯和易于解讀。
在 Seata 的舊版本中,RPC 模塊的整體結(jié)構(gòu)有點混亂,尤其是在各個類的繼承關(guān)系上,主要體現(xiàn)在:
直接在 Remoting 類繼承 Netty Handler,使得 Remoting 類與 Netty Handler 處理邏輯耦合在一起。
客戶端和服務(wù)端的 Reomting 類繼承關(guān)系不統(tǒng)一。
RemotingClient 被 RpcClientBootstrap 實現(xiàn),而 RemotingServer 卻被 RpcServer 實現(xiàn),沒有一個獨立的 ServerBootstrap,這個看起來關(guān)系非常混亂。
有些接口沒必要抽取出來,比如 ClientMessageSender、ClientMessageListener、ServerMessageSender 等接口,因這些接口會增加整體結(jié)構(gòu)繼承關(guān)系的復(fù)雜性。
針對上面發(fā)現(xiàn)的問題,在重構(gòu)過程中我大致做了如下事情:
將 Netty Handler 抽象成一個內(nèi)部類放在 Remoting 類中。
將 RemotingClient 為客戶端頂級接口,定義客戶端與服務(wù)端交互的基本方法,抽象一層 AbstractNettyRemotingClient,下面分別有 RmNettyRemotingClient、TmNettyRemotingClient;將 RemotingServer 為服務(wù)端頂級接口,定義服務(wù)端與客戶端交互的基本方法,實現(xiàn)類 NettyRemotingServer。
同時將 ClientMessageSender、ClientMessageListener、ServerMessageSender 等接口方法歸入到 RemotingClient、RemotingServer 中,由 Reomting 類實現(xiàn) RemotingClient、RemotingServer,統(tǒng)一 Remoting 類繼承關(guān)系。
新建 RemotingBootstrap 接口,客戶端和服務(wù)端分別實現(xiàn) NettyClientBootstrap、NettyServerBootstrap,將引導(dǎo)類邏輯從 Reomting 類抽離出來。
在最新的 RPC 模塊中的繼承關(guān)系簡單清晰,用如下類關(guān)系圖表示:
AbstractNettyRemoting:Remoting 類的最頂層抽象,包含了客戶端和服務(wù)端公用的成員變量與公用方法,擁有通用的請求方法(文章后面會講到),Processor 處理器調(diào)用邏輯(文章后面會講到)。
RemotingClient:客戶端最頂級接口,定義客戶端與服務(wù)端交互的基本方法。
RemotingServer:服務(wù)端最頂級接口,定義服務(wù)端與客戶端交互的基本方法。
AbstractNettyRemotingClient:客戶端抽象類,繼承 AbstractNettyRemoting 類并實現(xiàn)了 RemotingClient 接口。
NettyRemotingServer:服務(wù)端實現(xiàn)類,繼承 AbstractNettyRemoting 類并實現(xiàn)了 RemotingServer 接口。
RmNettyRemotingClient:Rm 客戶端實現(xiàn)類,繼承 AbstractNettyRemotingClient 類。
TmNettyRemotingClient:Tm 客戶端實現(xiàn)類,繼承 AbstractNettyRemotingClient 類。
同時將客戶端和服務(wù)端的引導(dǎo)類邏輯抽象出來,如下類關(guān)系圖表示:
RemotingBootstrap:引導(dǎo)類接口,有 start 和 stop 兩個抽象方法。
NettyClientBootstrap:客戶端引導(dǎo)實現(xiàn)類。
NettyServerBootstrap:服務(wù)端引導(dǎo)實現(xiàn)類。
解耦處理邏輯即是將 RPC 交互的處理邏輯從 Netty Handler 中抽離出來,并將處理邏輯抽象成一個個 Processor,為什么要這么做呢?我大致講下現(xiàn)在存在的一些問題:
Netty Handler 與 處理邏輯是糅合在一起的,由于客戶端與服務(wù)端都共用了一套處理邏輯,因此為了兼容更多的交互,在處理邏輯中你可以看到非常多難以理解的判斷邏輯。
在 Seata 的交互中有些請求是異步處理的,也有一些請求是同步處理的,但是在舊的處理代碼邏輯中對同步異步處理的表達(dá)非常隱晦,而且難以看明白。
無法從代碼邏輯當(dāng)中清晰地表達(dá)出請求消息類型與對應(yīng)的處理邏輯關(guān)系。
在 Seata 后面的更新迭代中,如果不將處理處理邏輯抽離出來,這部分代碼想要增加新的交互邏輯,將會非常困難。
在將處理邏輯從 Netty Handler 進(jìn)行抽離之前,我們先梳理一下 Seata 現(xiàn)有的交互邏輯。
RM 客戶端請求服務(wù)端的交互邏輯:
TM 客戶端請求服務(wù)端的交互邏輯:
服務(wù)端請求 RM 客戶端的交互邏輯:
從以上的交互圖中可以清晰地看到了 Seata 的交互邏輯。
客戶端總共接收服務(wù)端的消息:
1)服務(wù)端請求消息
BranchCommitRequest、BranchRollbackRequest、UndoLogDeleteRequest
2)服務(wù)端響應(yīng)消息
RegisterRMResponse、BranchRegisterResponse、BranchReportResponse、GlobalLockQueryResponse
RegisterTMResponse、GlobalBeginResponse、GlobalCommitResponse、GlobalRollbackResponse、GlobalStatusResponse、GlobalReportResponse
HeartbeatMessage(PONG)
服務(wù)端總共接收客戶端的消息:
1)客戶端請求消息
RegisterRMRequest、BranchRegisterRequest、BranchReportRequest、GlobalLockQueryRequest
RegisterTMRequest、GlobalBeginRequest、GlobalCommitRequest、GlobalRollbackRequest、GlobalStatusRequest、GlobalReportRequest
HeartbeatMessage(PING)
2)客戶端響應(yīng)消息
BranchCommitResponse、BranchRollbackResponse
基于以上的交互邏輯分析,我們可以將處理消息的邏輯抽象成若干個 Processor,一個 Processor 可以處理一個或者多個消息類型的消息,只需在 Seata 啟動時注冊將消息類型注冊到 ProcessorTable 中即可,形成一個映射關(guān)系,這樣就可以根據(jù)消息類型調(diào)用對應(yīng)的 Processor 對消息進(jìn)行處理,用如下圖表示:
在抽象 Remoting 類中定一個 processMessage 方法,方法邏輯是根據(jù)消息類型從 ProcessorTable 中拿到消息類型對應(yīng)的 Processor。
這樣就成功將處理邏輯從 Netty Handler 中徹底抽離出來了,Handler#channelRead 方法只需要調(diào)用 processMessage 方法即可,且還可以靈活根據(jù)消息類型動態(tài)注冊 Processor 到 ProcessorTable 中,處理邏輯的可擴(kuò)展性得到了極大的提升。
以下是 Processor 的調(diào)用流程:
1)客戶端
RmBranchCommitProcessor:處理服務(wù)端全局提交請求。
RmBranchRollbackProcessor:處理服務(wù)端全局回滾請求。
RmUndoLogProcessor:處理服務(wù)端 undo log 刪除請求。
ClientOnResponseProcessor:客戶端處理服務(wù)端響應(yīng)請求,如:BranchRegisterResponse、GlobalBeginResponse、GlobalCommitResponse 等。
ClientHeartbeatProcessor:處理服務(wù)端心跳響應(yīng)。
2)服務(wù)端
RegRmProcessor:處理 RM 客戶端注冊請求。
RegTmProcessor:處理 TM 客戶端注冊請求。
ServerOnRequestProcessor:處理客戶端相關(guān)請求,如:BranchRegisterRequest、GlobalBeginRequest、GlobalLockQueryRequest 等。
ServerOnResponseProcessor:處理客戶端相關(guān)響應(yīng),如:BranchCommitResponse、BranchRollbackResponse 等。
ServerHeartbeatProcessor:處理客戶端心跳響應(yīng)。
下面我以 TM 發(fā)起全局事務(wù)提交請求為例子,讓大家感受下 Processor 在整個交互中所處的位置:
在 Seata 的舊版本當(dāng)中,RPC 的請求方法也是欠缺優(yōu)雅,主要體現(xiàn)在:
請求方法過于雜亂無章,沒有層次感。
sendAsyncRequest 方法耦合的代碼太多,邏輯過于混亂,客戶端與服務(wù)端都共用了一套請求邏輯,方法中決定是否批量發(fā)送是根據(jù)參數(shù) address 是否為 null 決定,決定是否同步請求是根據(jù) timeout 是否大于 0 決定,顯得極為不合理,且批量請求只有客戶端有用到,服務(wù)端并沒有批量請求,共用一套請求邏輯還會導(dǎo)致服務(wù)端異步請求也會創(chuàng)建 MessageFuture 放入 futures 中。
請求方法名稱風(fēng)格不統(tǒng)一,比如客戶端 sendMsgWithResponse,服務(wù)端卻叫 sendSyncRequest;
針對以上舊版本 RPC 請求方法的各種缺點,我作了以下改動:
將請求方法統(tǒng)一放入 RemotingClient、RemotingServer 接口當(dāng)中,并作為頂級接口;
分離客戶端與服務(wù)端請求邏輯,將批量請求邏輯單獨抽到客戶端相關(guān)請求方法中,使得是否批量發(fā)送不再根據(jù)參數(shù) address 是否為 null 決定;
由于 Seata 自身的邏輯特點,客戶端服務(wù)端請求方法的參數(shù)無法統(tǒng)一,可通過抽取通用的同步/異步請求方法,客戶端和服務(wù)端根據(jù)自身請求邏輯特點實現(xiàn)自身的同步/異步請求邏輯,最后再調(diào)用通用的同步/異步請求方法,使得同步/異步請求都有明確的方法,不再根據(jù) timeout 是否大于 0 決定;
統(tǒng)一請求名稱風(fēng)格。
最終,Seata RPC 的請求方法終于看起來更加優(yōu)雅且有層次感了。
同步請求:
異步請求:
類目錄調(diào)整:RPC 模塊目錄中還有一個 netty 目錄,也可以從目錄結(jié)構(gòu)中發(fā)現(xiàn) Seata 的初衷是兼容多個 RPC 框架,目前只實現(xiàn)了 netty,但發(fā)現(xiàn) netty 模塊中有些類并不 ”netty“,且 RPC 跟目錄的類也并不通用,因此需要將相關(guān)類的位置進(jìn)行調(diào)整。
某些類重新命名,比如 netty 相關(guān)類包含 「netty」。
最終 RPC 模塊看起來是這樣的:
看完上述內(nèi)容,你們對Seata RPC模塊的示例分析有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。