? RPC是遠(yuǎn)程調(diào)用系統(tǒng)簡(jiǎn)稱,它允許程序調(diào)用運(yùn)行在另一臺(tái)計(jì)算機(jī)上的過(guò)程,就像調(diào)用本地的過(guò)程一樣。RPC 實(shí)現(xiàn)了網(wǎng)絡(luò)編程的“過(guò)程調(diào)用”模型,讓程序員可以像調(diào)用本地函數(shù)一樣調(diào)用遠(yuǎn)程函數(shù)。最近在做的也是遠(yuǎn)程調(diào)用過(guò)程,所以通過(guò)重新梳理RPC來(lái)整理總結(jié)一下。
創(chuàng)新互聯(lián)公司的客戶來(lái)自各行各業(yè),為了共同目標(biāo),我們?cè)诠ぷ魃厦芮信浜?,從?chuàng)業(yè)型小企業(yè)到企事業(yè)單位,感謝他們對(duì)我們的要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。專業(yè)領(lǐng)域包括成都做網(wǎng)站、網(wǎng)站建設(shè)、外貿(mào)營(yíng)銷網(wǎng)站建設(shè)、電商網(wǎng)站開(kāi)發(fā)、微信營(yíng)銷、系統(tǒng)平臺(tái)開(kāi)發(fā)。? 項(xiàng)目來(lái)源:
GitHub - qicosmos/rest_rpc: modern C++(C++11), simple, easy to use rpc framework
目錄
一、RPC簡(jiǎn)介
1.1 簡(jiǎn)介
1.2 本地調(diào)用和遠(yuǎn)程調(diào)用的區(qū)別
1.3 RPC運(yùn)行的流程
1.4 小結(jié)
二、RPC簡(jiǎn)單實(shí)現(xiàn)
2.1 客戶端實(shí)現(xiàn)代碼
2.2 服務(wù)端代碼
三、加強(qiáng)版RPC(以“RPC簡(jiǎn)單實(shí)現(xiàn)”為基礎(chǔ))
3.1 加入錯(cuò)誤處理
3.2 加入網(wǎng)絡(luò)連接(socket)
3.3 加強(qiáng)并發(fā)性
3.4 加入容錯(cuò)機(jī)制(修改客戶端部分)
3.5 負(fù)載均衡
四、總結(jié)
? RPC指的是計(jì)算機(jī)A的進(jìn)程調(diào)用另外一臺(tái)計(jì)算機(jī)B的進(jìn)程,A上的進(jìn)程被掛起,B上被調(diào)用的進(jìn)程開(kāi)始執(zhí)行,當(dāng)B執(zhí)行完畢后將執(zhí)行結(jié)果返回給A,A的進(jìn)程繼續(xù)執(zhí)行。調(diào)用方可以通過(guò)使用參數(shù)將信息傳送給被調(diào)用方,然后通過(guò)傳回的結(jié)果得到信息。這些傳遞的信息都是被加密過(guò)或者其他方式處理。這個(gè)過(guò)程對(duì)開(kāi)發(fā)人員是透明的,因此RPC可以看作是本地過(guò)程調(diào)用的一種擴(kuò)展,使被調(diào)用過(guò)程不必與調(diào)用過(guò)程位于同一物理機(jī)中。
? RPC可以用于構(gòu)建基于B/S模式的分布式應(yīng)用程序:請(qǐng)求服務(wù)是一個(gè)客戶端、而服務(wù)提供程序是一臺(tái)服務(wù)器。和常規(guī)和本地的調(diào)用過(guò)程一樣,遠(yuǎn)程過(guò)程調(diào)用是同步操作,在結(jié)果返回之前,需要暫時(shí)中止請(qǐng)求程序。
RPC的優(yōu)點(diǎn):
? 以ARM環(huán)境為例,我們拆解本地調(diào)用的過(guò)程,以下面代碼為例:
int selfIncrement(int a)
{
return a + 1;
}
int a = 10;
selfIncrement(a);
? 當(dāng)執(zhí)行到selfIncrement(a)時(shí),首先把a(bǔ)存入寄存器R0,之后轉(zhuǎn)到函數(shù)地址selfIncrement,執(zhí)行函數(shù)內(nèi)的指令?ADD R0,#1。跳轉(zhuǎn)到函數(shù)的地址偏移量在編譯時(shí)確定。
? 但是如果這是一個(gè)遠(yuǎn)程調(diào)用,selfIncrement函數(shù)存在于其他機(jī)器,為了實(shí)現(xiàn)遠(yuǎn)程調(diào)用,請(qǐng)求方和服務(wù)方需要提供需要解決以下問(wèn)題:
1. 網(wǎng)絡(luò)傳輸。
?????本地調(diào)用的參數(shù)存放在寄存器或棧中,在同一塊內(nèi)存中,可以直接訪問(wèn)到。遠(yuǎn)程過(guò)程調(diào)用需要借助網(wǎng)絡(luò)來(lái)傳遞參數(shù)和需要調(diào)用的函數(shù) ID。
? 2. 編解碼
?????請(qǐng)求方需要將參數(shù)轉(zhuǎn)化為字節(jié)流,服務(wù)提供方需要將字節(jié)流轉(zhuǎn)化為參數(shù)。
? 3. 函數(shù)映射表
?????服務(wù)提供方的函數(shù)需要有唯一的 ID 標(biāo)識(shí),請(qǐng)求方通過(guò) ID 標(biāo)識(shí)告知服務(wù)提供方需要調(diào)用哪個(gè)函數(shù)。
以上三個(gè)功能即為 RPC 的基本框架所必須包含的功能。
1.3 RPC運(yùn)行的流程一次 RPC 調(diào)用的運(yùn)行流程大致分為如下七步,具體如下圖所示。
服務(wù)端存根和客戶端存根可以看做是被封裝起來(lái)的細(xì)節(jié),這些細(xì)節(jié)對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)是透明的,但是在客戶端層面看到的是 “本地” 調(diào)用了?selfIncrement()
方法,在服務(wù)端層面,則需要封裝、網(wǎng)絡(luò)傳輸、解封裝等等操作。因此 RPC 可以看作是傳統(tǒng)本地過(guò)程調(diào)用的一種擴(kuò)展,其使得被調(diào)用過(guò)程不必與調(diào)用過(guò)程位于同一物理機(jī)中。
RPC 的目標(biāo)是做到在遠(yuǎn)程機(jī)器上調(diào)用函數(shù)與本地調(diào)用函數(shù)一樣的體驗(yàn)。 為了達(dá)到這個(gè)目的,需要實(shí)現(xiàn)網(wǎng)絡(luò)傳輸、序列化與反序列化、函數(shù)映射表等功能,其中網(wǎng)絡(luò)傳輸可以使用socket或其他,序列化和反序列化可以使用protobuf,函數(shù)映射表可以使用std::function。
? lambda與std::function內(nèi)容可以看:
C++11 匿名函數(shù)lambda的使用_Thomas_Lbw的博客-博客
C++11 std::function 基礎(chǔ)用法_Thomas_Lbw的博客-博客
lambda
表達(dá)式和?std::function
的功能是類似的,lambda
表達(dá)式可以轉(zhuǎn)換為?std::function
,一般情況下,更多使用?lambda
表達(dá)式,只有在需要回調(diào)函數(shù)的情況下才會(huì)使用?std::function
。
#include#include#include#include#includeclass RPCClient
{
public:
using RPCCallback = std::function;
RPCClient(const std::string& server_address) : server_address_(server_address) {}
~RPCClient() {}
void Call(const std::string& method, const std::string& request, RPCCallback callback)
{
// 序列化請(qǐng)求數(shù)據(jù)
std::string data = Serialize(method, request);
// 發(fā)送請(qǐng)求
SendRequest(data);
// 開(kāi)啟線程接收響應(yīng)
std::thread t([this, callback]() {
std::string response = RecvResponse();
// 反序列化響應(yīng)數(shù)據(jù)
std::string result = Deserialize(response);
callback(result);
});
t.detach();
}
private:
std::string Serialize(const std::string& method, const std::string& request)
{
// 省略序列化實(shí)現(xiàn)
}
void SendRequest(const std::string& data)
{
// 省略網(wǎng)絡(luò)發(fā)送實(shí)現(xiàn)
}
std::string RecvResponse()
{
// 省略網(wǎng)絡(luò)接收實(shí)現(xiàn)
}
std::string Deserialize(const std::string& response)
{
// 省略反序列化實(shí)現(xiàn)
}
private:
std::string server_address_;
};
int main()
{
std::shared_ptrclient(new RPCClient("127.0.0.1:8000"));
client->Call("Add", "1,2", [](const std::string& result) {
std::cout<< "Result: "<< result<< std::endl;
});
return 0;
}
? 這段代碼定義了RPCClient類來(lái)處理客戶端的請(qǐng)求任務(wù),用到了lambda和std::function來(lái)處理函數(shù)調(diào)用,在Call中使用多線程技術(shù)。main中使用智能指針管理Rpcclient類,并調(diào)用了客戶端的Add函數(shù)。?
? 127.0.0.1為本地地址,對(duì)開(kāi)發(fā)來(lái)說(shuō)需要使用本地地址自測(cè),端口號(hào)為8000,需要選擇一個(gè)空閑端口來(lái)通信。
2.2 服務(wù)端代碼? 下面是服務(wù)端的實(shí)現(xiàn)
#include#include
上面的代碼實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的C++ RPC服務(wù)端。主要實(shí)現(xiàn)了以下功能:
注意,這套代碼是最簡(jiǎn)單的RPC機(jī)制,只能調(diào)用本地的資源,他還存在以下缺點(diǎn):
下面我們一步一步完善它。
三、加強(qiáng)版RPC(以“RPC簡(jiǎn)單實(shí)現(xiàn)”為基礎(chǔ)) 3.1 加入錯(cuò)誤處理下面是 RPCHandler 類中加入錯(cuò)誤處理的代碼示例:
class RPCHandler {
public:
// 其他代碼...
std::string handleRequest(const std::string& request) {
// 反序列化請(qǐng)求
std::maprequestMap;
std::istringstream is(request);
boost::archive::text_iarchive ia(is);
ia >>requestMap;
// 查找并調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)
std::string name = requestMap["name"];
std::string args = requestMap["args"];
std::unique_locklock(mtx_);
auto it = callbacks_.find(name);
if (it == callbacks_.end()) {
return "Error: Unknown function";
}
RPCCallback callback = it->second;
try {
return callback(args);
} catch (const std::exception& e) {
return "Error: Exception occurred: " + std::string(e.what());
} catch (...) {
return "Error: Unknown exception occurred";
}
}
};
上面的代碼在 RPCHandler 類的 handleRequest 函數(shù)中加入了錯(cuò)誤處理的代碼,它使用了 try-catch 語(yǔ)句來(lái)捕獲可能發(fā)生的異常。如果找不到對(duì)應(yīng)的函數(shù)或發(fā)生了異常,會(huì)返回錯(cuò)誤信息。這樣,如果請(qǐng)求格式不正確或函數(shù)不存在,服務(wù)端將會(huì)返回相應(yīng)的錯(cuò)誤信息。
3.2 加入網(wǎng)絡(luò)連接(socket)? 加入網(wǎng)絡(luò)連接不需要?jiǎng)臃?wù)端的實(shí)現(xiàn),只需要在main里創(chuàng)造套接字去鏈接就好:
int main()
{
io_context ioc;
ip::tcp::acceptor acceptor(ioc, ip::tcp::endpoint(ip::tcp::v4(), 8080));
RPCHandler rpcHandler;
// 注冊(cè)函數(shù)
rpcHandler.registerCallback("add", [](const std::string& args) {
std::istringstream is(args);
int a, b;
is >>a >>b;
int result = a + b;
std::ostringstream os;
os<< result;
return os.str();
});
rpcHandler.registerCallback("sub", [](const std::string& args) {
std::istringstream is(args);
int a, b;
is >>a >>b;
int result = a - b;
std::ostringstream os;
os<< result;
return os.str();
});
// 等待連接
while (true) {
ip::tcp::socket socket(ioc);
acceptor.accept(socket);
// 創(chuàng)建線程處理請(qǐng)求
std::thread requestThread([&](ip::tcp::socket socket) {
while (true) {
// 讀取請(qǐng)求
boost::asio::streambuf buf;
read_until(socket, buf, '\n');
std::string request = boost::asio::buffer_cast(buf.data());
request.pop_back();
// 處理請(qǐng)求
std::string response = rpcHandler.handleRequest(request);
// 發(fā)送響應(yīng)
write(socket, buffer(response + '\n'));
}
}, std::move(socket));
requestThread.detach();
}
return 0;
}
這是一個(gè)使用Boost.Asio庫(kù)實(shí)現(xiàn)的RPC服務(wù)端代碼示例。它使用了TCP協(xié)議監(jiān)聽(tīng)8080端口,等待客戶端的連接。當(dāng)有客戶端連接時(shí),創(chuàng)建一個(gè)新線程來(lái)處理請(qǐng)求。請(qǐng)求和響應(yīng)通過(guò)網(wǎng)絡(luò)傳輸。
3.3 加強(qiáng)并發(fā)性?使用并發(fā)和異步機(jī)制,忽略重復(fù)代碼,實(shí)現(xiàn)如下:
class RPCHandler {
public:
// ...
void handleConnection(ip::tcp::socket socket) {
while (true) {
// 讀取請(qǐng)求
boost::asio::streambuf buf;
read_until(socket, buf, '\n');
std::string request = boost::asio::buffer_cast(buf.data());
request.pop_back();
// 使用并行執(zhí)行處理請(qǐng)求
std::vector>futures;
for (int i = 0; i< request.size(); i++) {
futures.emplace_back(std::async(std::launch::async, &RPCHandler::handleRequest, this, request[i]));
}
// 等待所有請(qǐng)求處理完成并發(fā)送響應(yīng)
for (auto& f : futures) {
std::string response = f.get();
write(socket, buffer(response + '\n'));
}
}
}
};
這樣,請(qǐng)求會(huì)被分成多個(gè)部分并行處理,可以利用多核 CPU 的優(yōu)勢(shì)提高服務(wù)端的并發(fā)性能。
main():
int main() {
io_context ioc;
ip::tcp::acceptor acceptor(ioc, ip::tcp::endpoint(ip::tcp::v4(), 8080));
RPCHandler rpcHandler;
// 注冊(cè)函數(shù)
rpcHandler.registerCallback("add", [](const std::string& args) {
std::istringstream is(args);
int a, b;
is >>a >>b;
int result = a + b;
std::ostringstream os;
os<< result;
return os.str();
});
rpcHandler.registerCallback("sub", [](const std::string& args) {
std::istringstream is(args);
int a, b;
is >>a >>b;
int result = a - b;
std::ostringstream os;
os<< result;
return os.str();
});
// 創(chuàng)建線程池
boost::thread_pool::executor pool(10);
// 等待連接
while (true) {
ip::tcp::socket socket(ioc);
acceptor.accept(socket);
// 將請(qǐng)求添加到線程池中處理
pool.submit(boost::bind(&RPCHandler::handleConnection, &rpcHandler, std::move(socket)));
}
return 0;
}
在 main 函數(shù)中可以使用 boost::thread_pool::executor 來(lái)管理線程池,在線程池中提交任務(wù)來(lái)處理請(qǐng)求。這里的線程池大小設(shè)置為10,可以根據(jù)實(shí)際情況調(diào)整。
3.4 加入容錯(cuò)機(jī)制(修改客戶端部分)在其中使用了重試機(jī)制來(lái)保證客戶端能夠重新連接服務(wù)端:
class RPCClient {
public:
RPCClient(const std::string& address, int port) : address_(address), port_(port), socket_(io_context_) {
connect();
}
std::string call(const std::string& name, const std::string& args) {
// 序列化請(qǐng)求
std::ostringstream os;
boost::archive::text_oarchive oa(os);
std::maprequest;
request["name"] = name;
request["args"] = args;
oa<< request;
std::string requestStr = os.str();
// 發(fā)送請(qǐng)求
write(socket_, buffer(requestStr + '\n'));
// 讀取響應(yīng)
boost::asio::streambuf buf;
read_until(socket_, buf, '\n');
std::string response = boost::asio::buffer_cast(buf.data());
response.pop_back();
return response;
}
private:
void connect() {
bool connected = false;
while (!connected) {
try {
socket_.connect(ip::tcp::endpoint(ip::address::from_string(address_), port_));
connected = true;
} catch (const std::exception& e) {
std::cerr<< "Error connecting to server: "<< e.what()<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
}
std::string address_;
int port_;
io_context io_context_;
ip::tcp::socket socket_;
};
在這個(gè)示例中,當(dāng)連接服務(wù)端失敗時(shí),客戶端會(huì)在一定的時(shí)間間隔后重試連接,直到成功連接上服務(wù)端為止。
3.5 負(fù)載均衡? 服務(wù)端需要處理大量的請(qǐng)求,這部分的實(shí)現(xiàn)是可以獨(dú)立拎出來(lái)長(zhǎng)篇大論的,在此貼出其他大神的帖子吧。
服務(wù)器負(fù)載均衡_負(fù)載均衡服務(wù)器_我是一條胖咸魚的博客-博客
四、總結(jié)什么是負(fù)載均衡,看完文章秒懂_愛(ài)銘網(wǎng)絡(luò)的博客-博客_負(fù)載均衡
? 至此,我們逐步完善了RPC,在最簡(jiǎn)單的RPC基礎(chǔ)上加入了網(wǎng)絡(luò)連接、加入錯(cuò)誤處理、增強(qiáng)了并發(fā)訪問(wèn)的功能、并加入了容錯(cuò)機(jī)制,但是對(duì)于一個(gè)可以讓客戶正常使用的RPC來(lái)說(shuō),這還遠(yuǎn)遠(yuǎn)不夠,我本人也是實(shí)力有限,僅僅能讀懂或者解析部分RPC的設(shè)計(jì)動(dòng)機(jī)及原理,要詳細(xì)介紹RPC光寫這些是遠(yuǎn)遠(yuǎn)不夠的。工作中一套R(shí)PC附加其他功能需要一個(gè)團(tuán)隊(duì)忙活差不多兩個(gè)月,我僅僅在其中負(fù)責(zé)測(cè)試工具開(kāi)發(fā)和代碼生成,所以有不妥的地方請(qǐng)讀者諒解,有錯(cuò)的地方請(qǐng)指出必將改正。好夢(mèng)?。?!
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧
標(biāo)題名稱:C++簡(jiǎn)單實(shí)現(xiàn)RPC網(wǎng)絡(luò)通訊-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://jinyejixie.com/article24/dpohce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動(dòng)網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)、面包屑導(dǎo)航、電子商務(wù)、網(wǎng)站維護(hù)、定制開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容