[TOC]
引言
? 在先前發(fā)布的文章中,我們構(gòu)建了RPC底層數(shù)據(jù)傳輸?shù)幕A(chǔ)設(shè)計并實(shí)現(xiàn)了其功能(詳盡代碼與深入分析可參閱《實(shí)戰(zhàn)高效RPC方案在嵌入式環(huán)境中的應(yīng)用與揭秘》)。本文將繼續(xù)以此為基礎(chǔ)秃流,探討如何通過分層封裝來提升RPC框架的易用性苟跪,旨在提供更便捷和正式的使用接口。
概述
? 在之前的文章中,我們闡述了結(jié)合共享內(nèi)存與環(huán)形緩沖區(qū)技術(shù)箍邮,設(shè)計并實(shí)現(xiàn)了一種創(chuàng)新的共享環(huán)形緩沖區(qū)機(jī)制棒动,用以支持RPC進(jìn)程間高效的數(shù)據(jù)請求與響應(yīng)交互糙申。本篇文章將進(jìn)一步依托此共享環(huán)形緩沖區(qū)的核心架構(gòu),專注于RPC框架的接口層次封裝船惨,力求精簡對外接口柜裸,減輕使用者負(fù)擔(dān),從而實(shí)現(xiàn)實(shí)現(xiàn)服務(wù)進(jìn)程與RPC框架之間的無縫集成與簡便應(yīng)用粱锐。
需求
? 針對專為嵌入式Linux環(huán)境定制的小型RPC框架疙挺,其核心功能需求可概括為以下幾點(diǎn),旨在實(shí)現(xiàn)高效怜浅、靈活且易于集成的遠(yuǎn)程通信解決方案:
-
自動服務(wù)注冊與發(fā)現(xiàn)
框架應(yīng)在服務(wù)端啟動時自動完成RPC接口的注冊铐然,同時,客戶端需能動態(tài)識別并連接至可用服務(wù)端口恶座,無縫調(diào)用指定服務(wù)接口搀暑。 -
極致性能與低延遲通訊
強(qiáng)調(diào)從客戶端請求發(fā)出到接收服務(wù)端響應(yīng)的全鏈路快速響應(yīng),確保過程無明顯阻塞跨琳,適合實(shí)時性要求高的嵌入式應(yīng)用自点。 -
泛型數(shù)據(jù)序列化支持
支持豐富數(shù)據(jù)類型的參數(shù)傳遞,通過高效的序列化與反序列化機(jī)制脉让,保障各類數(shù)據(jù)在調(diào)用過程中的準(zhǔn)確無損傳輸樟氢。 -
高度抽象與易用性設(shè)計
框架設(shè)計應(yīng)隱藏復(fù)雜的數(shù)據(jù)通信邏輯,為開發(fā)者提供簡潔直觀的API接口侠鳄,使得遠(yuǎn)程方法調(diào)用如同調(diào)用本地函數(shù)一樣直接和自然埠啃。
注:由于是針對嵌入式環(huán)境定制的RPC框架設(shè)計,重點(diǎn)聚焦于核心實(shí)用功能伟恶,鑒于資源限制與特定場景需求碴开,我們將注意力集中于以下基本需求,暫不涵蓋如數(shù)據(jù)加密等高級安全特性和跨語言交互能力。
類圖
? 針對上述需求的分析潦牛,以及RPC功能的理解眶掌。初步可將其分為6個類實(shí)現(xiàn):BindingHub
、BindInterface
巴碗、Binder
朴爬、IBinder
、Parcel
橡淆、SharedRingBuffer
召噩。類圖如下:
BindingHub (Bind中央?yún)f(xié)調(diào)器)
該類核心職責(zé)在于維護(hù)一個全局的服務(wù)注冊表,其中記錄了所有服務(wù)端的遠(yuǎn)程調(diào)用元數(shù)據(jù)逸爵。其作為獨(dú)立的服務(wù)運(yùn)行在后臺具滴,扮演著信息維護(hù)的角色,向客戶端提供查詢服務(wù)端遠(yuǎn)程入口能力师倔。SharedRingBuffer (數(shù)據(jù)傳輸類)
構(gòu)成數(shù)據(jù)傳輸基礎(chǔ)設(shè)施的關(guān)鍵組件构韵,利用先進(jìn)的共享內(nèi)存技術(shù)結(jié)合高效的環(huán)形緩沖區(qū)設(shè)計,極大提升了進(jìn)程間數(shù)據(jù)傳輸?shù)乃俾逝c效率。此組件減少了系統(tǒng)調(diào)用的開銷,特別是在資源受限的嵌入式環(huán)境中铡溪,其輕量級特性和高性能表現(xiàn)尤為顯著。Parcel (數(shù)據(jù)封裝類)
該類是數(shù)據(jù)封裝與傳輸?shù)暮诵膶?shí)現(xiàn)显拳,負(fù)責(zé)數(shù)據(jù)的序列化與反序列化操作,確保信息在不同進(jìn)程間準(zhǔn)確無誤地流動抖单。通過集成SharedRingBuffer
,它不僅高效地利用共享內(nèi)存與環(huán)形緩沖區(qū)技術(shù)完成數(shù)據(jù)交換遇八,還實(shí)現(xiàn)了數(shù)據(jù)交互的同步控制矛绘,增強(qiáng)了傳輸?shù)目煽啃耘c一致性。Binder (服務(wù)端交互標(biāo)識)
在服務(wù)端側(cè)刃永,該類扮演數(shù)據(jù)傳輸控制器的角色货矮,依據(jù)服務(wù)標(biāo)識生成對應(yīng)的rspParcel
和reqParcel
實(shí)例與客戶端通信。此類接口封裝在BindInterface
中斯够,服務(wù)側(cè)代碼無感知囚玫。IBinder (客戶端交互標(biāo)識)
在客戶端側(cè),負(fù)責(zé)依據(jù)服務(wù)名稱獲取與之相匹配的rspParcel
和reqParcel
實(shí)例與服務(wù)端通信读规。此類接口封裝在BindInterface
中抓督,客戶側(cè)代碼無感知。BindInterface (Bind初始化接口類)
此接口定義了RPC框架與外部應(yīng)用交互的邊界束亏,為應(yīng)用程序提供簡潔的初始化接口铃在。無論是服務(wù)端部署還是客戶端查詢適配,都通過此類返回類型安全的Binder
與IBinder
實(shí)例。
源碼實(shí)現(xiàn)
編程環(huán)境
① 編譯環(huán)境: Linux環(huán)境
② 語言: C++語言
接口定義
- BindingHub (Bind中央?yún)f(xié)調(diào)器)
class BindingHub
{
public:
~BindingHub();
static BindingHub* GetInstance();
int32_t HandleMsgLoop();
private:
BindingHub();
int32_t EnvReady(const std::string& srvName);
int32_t MsgResponseAddService();
int32_t MsgResponseRemoveService();
int32_t MsgResponseGetService();
private:
using HandleFunction = int32_t (BindingHub::*)(void);
std::map<std::string, BinderInfo> mBinderMap;
std::map<int32_t, HandleFunction> mHandleFuncs;
};
BindHub
維護(hù)了一個全局服務(wù)注冊表mBinderMap
定铜,用于實(shí)現(xiàn)進(jìn)程間服務(wù)的高效發(fā)現(xiàn)與通信阳液。分為如下步驟描述:
步驟一:服務(wù)注冊過程
① 初始化請求:
當(dāng)一個服務(wù)進(jìn)程欲將其服務(wù)注冊至系統(tǒng)中時,首先向BindHub
發(fā)起注冊請求揣炕。請求中包含了服務(wù)的唯一標(biāo)識(進(jìn)程名)帘皿,作為服務(wù)辨識的基礎(chǔ)信息。
② 分配唯一標(biāo)識:
接收到注冊請求后畸陡,BindHub
會隨即為該服務(wù)生成一個隨機(jī)的鹰溜、唯一的key
值。用于為每個服務(wù)分配一個系統(tǒng)內(nèi)的代理標(biāo)識罩锐,便于后續(xù)的匿名化調(diào)用與管理奉狈。
③ 建立映射關(guān)系:
將生成的key
與服務(wù)端進(jìn)程名綁定,緩存至mBinderMap
中涩惑。此步驟用于確定服務(wù)名與key
之間的綁定關(guān)系仁期,為后續(xù)查找與調(diào)用服務(wù)奠定了基礎(chǔ)。
④ 響應(yīng)確認(rèn):
完成映射關(guān)系的建立后竭恬,BindHub
將生成的key
與服務(wù)名一并返回給服務(wù)端進(jìn)程跛蛋。服務(wù)端進(jìn)程以key
和服務(wù)名,創(chuàng)建共享內(nèi)存和信號量痊硕,作為通信憑證赊级,為即將到來的客戶端請求做好準(zhǔn)備。步驟二:服務(wù)獲取與通信
① 客戶端請求服務(wù):
客戶端進(jìn)程啟動服務(wù)調(diào)用前岔绸,需先通過BindHub
請求指定服務(wù)理逊。此請求中需包含服務(wù)的名稱。
② 查詢服務(wù)信息:
接收到客戶端的請求后盒揉,BindHub
在其維護(hù)的mBinderMap
中依據(jù)服務(wù)名進(jìn)行查找晋被,獲取與該服務(wù)關(guān)聯(lián)的唯一key
。
③ 返回通信憑證:
查詢到key
后刚盈,BindHub
向客戶端返回該服務(wù)的name
與對應(yīng)的key
羡洛。這兩個元素共同構(gòu)成了客戶端與服務(wù)端通信的憑證,允許客戶端直接且安全地與目標(biāo)服務(wù)建立連接藕漱。
④ 建立直接通信:
客戶端利用獲得的name
和key
欲侮,可直接與服務(wù)端進(jìn)程建立點(diǎn)對點(diǎn)通信鏈路(共享內(nèi)存和信號量),無需再經(jīng)過BindHub
中介肋联,從而實(shí)現(xiàn)高效的進(jìn)程間通信威蕉。
SharedRingBuffer (數(shù)據(jù)傳輸類)
此類為共享環(huán)形緩沖區(qū),用于存儲通信數(shù)據(jù)橄仍,之前文章有詳細(xì)說明忘伞,這里不再細(xì)說。Parcel (數(shù)據(jù)封裝類)
此類用實(shí)現(xiàn)數(shù)據(jù)序列化和反序列化,然后通過SharedRingBuffer
傳輸氓奈,業(yè)務(wù)比較簡單翘魄。
class Parcel
{
public:
Parcel(const std::string& path, int key, bool master);
~Parcel();
Parcel(const Parcel& other) = delete;
Parcel& operator=(const Parcel& other) = delete;
Parcel(Parcel&& other) = delete;
Parcel& operator=(Parcel&& other) = delete;
int Wait();
int Post();
int WriteBool(bool value);
int ReadBool(bool& value);
int WriteInt(int value);
int ReadInt(int& value);
int WriteString(const std::string& value);
int ReadString(std::string& value);
int WriteData(void* data, int size);
int ReadData(void* data, int& size);
private:
bool mMaster;
int mShmKey;
sem_t* mSem ;
std::string mShmPath;
SharedRingBuffer* mRingBuffer;
};
-
Binder (服務(wù)端交互標(biāo)識)
? 此類作用于服務(wù)端,通過服務(wù)名name
和key
生成兩個Parecl
實(shí)例:reqParcel
和rspParcel
舀奶。作為通信橋梁與客戶端進(jìn)行交互暑竟。
class Binder
{
public:
Binder(const std::string& name, int key) : mKey(key), mName(name) {};
~Binder() {};
int32_t GetParcel(std::shared_ptr<Parcel>& reqParcel, std::shared_ptr<Parcel>& rspParcel);
private:
int32_t mKey;
std::string mName;
};
-
IBinder (客戶端交互標(biāo)識)
? 此類作用于客戶端,作用和Binder
相似育勺,只是生成的是用于與服務(wù)端交互的兩個Parcel
實(shí)例但荤。
? 客戶端的reqParecel
負(fù)責(zé)向服務(wù)端rspParcel
發(fā)數(shù)據(jù),客戶端的rspParcel
負(fù)責(zé)接收服務(wù)端reqParecel
的應(yīng)答消息涧至。從而實(shí)現(xiàn)客戶端與服務(wù)端的全雙工通信腹躁。
class IBinder
{
public:
IBinder(const std::string& name, int key) : mKey(key), mName(name) {};
~IBinder() {};
int32_t GetParcel(std::shared_ptr<Parcel>& reqParcel, std::shared_ptr<Parcel>& rspParcel);
private:
int mKey;
std::string mName;
};
-
BindInterface (Bind初始化接口類)
? 該類的作用在于抽象化Binder
和IBinder
接口的復(fù)雜性,用戶僅需通過初始化方法Initialize
即可輕松獲得預(yù)配置的Parcel實(shí)例南蓬,進(jìn)而開展數(shù)據(jù)交換操作纺非。
class BindInterface
{
public:
~BindInterface() = default;
static BindInterface* GetInstance();
bool InitializeServiceBinder(const std::string& srvName, std::shared_ptr<Parcel>& pReqParcel, std::shared_ptr<Parcel>& pRspParcel);
bool InitializeClientBinder(const std::string& srvName, std::shared_ptr<Parcel>& pReqParcel, std::shared_ptr<Parcel>& pRspParcel);
private:
BindInterface() = default;
std::shared_ptr<Binder> AddService(const std::string& name);
std::shared_ptr<IBinder> GetService(const std::string& name);
int32_t RemoveService(const std::string& name);
};
測試驗(yàn)證
- 測試代碼
篇幅有限,這里只列舉關(guān)鍵部分代碼:
int Client()
{
std::shared_ptr<Parcel> pReqParcel = nullptr;
std::shared_ptr<Parcel> pRspParcel = nullptr;
BindInterface::GetInstance()->InitializeClientBinder(SERVICE_NAME, pReqParcel, pRspParcel);
if (pReqParcel == nullptr || pRspParcel == nullptr) {
SPR_LOGE("GetParcel failed!\n");
return -1;
}
...
pReqParcel->WriteInt(CMD_SUM);
pReqParcel->WriteInt(10);
pReqParcel->WriteInt(20);
pReqParcel->Post();
int sum = 0, ret = 0;
pRspParcel->Wait();
pRspParcel->ReadInt(sum);
pRspParcel->ReadInt(ret);
SPR_LOGD("sum = %d, ret = %d\n", sum, ret);
...
}
...
int Server()
{
std::shared_ptr<Parcel> pReqParcel = nullptr;
std::shared_ptr<Parcel> pRspParcel = nullptr;
BindInterface::GetInstance()->InitializeServiceBinder(SERVICE_NAME, pReqParcel, pRspParcel);
if (pReqParcel == nullptr || pRspParcel == nullptr) {
SPR_LOGE("GetParcel failed\n");
return -1;
}
do {
int cmd = 0;
pReqParcel->Wait();
pReqParcel->ReadInt(cmd);
switch(cmd)
{
case CMD_SUM:
{
SPR_LOGD("CMD_SUM\n");
int a = 0, b = 0;
pReqParcel->ReadInt(a);
pReqParcel->ReadInt(b);
int sum = a + b;
pRspParcel->WriteInt(sum);
pRspParcel->WriteInt(0);
pRspParcel->Post();
break;
}
default:
{
SPR_LOGE("Unknown cmd: %d\n", cmd);
break;
}
}
} while(1);
return 0;
}
- 測試結(jié)果
$ ./debugbinder
------------------------------------------------------------------
Usage:
0: CMD_TEST
1: CMD_SUM
q: Quit
------------------------------------------------------------------
146 DebugBinder D: Input:1
173 DebugBinder D: sum = 30, ret = 0
這里只是一個簡單測試赘方,客戶端發(fā)起請求烧颖,并同步獲取服務(wù)端返回值30,初步驗(yàn)證RPC功能OK窄陡。
總結(jié)
先前探討了
SharedRingbuffer
在數(shù)據(jù)傳輸中的低層機(jī)制炕淮。本文章重心主要聚焦軟件設(shè)計,旨在簡化RPC框架的使用體驗(yàn)跳夭,提升易用性涂圆。RPC通信核心在于參數(shù)傳遞與返回值的高效同步。該過程依托共享內(nèi)存與信號量技術(shù)币叹,確保數(shù)據(jù)流暢傳輸與操作同步润歉,提升交互效率。
RPC框架的實(shí)踐不僅為項(xiàng)目提供便捷工具套硼,也能夠深入了解RPC技術(shù)底層邏輯與原理卡辰,從技術(shù)和設(shè)計的層面提升自己胞皱。