You can refer to the article "echo service implementation" using Google protobuf rpc. The details are not described in this article.
Google protobuf is only responsible for the packaging and package of messages, and does not include RPC implementation, but contains the RPC definition. Suppose there is the following RPC definition:
service MyService { rpc Echo(EchoReqMsg) returns(EchoRespMsg) }
So what do I need to do at least to implement this RPC? To sum up, you need to complete the following steps:
Client
RPC client implementationgoogle::protobuf::RpcChannel
. Main ImplementationRpcChannel::CallMethod
Interface. When the client calls any RPC interfaceCallMethod
. A typical implementation of this function is to serialize the RPC call parameters and deliver them to the network module for sending.
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 (...) // depending on your network implementation request-> serializetozerocopystream (& outputstream); _ connection-> postdata (outputstream. getdata (),......}
Server
The server first needs to implement the RPC interface to directly implementMyService
Interface defined in:
class MyServiceImpl : public MyService { virtual void Echo(::google::protobuf::RpcController* controller, const EchoReqMsg* request, EchoRespMsg* response, ::google::protobuf::Closure* done) { ... done->Run(); } }
Service & Method
Based on the above, we can see that the server does not know which RPC interface the client wants to call. The server receives network messages and callsMyServiceImpl::Echo
There is still a long way to go.
The solution is to add the RPC Interface ID to the network message. This identifier can be directly carried with the service name and method name, but this implementation causes the network message to be too large. Another implementation is to generate a hash value based on service name and method name. because there are not many interfaces, it is easier to find a string hash algorithm that basically does not conflict with each other.
Either way, the server must establish a RPC ing between the RPC Interface ID and the protobuf service object.
Here we provide the third method: the option-based method.
In protobuf, the option mechanism is similar to this mechanism: Service & method is regarded as an object and has many attributes, including built-in attributes and user extensions. You can use the option extension. Each attribute has a value. Protobuf provides interfaces for accessing services and methods.
First, extend the attributes of Service & method. The following defines the keys of these attributes:
extend google.protobuf.ServiceOptions { required uint32 global_service_id = 1000; } extend google.protobuf.MethodOptions { required uint32 local_method_id = 1000; }
When the application layer defines service & method, you can specify the value of the above 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; } ... }
The above is equivalent to that in the entire application, each service is assigned a unique ID, and the method in a single service also has a unique ID.
Then, you can use protobuf to retrieve the preceding attribute values:
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)); ... }
ConsideringserviceId
methodId
Can be directly packaged into a 32-bit integer:
uint32_t ret = (serviceId << 16) | methodId;
Then, the value can be sent as part of the network message header.
Of course, the server needs to establish a service ing between the id value and the 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); // calculate the ing value _ rpccallmap [rpccode] = make_pair (rpcservice, pmethoddes); // creates a ing} return true ;}
After receiving the RPC call, the server extracts the id value and then_rpcCallMap
To obtain the corresponding service and method, and finally call:
Google: protobuf: Message * response = _ pservice-> getresponseprototype (_ pmethoddes ). new (); // closure rpcserverclosure * pclosure = new (nothrow) rpcserverclosure (_ channelid, _ pconnection, _ preqmsg, presmsg, _ messagecodec, _ version) for the response ); rpccontroller * pcontroller = pclosure-> getrpccontroller ();... // The callmethod generated by protobuf will automatically call the echo interface _ pservice-> callmethod (_ pmethoddes, pcontroller, _ preqmsg, presmsg, pclosure );
Reference
- Implement echo service using Google protobuf RPC
- Protobuf extensions
- Protobuf Service
- Protobuf options
Address: http://codemacro.com/2014/08/31/protobuf-rpc/
Written by Kevin Lynx posted athttp: // codemacro.com
RPC implementation based on protobuf