zoukankan      html  css  js  c++  java
  • 智能 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,再结合网络库,就能很轻松的开发业务逻辑。

  • 相关阅读:
    Sqoop
    Mediawiki
    TextMate 通用快捷键
    Wind7外接显示器选择拓展模式后,鼠标只能往右移动才能切换到外接显示器上,不能修改切换方向
    用Nginx+Lua(OpenResty)开发高性能Web应用
    netty4.0.x源码分析—bootstrap
    mysql分组合并GROUP_CONCAT
    Only POT texture can be compressed to PVRTC format
    手机屏幕左下角显示Fastboot mode是什么情况?
    判断UNITY版本号
  • 原文地址:https://www.cnblogs.com/irons/p/4208256.html
Copyright © 2011-2022 走看看