智能 RPC框架 (C++)


RPC中文叫遠程函數調用,它是一種通信方式,只是看起來像普通的函數調用。

它包括三個基本要素:

1:服務端注冊相應的(服務)函數(用於調用方調用)

2:調用方通過函數調用的方式將一些信息和參數打包到消息,然后發送消息給被調用方。

3:被調用方收到消息后,提取信息和參數。調用相應函數。

 

被調用方不需要用戶手動解析參數,而是由"包裝代碼"預先解析出來。

 

目前很多rpc框架都(設計)配有協議描述文件,通過代碼生成,產生((含有)"包裝代碼")服務端的服務類或函數。

 

我不喜歡代碼生成,我喜歡直接在代碼中搞定它。

果然,我最近看到有朋友在一些腳本語言中做到這點。某些實現還不需要手動(預先)注冊服務函數。

比如:

https://github.com/sniperHW/distri.lua/blob/master/examples/rpcserver.lua

https://github.com/akirayu101/GM_RPC/blob/master/gmrpc_lua/rpc_handlers/rpc_handler_sample.lua

https://github.com/akirayu101/GM_RPC/blob/master/gmrpc_py/rpc_handlers/rpc_handler_sample.py

 

然而我又不熟悉lua或python,所以我用C++11 來實現了它。

主要功能:

1:注冊服務函數

void test5(string a, int b, map<int, map<int, string>> vlist) 
{ 
} 

rpc rpc_server; /*rpc服務器*/
rpc_server.def("test5", test5);

 

2:客戶端調用遠程函數

rpc rpc_client; /*rpc客戶端*/
rpc_client.call("test5", "a", 1, mlist, [&upvalue](int a, int b){ 
    upvalue++; 
    cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << endl; 
}); 

 

其中mlis是一個map<int,map<int,string>>類型變量。 rpc_client.call 的返回值是一個string,它表示此次call的消息。

我們可以把它(string 消息)通過網絡發送給服務器。在這里(測試)我們直接通過下面的方式傳遞給服務端。

 

!!!注意!!!:call的最后一個參數可以是一個lambda,它表示處理此rpc返回值。 如果不是一個lambda,則它也是rpc調用參數。

 

3:服務端處理rpc request

rpc_server.handleRpc(rpc_request_msg);

 

其中 rpc_request_msg為接受到的網絡消息(字符串)。

這樣就會自動調用到我們的 test5 函數。 並且形參已經(自動)准備OK。你只需要在test5 里使用這些參數即可。(不用關心網絡消息協議)。

 

4:被調用方可以返回數據給調用方

rpc_response_str = rpc_server.reply(1, 1, 2);   /* (1,1,2)中的1為調用方的req_id, (1,2)為返回值 */ 
rpc_client.handleResponse(rpc_response_str); 

 

上面代碼通過 rpc_server.reply返回消息給客戶端。 然后客戶端模擬收到消息后通過 rpc_client.handleResponse(rpc_response_str)

會回調rpc_client.call() 時 所傳遞的lambda回調函數。

 

注意:以上 服務函數(譬如test5)和rpc 返回值處理函數(譬如那個lambda)的參數 是任意個數,且"任意"類型

(支持 int,string,JsonObject-json對象,vector<int>,vector<string>, map<int,string>,map<string,int>,map<string,string>, map<int/string, 前述所有類型/遞歸> )

 

整個測試代碼:

void test1(int a, int b)
{
    cout << "in test1" << endl;
    cout << a << ", " << b << endl;
}

void test2(int a, int b, string c)
{
    cout << "in test2" << endl;
    cout << a << ", " << b << ", " << c << endl;
}

void test3(string a, int b, string c)
{
    cout << "in test3" << endl;
    cout << a << ", " << b << ", " << c << endl;
}

void test4(string a, int b)
{
    cout << "in test4" << endl;
    cout << a << "," << b <<  endl;
}

void test5(string a, int b, map<int, map<int, string>> vlist)
{
}

void test6(string a, int b, map<string, int> vlist)
{
}

void test7()
{
    cout << "in test7" << endl;
}

int main()
{
    int upvalue = 10;
    using namespace dodo;

    rpc rpc_server; /*rpc服務器*/
    rpc rpc_client; /*rpc客戶端*/

    rpc_server.def("test4", test4);
    rpc_server.def("test5", test5);
    rpc_server.def("test7", test7);

    string rpc_request_msg; /*  rpc消息   */
    string rpc_response_str;       /*  rpc返回值  */

    {
        rpc_request_msg = rpc_client.call("test7");

        rpc_server.handleRpc(rpc_request_msg);
    }

    map<int, string> m1;
    m1[1] = "Li";
    map<int, string> m2;
    m2[2] = "Deng";
    map<int, map<int, string>> mlist;
    mlist[100] = m1;
    mlist[200] = m2;

    {
        rpc_request_msg = rpc_client.call("test5", "a", 1, mlist, [&upvalue](int a, int b){
            upvalue++;
            cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << endl;
        });

        rpc_server.handleRpc(rpc_request_msg);
    }

    {
        rpc_request_msg = rpc_client.call("test5", "a", 1, mlist, [&upvalue](string a, string b, int c){
            upvalue++;
            cout << "upvalue:" << upvalue << ", a:" << a << ", b:" << b << ", c:" << c << endl;
        });

        rpc_server.handleRpc(rpc_request_msg);
    }

    {
        rpc_request_msg = rpc_client.call("test4", "a", 1);
        rpc_server.handleRpc(rpc_request_msg);
    }
    
    /*  模擬服務器通過reply返回數據給rpc client,然后rpc client處理收到的rpc返回值 */
    {
        rpc_response_str = rpc_server.reply(1, 1, 2);   /* (1,1,2)中的1為調用方的req_id, (1,2)為返回值 */
        rpc_client.handleResponse(rpc_response_str);
    }

    {
        rpc_response_str = rpc_server.reply(2, "hello", "world", 3);
        rpc_client.handleResponse(rpc_response_str);
    }

    cin.get();
    return 0;
}

 

RPC"框架"代碼地址: https://github.com/IronsDu/accumulation-dev/blob/master/utils/rpc_test.cpp 。

歡迎討論。

 

---update:

我們來看看最新戰果:

class Player : public dodo::rpc
{
public:
    Player()
    {
        registerHandle("player_attack", &Player::attack);
        registerHandle("player_hi", &Player::hi);
    }

private:
    template<typename... Args>
    void        registerHandle(string name, void (Player::*callback)(Args...))
    {
        def(name.c_str(), [this, callback](Args... args){
            (this->*callback)(args...);
        });
    }

private:
    void    attack(string target)
    {
        cout << "attack:" << target << endl;
    }

    void    hi(string i, string j)
    {
        cout << i << j << endl;
    }
};

Player rpc_server; /*rpc服務器*/
Player rpc_client; /*rpc客戶端*/

rpc_request_msg = rpc_client.call("player_attack", "Li Lei");
rpc_server.handleRpc(rpc_request_msg);
rpc_request_msg = rpc_client.call("player_hi", "Hello", "World");
rpc_server.handleRpc(rpc_request_msg);

 

 是不是非常簡單呢?很輕松的把消息(參數)自動派發到實際的業務函數里?

每一個Player就是一個rpc,再結合網絡庫,就能很輕松的開發業務邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM