gRPC学习(一)

GRPC

宝藏网站:http://cmsblogs.com/

今天来学习下grpc,遵循 What-Why-How

What

关键词:RPC, HTTP2.0 protobuf 3.x Netty4.x

同类技术:dubbo, grpc, thrift,feign

解决了什么问题:

  1. grpc是干什么的:解决服务间相互调用的问题,使得组件之间可以相互发送请求传输数据。gRPC使客户端程序可以像调本地程序一样调用不同服务器上的应用程序
  2. 优缺点:性能高(通信采用Netty,http2.0), 通过proto 定义的接口也是非常清晰和灵活 (它的优点也对应其他技术的缺点)

组成部分

<dependency>
	<groupId>io.grpc</groupId>
	<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
	<groupId>io.grpc</groupId> 
	<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
	<groupId>io.grpc</groupId>
	<artifactId>grpc-netty</artifactId>
</dependency>
<dependency>

Why 存在?

为什么会出现:基于之前各种技术的弊端吧

How 来个Hello World?

说做就做

1. 新建Maven项目

3个依赖包

<properties>
       <grpc.version>1.0.3</grpc.version>
   </properties>

   <dependencies>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-netty</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-protobuf</artifactId>
           <version>${grpc.version}</version>
       </dependency>
       <dependency>
           <groupId>io.grpc</groupId>
           <artifactId>grpc-stub</artifactId>
           <version>${grpc.version}</version>
       </dependency>
   </dependencies>

转换工具

<build>
       <extensions>
           <extension>
               <groupId>kr.motd.maven</groupId>
               <artifactId>os-maven-plugin</artifactId>
               <version>1.4.1.Final</version>
           </extension>
       </extensions>
       <plugins>
           <plugin>
               <groupId>org.xolstice.maven.plugins</groupId>
               <artifactId>protobuf-maven-plugin</artifactId>
               <version>0.5.0</version>
               <configuration>
                   <protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact>
                   <pluginId>grpc-java</pluginId>
                   <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
               </configuration>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>compile-custom</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>

2. 编写proto文件

在main目录下建立一个proto文件夹,然后在里面建立hello.proto,内容如下:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

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;
}

接下来执行 mvn compile,就会生成如下代码,如果未被标记为源代码目录的话,就右键标记下:

image-20200924144459456

3. 服务端代码

public class HelloWorldServer {

   private int port = 50051;
   private Server server;

   /**
    * 启动服务
    * @throws IOException
    */
   private void start() throws IOException {
       server = ServerBuilder.forPort(port)
                             .addService(new GreeterImpl())
                             .build()
                             .start();

       System.out.println("service start...");

       Runtime.getRuntime().addShutdownHook(new Thread() {

           @Override
           public void run() {
               System.err.println("*** shutting down gRPC server since JVM is shutting down");
               HelloWorldServer.this.stop();
               System.err.println("*** server shut down");
           }
       });
   }

   private void stop() {
       if (server != null) {
           server.shutdown();
       }
   }

   // block 一直到退出程序
   private void blockUntilShutdown() throws InterruptedException {
       if (server != null) {
           server.awaitTermination();
       }
   }

   public static void main(String[] args) throws IOException, InterruptedException {
       final HelloWorldServer server = new HelloWorldServer();
       server.start();
       server.blockUntilShutdown();
   }

   // 实现 定义一个实现服务接口的类
   private class GreeterImpl extends GreeterGrpc.GreeterImplBase {

       public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
           //获取参数
           System.out.println("收到的信息:"+req.getName());

           //这里可以放置具体业务处理代码 start

           //这里可以放置具体业务处理代码 end

           //构造返回
           HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build();
           responseObserver.onNext(reply);
           responseObserver.onCompleted();
       }
   }
}

4. 客户端代码

public class HelloWorldClient {

   private final ManagedChannel channel; //一个gRPC信道
   private final GreeterGrpc.GreeterBlockingStub blockingStub;//阻塞/同步 存根

  //初始化信道和存根
   public HelloWorldClient(String host,int port){
       this(ManagedChannelBuilder.forAddress(host, port)
                                 // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
                                 // needing certificates.
                                 .usePlaintext(true));
   }

   /** Construct client for accessing RouteGuide server using the existing channel. */
   private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
       channel = channelBuilder.build();
       blockingStub = GreeterGrpc.newBlockingStub(channel);
   }

   public void shutdown() throws InterruptedException {
       channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
   }

   //客户端方法
   public  void greet(String name){
       HelloRequest request = HelloRequest.newBuilder().setName(name).build();
       HelloReply response;
       try {
           response = blockingStub.sayHello(request);
       } catch (StatusRuntimeException e) {
           System.out.println("RPC调用失败:"+e.getMessage());
           return;
       }
       System.out.println("服务器返回信息:"+response.getMessage());
   }

   public static void main(String[] args) throws InterruptedException {
       HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
       try {
           for(int i=0;i<5;i++){
               client.greet("world:"+i);
           }
       }finally {
           client.shutdown();
       }
   }
}

5. 测试

启动服务端

image-20200924144940647

启动客户端

image-20200924145056159

6. 回顾分析

这一步思考做了什么,为什么跑通了,大概就是如下

  1. 引入3个依赖grpc-netty,grpc-protobuf,grpc-stub 通信,序列化,客户端,最后再引入build插件
  2. proto 文件,自己命名并且定义消息格式
  3. 用build工具生成代码,然后测试
### 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、付费专栏及课程。

余额充值