【橘子分布式】Thrift RPC(編程篇)

一、簡介

之前我們研究了一下thrift的一些知識,我們知道他是一個rpc框架,他作為rpc自然是提供了客戶端到服務端的訪問以及兩端數據傳輸的消息序列化,消息的協議解析和傳輸,所以我們今天就來了解一下他是如何實現這些功能,并且如何在實際代碼中使用。
我們需要搭建環境。

1. 安裝Thrift 作用:把IDL語言描述的接口內容,生成對應編程語言的代碼,簡化開發。我們已經介紹了在mac如何使用brew安裝了。
2. 引入依賴    作用:引入thrift針對于某一種編程語言的封裝 (網絡通信 協議【序列化】)

二、結構和模塊

1、Thrift核心對象

1. TTransport作用:底層封裝了rpc的網絡通信能力,具體實現有下面三種TSocket 阻塞IO通信TNonblockdingTransport 非阻塞網絡通信TFramedTransport 加入了封幀的操作 (壓縮后 數據邊界問題)  
2. TProtocol作用:底層封裝了rpc的關于協議的處理 (序列化方式)TBinayProtocol 二進制進行序列化TCompactProtocol 壓縮方式 處理二進制 TJSONProtocol  JSON進行序列化這里需要說明一下,當我們的序列化方式走了TCompactProtocol壓縮處理的時候,你最好使用TFramedTransport來處理網絡通信,因為一旦數據開啟了壓縮,那就可能傳輸很大量的數據,數據的模樣和原始的不同,此時可能因為緩沖區大小等等出現半包粘包問題。需要解決封幀。其他的方式一般沒啥,因為rpc數據量不會很大,一般沒有半包粘包,具體問題具體分析吧。而且你能看到他提供多種協議,并不是用的http協議這個最常見的,這個沒啥說的,高性能的網絡協議,一般都不用http,http的優勢是簡單通用。
3. TProcessor作用:進行業務處理,把通信數據 和 業務功能整合在一起。換句話說我們的業務代碼就在這里,其實就是把數據拿過來了,開始業務處理了。
4. TServer 服務端服務端就是用上面的三個組件,組合起來實現具體的服務端邏輯,根據不同的實現有異步的有同步的等等,并且發布服務出去。

2、項目結構

我們在編碼之前需要明確一點,就是作為rpc(Remote Procedure Call),遠程調用必然存在一個服務的提供者和一個服務的調用者。所以我們必須有的兩個模塊是rpc-server和rpc-client。
在實際開發的時候,我們比如說服務調用者和提供者可能有相同的數據比如User這個類,那你不能在兩邊各自都寫一個吧,肯定是寫一份維護就好了。所以我們還需要一個rpc-common的模塊,用來聲明那些公用的類,包括一些公用的實體類,還有發起rpc調用的service代理類等等。
于是我們就抽象出了三個模塊。

1. thrift-client  代表的是服務的調用者,客戶端要想本地方法那樣調用遠端方法,底層必然是代理。
2. thrift-server  代表的是服務的提供者2.1. 實現服務接口 :idl語言生成的2.2. 創建服務端代碼
3. thrift-common  RPC編程共有的內容 1,實體類型 2,服務接口3.1. 通過IDL語言 定義 client與服務端 共用的數據類型 和 服務接口 3.2. client server端需要引入 common模塊

我們這里是以同一個工程下的模塊化的形式搭建項目,實際上你也可以單獨創建不同的工程。

3、引入依賴

我們這里是一個項目多個模塊,所以我直接在父級項目引入了。

<modules><module>thrift-client</module><module>thrift-server</module><module>thrift-common</module>
</modules><properties><thrift.version>0.22.0</thrift.version><javax.annotation.version>1.3.2</javax.annotation.version><slf4j-api.version>1.7.32</slf4j-api.version><logback-classic.version>1.2.9</logback-classic.version><junit.version>3.8.1</junit.version><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><!-- 引入thrift針對于某一種編程語言的封裝,java就是maven管理jar,你要是用py開發,那就是pip (網絡通信 協議【序列化】)--><dependency><groupId>org.apache.thrift</groupId><artifactId>libthrift</artifactId><version>${thrift.version}</version></dependency><dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>${javax.annotation.version}</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>${slf4j-api.version}</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>${logback-classic.version}</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version><scope>test</scope></dependency></dependencies>

4、構建thrift文件

此時我們需要構建一些用來編程的代碼。首先我們的實體類和service要放在common里面,然后client和server引用common即可。我們實現一個保存用戶User和根據用戶名查看用戶的實現。
因為我們是java開發,所以我們其實目標就是一個User類,和一個service類,service里面提供void save(User user)和User getUserByName(String name)這樣的東西。所以我們就基于這個構建IDL,然后用thrift翻譯出來java代碼即可。根據我們昨天對于IDL的語法梳理,可以這么編寫.thrift文件。
名稱為rpc-thrift.thrift

# 用來構建rpc的thrift文件namespace java com.levi
struct User {1: required string name,2: required string password
}
service UserService {/*** 注冊用戶* @param user 用戶信息* @return 注冊結果*/void registerUser(1: User user)/*** 獲取用戶信息* @param name 用戶名稱* @return 用戶信息*/User getUser(1: string name)
}

然后我們進入到這個文件的目錄,使用如下命令:

thrift --gen java rpc-thrift.thrift

生成如下結果:
在這里插入圖片描述
我們把這些轉移到類目錄下即可,然后刪除這些即可。
在這里插入圖片描述
然后我們maven install,再把common這個依賴給client和server模塊引入即可。

<dependencies><dependency><groupId>com.levi</groupId><artifactId>thrift-common</artifactId><version>1.0-SNAPSHOT</version></dependency>
</dependencies>

此時我們就完成了對應的項目結構搭建,下面我們來實現業務。

三、業務實現

1、服務端實現

package com.levi.service;import com.levi.User;
import com.levi.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 注意這里實現的接口不是UserService,而是UserService.Iface,這是thrift生成的接口* 按照thrift的規范,每個接口都有一個對應的Iface接口,用于封裝接口的方法,方便調用*/
public class UserServiceImpl implements UserService.Iface{Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);@Overridepublic void registerUser(User user) {logger.info("register user:{}",user);}@Overridepublic User getUser(String name) {logger.info("get user:{}",name);return new User(name,"123456");}
}

有了業務類之后,就啟動服務端。

package com.levi;import com.levi.service.UserServiceImpl;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;public class TestServer {public static void main(String[] args) {// 創建服務端,監聽端口8888,其實就是對應的TTransport組件,通信功能在此封裝try (TServerSocket tServerSocket = new TServerSocket(8888);){// 協議工廠,用于封裝協議,比如二進制協議、JSON協議等,此處使用二進制協議,其實對應的就是TProtocol組件,協議功能在此封裝TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();// 處理器工廠,用于封裝處理器,比如UserService.Processor,其實對應的就是TProcessor組件,處理器功能在此封裝。綁定我們的業務邏輯。UserService.Processor processor = new UserService.Processor(new UserServiceImpl());// 封裝參數TSimpleServer.Args arg = new TSimpleServer.Args(tServerSocket).protocolFactory(protocolFactory).processor(processor);// 構建服務端起動器,其實對應的就是TServer組件,服務端功能在此封裝,發布服務出去,等待客戶端連接TSimpleServer tSimpleServer = new TSimpleServer(arg);tSimpleServer.serve();}catch (Exception e){e.printStackTrace();}}
}

2、客戶端實現

package com.levi;import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class TestClient {private static Logger logger = LoggerFactory.getLogger(TestClient.class);public static void main(String[] args) {// 創建服務端,監聽端口8888,其實就是對應的TTransport組件,通信功能在此封裝try (TTransport tTransport = new TSocket("localhost", 8888);){// 協議工廠,用于封裝協議,比如二進制協議、JSON協議等,此處使用二進制協議,其實對應的就是TProtocol組件,協議功能在此封裝,和服務端保持一致TProtocol tProtocol = new TBinaryProtocol(tTransport);// rpc的本質,就是像調用本地服務一樣調用遠程服務,此處就是一個體現,但是本質都是代理模式,只不過這個代理thrift封裝了UserService.Client userService = new UserService.Client(tProtocol);tTransport.open();User user = userService.getUser("levi");logger.info("client get user is {}",user);}catch (Exception e){e.printStackTrace();}}
}

啟動服務端之后開始監聽,然后啟動客戶端。輸出如下:

client get user is User(name:levi, password:123456)

沒毛病。

四、重構

實際上開發的時候,我們一般是有些業務公用的,可能RPC調用在用其他的業務,所以最好是封裝隔離開,我們一般是在RPC這里直接調用,實在沒得用再寫。
于是我們把thrift的這個實現改為UserServiceImplRPC

package com.levi.service;import com.levi.User;
import com.levi.UserService;/*** 注意這里實現的接口不是UserService,而是UserService.Iface,這是thrift生成的接口* 按照thrift的規范,每個接口都有一個對應的Iface接口,用于封裝接口的方法,方便調用*/
public class UserServiceImplRPC implements UserService.Iface{@Overridepublic void registerUser(User user) {UserServiceImpl userService = new UserServiceImpl();userService.registerUser(user);}@Overridepublic User getUser(String name) {UserServiceImpl userService = new UserServiceImpl();return userService.getUser(name);}
}

常規的業務放在UserServiceImpl

package com.levi.service;import com.levi.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 注意這里實現的接口不是UserService,而是UserService.Iface,這是thrift生成的接口* 按照thrift的規范,每個接口都有一個對應的Iface接口,用于封裝接口的方法,方便調用*/
public class UserServiceImpl {Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);public void registerUser(User user) {logger.info("register user:{}",user);}public User getUser(String name) {logger.info("get user:{}",name);return new User(name,"123456");}
}

這樣方便維護也比較清晰。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/89167.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/89167.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/89167.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

清理C盤--辦法

c盤經常爆紅1、命令行2、屬性3、臨時文件

Java-71 深入淺出 RPC Dubbo 上手 父工程配置編寫 附詳細POM與代碼

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持續更新中&#xff01;&#xff08;長期更新&#xff09; AI煉丹日志-29 - 字節跳動 DeerFlow 深度研究框斜體樣式架 私有…

創客匠人:創始人 IP 打造的內核,藏在有效的精神成長里

當創始人 IP 成為企業增長的重要引擎&#xff0c;許多人急于尋找 “爆款公式”&#xff0c;卻忽略了一個更本質的問題&#xff1a;IP 的生命力&#xff0c;終究源于創始人的精神成長。創客匠人在深耕知識付費賽道的過程中&#xff0c;見證了無數案例&#xff1a;那些能持續實現…

GPT和MBR分區

GPT&#xff08;GUID分區表&#xff09;和MBR&#xff08;主引導記錄&#xff09;是兩種不同的磁盤分區表格式&#xff0c;用于定義硬盤上分區的布局、位置及啟動信息&#xff0c;二者在設計、功能和適用場景上有顯著差異。以下從多個維度詳細對比&#xff1a; 一、核心定義與起…

c#進階之數據結構(字符串篇)----String

1、String介紹首先我們得明白&#xff0c;string和String代表的實際上是同一個類型&#xff0c;string是C#中的關鍵字&#xff0c;代表String類型&#xff0c;因此我們直接來學習String類型。從官方的底層實現代碼可以看出&#xff0c;當前String類型實際上就是一個Char類型的聚…

快速排序遞歸和非遞歸方法的簡單介紹

基本思想為&#xff1a;任取待排序元素序列中 的某元素作為基準值&#xff0c;按照該排序碼將待排序集合分割成兩子序列&#xff0c;左子序列中所有元素均小于基準值&#xff0c;右 子序列中所有元素均大于基準值&#xff0c;然后最左右子序列重復該過程&#xff0c;直到所有元…

從零開始的云計算生活——第三十二天,四面楚歌,HAProxy負載均衡

目錄 一.HAProxy簡介 二.HAProxy特點和優點&#xff1a; 三.HAProxy保持會話的三種解決方法 四.HAProxy的balance 8種負載均衡算法 1&#xff09;RR&#xff08;Round Robin&#xff09; 2&#xff09;LC&#xff08;Least Connections&#xff09; 3&#xff09;SH&am…

策略模式及優化

策略模式&#xff08;Strategy Pattern&#xff09;是一種行為設計模式&#xff0c;其核心思想是將算法的定義與使用分離&#xff0c;使算法可以獨立于客戶端進行變化。它通過定義一系列算法&#xff0c;將每個算法封裝到獨立的類中&#xff0c;并使它們可以互相替換&#xff0…

微信小程序開發-桌面端和移動端UI表現不一致問題記錄

桌面端和移動端UI表現不一致零、引擎說明一、樣式不同1、text 單行&#xff1a;1.1 空格開發者工具不展示&#xff0c;手機/PC端正常1.2 正常展示省略號&#xff0c;需要2、點擊按鈕z-index: -1。webview - 桌面端不行&#xff0c; skyline - 移動端可以&#xff1b;3、其他說明…

極限狀態下函數開根號的計算理解(含示意圖)

遇到一個挺有意思的題做個記錄&#xff1a; 求曲線y (x21)(x2?1)0.5\frac{\left(x^{2}1\right)}{\left(x^{2}-1\right)^{0.5}}(x2?1)0.5(x21)?漸近線的條數 比較明顯的x 1是無定義點。但是在求極限的時候發現1和1-得到的極限值似乎不一樣。似乎是1是趨向于∞&#xff0c;1…

C++——模版(函數模版和類模版)

C 模板&#xff08;Templates&#xff09;完整介紹模板是 C 中一種強大的泛型編程機制&#xff0c;允許開發者編寫與類型無關的代碼&#xff0c;從而提高代碼的復用性和靈活性。通過模板&#xff0c;可以避免為不同數據類型重復編寫相似的函數或類&#xff0c;實現真正的代碼復…

Python之cv2:cv2(OpenCV,opencv-python)庫pip下載超時、下載失敗、無法下載的解決方案大全

Python之cv2&#xff1a;cv2(OpenCV&#xff0c;opencv-python)庫pip下載超時、下載失敗、無法下載的解決方案大全 在學習和使用 OpenCV&#xff08;Python 包名&#xff1a;opencv-python 或簡稱 cv2&#xff09;的過程中&#xff0c;很多初學者常常會遇到通過 pip install o…

asyncio 與 uvloop

事件循環 事件循環 協調所有協程執行的中央調度器&#xff0c;它通過非阻塞機制&#xff0c;實現并發執行多個異步任務。 事件循環是 異步編程的核心機制&#xff0c;用一句話概括就是&#xff1a; 事件循環不斷檢查任務隊列&#xff0c;一旦某個異步任務完成&#xff0c;它…

一文讀懂循環神經網絡(RNN)—語言模型+n元語法(1)

目錄 什么是語言模型&#xff1f; 語言模型的核心目的 一.量化文本的合理性 二.支持下游 NLP 任務 三. 語義和上下文依賴 一元語法、二元語法和三元語法詳解 核心概念&#xff1a;n-gram 模型 1. 一元語法&#xff08;Unigram&#xff09; 2. 二元語法&#xff08;Bigram…

DirectX12(D3D12)基礎教程九 間接繪制

在學習directx12 microsoft提供了很多示例&#xff0c;有簡單的也有復雜,下載網址&#xff1a;https://github.com/microsoft/DirectX-Graphics-Samples 本章對D3D12ExecuteIndirect 示例做了簡化&#xff0c;只保留間接繪制部分&#xff0c;刪除了計算著色器部分。 間接繪制…

fastApi連接數據庫

1&#xff1a;pip install tortoise-orm2&#xff1a;pip install aiomysql3&#xff1a;pip install asyncmy或者使用國內清華園pip install -i https://pypi.tuna.tsinghua.edu.cn/simple asyncmy4&#xff1a;pip install aerich通過 python -m 直接運行&#xff08;推薦&a…

Apache-web服務器環境搭建

目錄 實驗要求 思路總結 1.常規配置web服務 2.通過用戶主頁配置web服務 3.通過虛擬目錄配置web服務 4.添加DNS解析服務&#xff0c;訪問虛擬機域名&#xff1a; www.TestWeb.com 實驗要求 (ip 192.168.48.130) 1、常規配置web服務 2、通過用戶主頁配置web服務 3、通過虛…

Altium Designer 25 安裝與配置完整教程

本教程將帶您一步步完成 Altium Designer 25 的下載、安裝與激活配置 第一步&#xff1a;下載安裝包 首先&#xff0c;需要獲取 Altium Designer 25 的完整安裝程序。 &#x1f449; 下載鏈接&#xff1a; 百度網盤&#xff1a;百度網盤 請輸入提取碼 提取碼: dxei 夸克網盤…

【工具】AndroidStudio修改中文語言漢化

AndroidStudio修改中文語言漢化 https://github.com/sollyu/AndroidStudioChineseLanguagePackhttps://github.com/sollyu/AndroidStudioChineseLanguagePack

代碼隨想錄|圖論|15并查集理論基礎

并查集理論基礎 | 代碼隨想錄 并查集還是比較簡單的&#xff0c;只要搞清楚兩個事情&#xff1a; 并查集是干啥的&#xff1f;解決什么類型問題&#xff1f;并查集模板&#xff08;背下來&#xff09; 1、并查集是干啥的 并查集主要是兩個功能&#xff1a; 兩個元素添加到…