关于Grpc的学习

本文介绍如何使用gRPC在Java环境中构建远程过程调用服务。包括定义服务、生成代码、编写服务器和客户端,以及运行示例的过程。

本文较为简略,只是介绍了一个大概的流程,具体的开发后面博文可见。

一、定义:定义一个服务,指定其能被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,同时运行一个grpc服务器来处理客户端调用。客户端拥有一个文件(存根)可以访问服务端。

二、依赖:protocol buffers,谷歌开源的结构数据序列化机制(类似json),通过使用proto file创建grpc服务,protocol buffers 消息类型来定义方法参数和返回类型。。我们在使用gprc时,使用版本是proto 3,针对c++和Java还有go都有支持。

三、测试环境:git、Java需要jdk

四、测试:

 

一、下载grpc源码:

git clone https://github.com/grpc/grpc-java.git
cd grpc-java/examples

二、定义服务:通过proto buffers 的proto 3 版本实现,proto buffer使用接口定义语言来实现服务方法、请求参数、返回类型。客户端和服务端均使用服务定义生成的接口代码。

在helloworld.proto 使用Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个 Greeter

syntax = "proto3";

option java_package = "io.grpc.examples";

package helloworld;

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

三、生成grpc代码:

使用编译器protoc生成客户端和服务器端所需的代码。

生成HelloRequest.java, HelloResponse.java和其他文件包含所有 protocol buffer 用来填充、序列化和提取 HelloRequest 和 HelloReply 消息类型的代码。

GreeterGrpc.java, 包含 (还有其他有用的代码):

Greeter 服务端需要实现的接口

        public static interface Greeter {
        public void sayHello(Helloworld.HelloRequest request,
        StreamObserver<Helloworld.HelloReply> responseObserver);
        }

客户端用来与 Greeter 服务端进行对话的 存根 类。就像你所看到的,异步存根也实现了 Greeter 接口。

    public static class GreeterStub extends AbstractStub<GreeterStub>
    implements Greeter {
      ...
    }

四、写一个服务器

GreeterImpl.java准确地实现了 Greeter 服务所需要的行为。 正如你所见,GreeterImpl 类通过实现 sayHello 方法,实现了从 IDL生成的GreeterGrpc.Greeter 接口 。

@Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}

sayHello 有两个参数:

  • HelloRequest,请求。
  • StreamObserver<HelloReply>: 应答观察者,一个特殊的接口,服务器用应答来调用它。

    为了返回给客户端应答并且完成调用:

1.用我们的激动人心的消息构建并填充一个在我们接口定义的 HelloReply 应答对象。

2.将 HelloReply 返回给客户端,然后表明我们已经完成了对 RPC 的处理.

五、服务端实现

确认服务在服务端有可用合理的 gRPC 服务器,将我们实现的 Greeter 服务绑定到一个端口。然后我们启动服务器:服务器现在已准备好从 Greeter 服务客户端接收请求。我们将在具体语言对应的文档里更深入地了解这所有的工作是怎样进行的。

/* The port on which the server should run */
private int port = 50051;
private Server server;
private void start() throws Exception {
server = ServerBuilder.forPort(port)
    .addService(GreeterGrpc.bindService(new GreeterImpl()))
    .build()
    .start();
logger.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
  @Override
  public void run() {
    // Use stderr here since the logger may has been reset by its JVM shutdown hook.
    System.err.println("*** shutting down gRPC server since JVM is shutting down");
    HelloWorldServer.this.stop();
    System.err.println("*** server shut down");
  }
});
}

六、写一个客户端

客户端的 gRPC 非常简单。在这一步,我们将用生成的代码写一个简单的客户程序来访问我们在上一节里创建的 Greeter 服务器。

1.连接服务:连接 Greeter 服务器。我们需要创建一个 gRPC 频道,指定我们要连接的主机名和服务器端口。然后我们用这个频道创建存根实例。

private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
public HelloWorldClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
    .usePlaintext(true)
    .build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}

2.调用 RPC:现在我们可以联系服务并获得一个 greeting,

  1. 我们创建并填充一个 HelloRequest 发送给服务。
  2. 我们用请求调用存根的 SayHello(),如果 RPC 成功,会得到一个填充的 HelloReply ,从其中我们可以获得 greeting。
HelloRequest req = HelloRequest.newBuilder().setName(name).build();
HelloReply reply = blockingStub.sayHello(req);

七、运行

不同的语言间的互操作性,即在不同的语言运行客户端和服务端。每个服务端和客户端使用从同一过 proto 文件生成的接口代码,则意味着任何 Greeter 客户端可以与任何 Greeter 服务端对话。

Java 首先运行服务端:你可以从 examples 目录构建并运行服务端。

首先构建客户端和服务端:

$ ../gradlew -PskipCodegen=true installDist

然后运行服务端,服务端将监听 50051 :

$ ./build/install/grpc-examples/bin/hello-world-server

一旦服务器在运行,在其他的终端窗口运行客户端并确认它收到一个消息。你可以在 examples 目录下构建并运行客户端。假如你还没有构建客户端,可以使用如下命令:

$ ../gradlew -PskipCodegen=true installDist

然后运行客户端:

$ ./build/install/grpc-examples/bin/hello-world-client

### C++ gRPC 学习教程和示例代码 #### 一、环境搭建 为了能够顺利运行C++中的gRPC程序,需要先完成开发环境的配置。这通常涉及到安装必要的依赖库以及设置编译工具链。具体来说,可以按照官方文档指导来准备所需的软件包,比如Protocol Buffers编译器`protoc`及其对应的C++插件,还有gRPC核心库等[^1]。 ```bash sudo apt-get install build-essential autoconf libtool pkg-config git clone https://github.com/protocolbuffers/protobuf.git cd protobuf ./autogen.sh && ./configure && make -j$(nproc) && sudo make install ``` 对于gRPC本身,则可以通过如下命令获取并构建: ```bash git clone --recurse-submodules -b v1.48.x https://github.com/grpc/grpc cd grpc mkdir -p cmake/build && cd cmake/build cmake ../.. make -j$(nproc) sudo make install ``` #### 二、创建Protobuf文件 接下来就是定义服务接口,在`.proto`文件里描述消息结构和服务方法。这里给出一个简单的例子——HelloWorld服务,其中包含了一个名为SayHello的方法用于接收请求并向客户端返回响应信息。 ```protobuf syntax = "proto3"; option cc_enable_arenas = true; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } ``` 保存上述内容到`helloworld.proto`之后,利用之前提到过的`protoc`命令行工具将其转换成相应的头文件与源码文件以便后续使用。 #### 三、编写服务器端逻辑 基于前面所生成的服务类模板,现在可以在项目中实现具体的业务处理函数了。下面展示的是如何继承自动生成出来的基类,并重写虚函数以提供实际功能的部分代码片段。 ```cpp #include <iostream> #include "helloworld.grpc.pb.h" using namespace std; using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; using helloworld::Greeter; using helloworld::HelloReply; using helloworld::HelloRequest; class GreeterServiceImpl final : public Greeter::Service { public: Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override { string prefix("Hello "); reply->set_message(prefix + request->name()); return Status::OK; } }; ``` 这段代码实现了当接收到客户端发起的调用时会执行的操作:拼接字符串形成回复文本并通过参数传递给对方。 #### 四、启动监听进程 有了完整的协议声明加上对应的功能模块后就可以着手建立网络连接等待远端访问啦! ```cpp void RunServer() { string server_address("0.0.0.0:50051"); GreeterServiceImpl service; ServerBuilder builder; // Listen on the given address without any authentication mechanism. builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); // Register "service" as the instance through which we'll communicate with // clients. In this case it corresponds to an *synchronous* service. builder.RegisterService(&service); // Finally assemble the server. unique_ptr<Server> server(builder.BuildAndStart()); cout << "Server listening on " << server_address << endl; // Wait for the server to shutdown. Note that some other thread must be // responsible for shutting down the server for this call to ever return. server->Wait(); } ``` 此部分负责初始化HTTP/2传输层设施并将指定地址开放出去供外界联系;同时注册好先前定义好的处理器对象使得每次有新链接进来都能找到合适的地方去解析数据流进而触发相应动作。 #### 五、客户端编程指南 最后一步自然是要让应用程序具备主动出击的能力咯~即构造出能向远程主机发出请求的消息体格式化为wire format再经由socket发送过去得到回应为止的过程。 ```cpp void RunClient() { string target_str("localhost:50051"); // Instantiate the client. It requires a channel, out of which the actual RPCs // are created. This channel models a connection to an endpoint specified by // the argument; you may provide extra arguments to indicate credentials, // compression Level etc. shared_ptr<Channel> channel = CreateChannel(target_str, InsecureChannelCredentials()); // Stub acts like a proxy object representing remote side entity. unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel)); // Data we are sending to the server. HelloRequest request; request.set_name("you"); // Container for the data we expect from the server. HelloReply reply; // Context for the client. It could be used to convey extra information to // the server and/or tweak certain RPC behaviors. ClientContext context; // The actual RPC. Status status = stub->SayHello(&context, request, &reply); // Act upon its status. if (status.ok()) { cout << "Greeter received: " << reply.message() << endl; } else { cerr << status.error_code() << ": " << status.error_message() << endl; } } ``` 以上便是整个流程的大致介绍,当然这只是冰山一角而已,更多高级特性和最佳实践还需要读者朋友们自行探索学习哦~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值