• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

            基于protobuf的RPC實現(xiàn)

            可以對照使用google protobuf RPC實現(xiàn)echo service一文看,細節(jié)本文不再描述。

            google protobuf只負責消息的打包和解包,并不包含RPC的實現(xiàn),但其包含了RPC的定義。假設有下面的RPC定義:

            service MyService {
                    rpc Echo(EchoReqMsg) returns(EchoRespMsg) 
                }

            那么要實現(xiàn)這個RPC需要最少做哪些事?總結起來需要完成以下幾步:

            客戶端

            RPC客戶端需要實現(xiàn)google::protobuf::RpcChannel。主要實現(xiàn)RpcChannel::CallMethod接口。客戶端調用任何一個RPC接口,最終都是調用到CallMethod。這個函數(shù)的典型實現(xiàn)就是將RPC調用參數(shù)序列化,然后投遞給網絡模塊進行發(fā)送。

            void CallMethod(const ::google::protobuf::MethodDescriptor* method,
                              ::google::protobuf::RpcController* controller,
                              const ::google::protobuf::Message* request,
                              ::google::protobuf::Message* response,
                              ::google::protobuf::Closure* done) {
                    ...
                    DataBufferOutputStream outputStream(...) // 取決于你使用的網絡實現(xiàn)
                    request->SerializeToZeroCopyStream(&outputStream);
                    _connection->postData(outputStream.getData(), ...
                    ...
                }

            服務端

            服務端首先需要實現(xiàn)RPC接口,直接實現(xiàn)MyService中定義的接口:

            class MyServiceImpl : public MyService {
                    virtual void Echo(::google::protobuf::RpcController* controller,
                        const EchoReqMsg* request,
                        EchoRespMsg* response,
                        ::google::protobuf::Closure* done) {
                        ...
                        done->Run();
                    }
                }

            標示service&method

            基于以上,可以看出服務端根本不知道客戶端想要調用哪一個RPC接口。從服務器接收到網絡消息,到調用到MyServiceImpl::Echo還有很大一段距離。

            解決方法就是在網絡消息中帶上RPC接口標識。這個標識可以直接帶上service name和method name,但這種實現(xiàn)導致網絡消息太大。另一種實現(xiàn)是基于service name和method name生成一個哈希值,因為接口不會太多,所以較容易找到基本不沖突的字符串哈希算法。

            無論哪種方法,服務器是肯定需要建立RPC接口標識到protobuf service對象的映射的。

            這里提供第三種方法:基于option的方法。

            protobuf中option機制類似于這樣一種機制:service&method被視為一個對象,其有很多屬性,屬性包含內置的,以及用戶擴展的。用戶擴展的就是option。每一個屬性有一個值。protobuf提供訪問service&method這些屬性的接口。

            首先擴展service&method的屬性,以下定義這些屬性的key:

            extend google.protobuf.ServiceOptions {
                  required uint32 global_service_id = 1000; 
                }
                extend google.protobuf.MethodOptions {
                  required uint32 local_method_id = 1000;
                }

            應用層定義service&method時可以指定以上key的值:

            service MyService
                {
                    option (arpc.global_service_id) = 2302; 
            
                    rpc Echo(EchoReqMsg) returns(EchoRespMsg) 
                    {
                        option (arpc.local_method_id) = 1;
                    }
                    rpc Echo_2(EchoReqMsg) returns(EchoRespMsg) 
                    {
                        option (arpc.local_method_id) = 2;
                    }
                    ...
                }

            以上相當于在整個應用中,每個service都被賦予了唯一的id,單個service中的method也有唯一的id。

            然后可以通過protobuf取出以上屬性值:

            void CallMethod(const ::google::protobuf::MethodDescriptor* method,
                              ::google::protobuf::RpcController* controller,
                              const ::google::protobuf::Message* request,
                              ::google::protobuf::Message* response,
                              ::google::protobuf::Closure* done) {
                    ...
                    google::protobuf::ServiceDescriptor *service = method->service();
                    uint32_t serviceId = (uint32_t)(service->options().GetExtension(global_service_id));
                    uint32_t methodId = (uint32_t)(method->options().GetExtension(local_method_id));
                    ...
                }

            考慮到serviceId methodId的范圍,可以直接打包到一個32位整數(shù)里:

            uint32_t ret = (serviceId << 16) | methodId;
            

            然后就可以把這個值作為網絡消息頭的一部分發(fā)送。

            當然服務器端是需要建立這個標識值到service的映射的:

            bool MyRPCServer::registerService(google::protobuf::Service *rpcService) {
                    const google::protobuf::ServiceDescriptor = rpcService->GetDescriptor();
                    int methodCnt = pSerDes->method_count();
            
                    for (int i = 0; i < methodCnt; i++) {
                        google::protobuf::MethodDescriptor *pMethodDes = pSerDes->method(i);
                        uint32_t rpcCode = PacketCodeBuilder()(pMethodDes); // 計算出映射值
                        _rpcCallMap[rpcCode] = make_pair(rpcService, pMethodDes); // 建立映射
                    }
                    return true;
                }

            服務端收到RPC調用后,取出這個標識值,然后再從_rpcCallMap中取出對應的service和method,最后進行調用:

            google::protobuf::Message* response = _pService->GetResponsePrototype(_pMethodDes).New();
                // 用于回應的closure
                RPCServerClosure *pClosure = new (nothrow) RPCServerClosure( 
                        _channelId, _pConnection, _pReqMsg, pResMsg, _messageCodec, _version);
                RPCController *pController = pClosure->GetRpcController();
                ...
                // protobuf 生成的CallMethod,會自動調用到Echo接口
                _pService->CallMethod(_pMethodDes, pController, _pReqMsg, pResMsg, pClosure);

            參考

            posted on 2014-08-31 19:40 Kevin Lynx 閱讀(6670) 評論(0)  編輯 收藏 引用 所屬分類: c/c++

            蜜臀久久99精品久久久久久小说| 精品综合久久久久久97| 国产产无码乱码精品久久鸭| 四虎国产精品免费久久5151| 久久免费大片| 精品久久久久久无码中文字幕一区 | 久久久这里有精品中文字幕| 成人精品一区二区久久久| 欧美午夜精品久久久久久浪潮| 亚洲国产成人精品女人久久久 | 久久久国产亚洲精品| 久久久久高潮毛片免费全部播放| 无码任你躁久久久久久老妇| 亚洲国产精品久久久天堂| 亚洲欧洲久久av| 久久久久人妻一区精品| 伊人热人久久中文字幕| 97精品伊人久久大香线蕉app| 漂亮人妻被黑人久久精品| 91久久香蕉国产熟女线看| 亚洲国产精品无码久久一线 | 72种姿势欧美久久久久大黄蕉| 久久99精品国产麻豆不卡| 国产精品亚洲美女久久久| 狼狼综合久久久久综合网| 久久毛片免费看一区二区三区| 国产午夜福利精品久久2021| 狠狠综合久久综合88亚洲| 久久久久久久人妻无码中文字幕爆| 91精品国产高清久久久久久91| 久久不见久久见免费视频7| 一本久久a久久精品亚洲| 色婷婷综合久久久久中文字幕| 国产精品99久久久久久www| 国产精品久久久久9999| 久久狠狠高潮亚洲精品| 18岁日韩内射颜射午夜久久成人| 亚洲国产成人久久精品99| 青青草原综合久久大伊人导航| 久久99精品久久久久久野外| 日本久久久精品中文字幕|