gRPC示例
gRPC是一个高性能、开源的RPC框架,它支持多种编程语言和平台,并且使用protobuf作为其默认的序列化协议。
使用gRPC,需要定义一个包含服务方法的.proto文件,并使用protobuf编译器生成客户端和服务器端代码。然后使用生成的客户端代码来调用服务器端提供的方法。
以下是一个简单的示例.proto文件:
syntax = "proto3"; package myservice; service MyService { rpc SayHello (HelloRequest) returns (HelloResponse) {} } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; }
在这个示例中,我们定义了一个包含一个方法的服务MyService,该方法接受一个HelloRequest消息并返回一个HelloResponse消息。
使用protobuf编译器生成客户端和服务器端代码,使用以下命令:
protoc --grpc_out=. --plugin=protoc-gen-grpc=/usr/local/bin/grpc_cpp_plugin myservice.proto
这将生成myservice.pb.h和myservice.grpc.pb.h两个头文件和对应的.cc文件,这四个文件提供了消息的映射和gRPC服务的接口实现。需要将4个文件放在客户端和服务端。
其中:myservice.pb.h和myservice.pb.cc是Protocol Buffers二进制协议的消息映射。
myservice.grpc.pb.h和myservice.grpc.pb.cc提供了grpc的网络服务。
服务端代码
可以编写服务器端实现和客户端代码来调用该服务。以下是一个简单的C++服务器端实现示例:
#include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "myservice.grpc.pb.h" using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using myservice::HelloRequest; using myservice::HelloResponse; using myservice::MyService; class MyServiceImpl final : public MyService::Service { Status SayHello(ServerContext* context, const HelloRequest* request, HelloResponse* response) override { std::string prefix("Hello, "); response->set_message(prefix + request->name()); return Status::OK; } }; void RunServer() { std::string server_address("0.0.0.0:50051");//服务器的IP地址和端口 MyServiceImpl service;//示例化一个对象,里面实现了grpc服务的业务逻辑 ServerBuilder builder;//创建一个对象,用于构建服务器 //指定服务器监听的地址和端口 builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&service);//将server注册到builder中,表示服务器会响应客户端的请求 std::unique_ptr<Server> server(builder.BuildAndStart());//创建并使用服务器,同时用智能指针管理这个对象 std::cout << "Server listening on " << server_address << std::endl; server->Wait();//阻塞等待客户端请求到来 } int main(int argc, char** argv) { RunServer(); return 0; }
在这个示例中实现了MyService服务的SayHello方法,该方法将请求中的名称与“Hello,”前缀组合在一起,并将其设置为响应消息中的消息字段。
客户端代码
要编写客户端代码来调用此服务,以下代码:
#include <iostream> #include <memory> #include <string> #include <grpcpp/grpcpp.h> #include "myservice.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; using myservice::HelloRequest; using myservice::HelloResponse; using myservice::MyService; class MyServiceClient { public: MyServiceClient(std::shared_ptr<Channel> channel) : stub_(MyService::NewStub(channel)) {} std::string SayHello(const std::string& name) { HelloRequest request;//创建一个对象 request.set_name(name);//将参数设置到请求中 HelloResponse response;//创建一个对象 ClientContext context;//创建一个对象 Status status = stub_->SayHello(&context, request, &response); if (status.ok()) { return response.message(); } else { return "RPC failed"; } } private: std::unique_ptr<MyService::Stub> stub_; }; int main(int argc, char** argv) { MyServiceClient client(grpc::CreateChannel( "localhost:50051", grpc::InsecureChannelCredentials())); //创建一个通道对象,用于与grpc服务器建立连接。 std::string name("World"); std::string reply = client.SayHello(name); std::cout << "Received: " << reply << std::endl; return 0; }
在这个示例中,创建了一个MyServiceClient类来封装与服务器的通信。使用CreateChannel函数创建一个与服务器的连接,并使用SayHello方法来调用服务器提供的方法。
Ice和grpc对比
实例对比 https://blog.csdn.net/whzhaochao/article/details/51406539
Ice的并发性能要好于grpc
传输协议对比
ICE的传输层协议采用的是自定义的 TCP Socket 通道(Ice.TCP)进行通信,相比于传统 TCP 协议,它具有以下特点:
- 支持异步通信:Ice.TCP 通道提供了异步通信的机制,Ice.TCP 通道使用异步 I/O 模型进行通信,即在发送和接收数据时,不需要等待对方的响应,而是直接返回控制权,等待数据就绪后再进行处理。这种方式可以大大提高通信效率和性能。
- 支持多路复用:Ice.TCP 通道使用 epoll 或 kqueue 等多路复用技术,可以在单个连接上同时处理多个请求,从而减少网络开销。
- 支持分片传输:Ice.TCP 通道将大的数据包分成多个小数据包进行传输,避免了网络拥塞和传输延迟。同时,它还支持数据压缩和加密等机制,可以进一步提高传输效率和安全性。
- 序列化和反序列化:Ice.TCP 通道使用 Protocol Buffers 或 Slice 等序列化框架,将数据进行序列化和反序列化处理,以便在网络中进行传输。
而grpc采用的HTTP2,没有自定义的TCP协议轻量,可能更兼容浏览器传输的这种模式。
序列化方法对比
ZeroC Ice 使用 Slice 语言进行数据序列化,它具有以下特点:
- 强类型:Slice 语言是一种强类型语言,可以在编译期间进行类型检查,避免了运行时类型错误。
- 动态扩展:Slice 语言支持动态扩展,可以在不修改代码的情况下添加新的数据类型或接口。
- 面向对象:Slice 语言是一种面向对象的语言,支持类、继承、多态等特性。
- 易于使用:Slice 语言的语法简单易懂,容易上手。
与 Google 的 Protocol Buffers 相比,Slice 语言也有一些优势:
- 支持动态扩展:与 Protocol Buffers 不同,Slice 语言支持动态扩展,可以在不修改代码的情况下添加新的数据类型或接口。
- 支持多语言:ZeroC Ice 支持多种编程语言,包括 C++, Java, Python 等,而 Protocol Buffers 只支持少数几种编程语言。
- 支持面向对象:Slice 语言是一种面向对象的语言,支持类、继承、多态等特性…[omitted]
动态扩展:
Slice 语言的动态扩展指的是可以在不修改已有代码的情况下添加新的数据类型或接口。举个例子,假设我们有一个 Slice 接口定义如下:
interface MyInterface { void MyMethod(); };
现在我们想要添加一个新的方法到这个接口中,可以通过继承的方式实现:
interface MyExtendedInterface extends MyInterface { void MyNewMethod(); };
这样,我们就扩展了原有的接口,并添加了一个新的方法,而不需要修改已有的代码。
相比之下,Google 的 Protocol Buffers 不支持动态扩展。如果要添加新的字段或消息类型,需要修改已有的 .proto 文件并重新生成代码。
// 反例:protobuf 不支持动态扩展 message MyMessage { int32 my_field = 1; } // 如果要添加新的字段,需要修改已有的 .proto 文件并重新生成代码 message MyExtendedMessage { int32 my_field = 1; string my_new_field = 2; }
这样就会导致代码的不兼容性和维护难度增加。