Nacos源码服务端集群系列第1篇gRPC之初识

学习目标: 了解grpc的使用方法,在现有的服务中如何集成gRPC,如何通过proto文件定义一个服务,如何创建gRPC服务并启动服务以及客户端如何请求服务。

一、简介

gRPC是最初由 Google 开发的高性能、开源的 RPC 框架,实现服务之的远程调用,屏蔽了底层的通讯、连接、序列化等技术,用户只需要专注于定义服务、实现服务、调用服务。更重要的它是语言无关的RPC框架。

2. 概述

该框架基于远程过程调用的客户端-服务器模型。客户端应用程序可以直接调用服务器应用程序上的方法,就好像它是本地方法对象一样。

本文将使用以下步骤使用 gRPC 创建典型的客户端-服务器应用程序:

  1. 在.proto文件中定义服务
  2. 使用protocol buffer compiler工具生成服务器和客户端代码
  3. 创建服务器应用程序,实现生成的服务接口并创建 gRPC 服务器
  4. 创建客户端应用程序,使用生成的stub包进行 RPC 调用

让我们定义一个简单的HelloService,它返回问候以换取名字和姓氏。

3. Maven依赖

我们基于之前搭建的【nacos-spring-cloud-provider-example】让我们添加grpc-nettygrpc-protobufgrpc-stub依赖项:

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

 

4. 定义服务

我们首先定义一个服务,指定可以远程调用的方法及其参数和返回类型。通过配置在.proto文件中完成的。包括发送、响应消息的结构信息等。

4.1。基本配置

让我们为示例HelloService创建一个proto/HelloService.proto文件。我们首先添加一些基本的配置:

syntax = "proto3";
option java_multiple_files = true;
package org.baeldung.grpc;

第一行告诉编译器在这个文件中使用了什么语法。默认情况下,编译器在单个 Java 文件中生成所有 Java 代码。第二行覆盖此设置,所有内容都将在单个文件类种生成。

最后,我们指定要用于生成的 Java 类的包。

4.2. 定义消息结构

接下来,我们定义消息:

message HelloRequest {
    string firstName = 1;
    string lastName = 2;
}

这定义了请求的数据结构。在这里,进入消息的每个属性都与其类型一起定义。

需要为每个属性分配一个唯一编号,称为标记。protocol buffer 使用此标记值来表示属性,而不是使用属性名称。

因此,与 JSON 中我们每次都传递属性名称firstName不同,protocol buffer 将使用数字 1 来表示firstName。响应的数据结构类似于请求的结构。

message HelloResponse {
    string greeting = 1;
}

4.3. 定义服务

最后,让我们定义服务。对于我们的HelloService,我们定义了一个hello()操作:

service HelloService {
    rpc hello(HelloRequest) returns (HelloResponse);
}

hello()操作接受HelloRequest请求并返回HelloResponse响应。gRPC 还通过在请求和响应中添加stream关​​键字来支持流式传输。

5. 生成代码

现在我们将HelloService.proto文件传递​​给编译器生成 Java 文件。有多种触发方式。

5.1。使用Protocol Buffer Compiler

首先,我们需要 Protocol Buffer Compiler。我们可以从这里提供的许多预编译二进制文件中进行选择。

此外,我们需要获取gRPC Java Codegen Plugin

最后,我们可以使用以下命令生成代码:

protoc --plugin=protoc-gen-grpc-java=$PATH_TO_PLUGIN -I=$SRC_DIR 
  --java_out=$DST_DIR --grpc-java_out=$DST_DIR $SRC_DIR/HelloService.proto

5.2. 使用 Maven 插件

作为开发人员,您希望代码生成与您的构建系统紧密集成。gRPC 为Maven 构建系统提供了一个protobuf-maven-plugin :

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

os-maven-plugin扩展/插件生成各种有用的平台相关项目属性,如${os.detected.classifier}

6. 创建服务器

无论您使用哪种方法生成代码,都会生成以下的文件:

  • HelloRequest.java –包含HelloRequest类型定义
  • HelloResponse.java -这包含HelleResponse类型定义
  • HelloServiceGrpc.HelloServiceImplBase  抽象类HelloServiceImplBase,它提供了我们在服务接口中定义的所有操作的实现,后面我们的服务就继承这这个类就可以了。

6.1。服务实现

抽象类HelloServiceImplBase默认实现是抛出运行时异常 io.grpc.StatusRuntimeException表示该方法未实现。

我们将扩展这个类并覆盖我们服务定义中提到的hello()方法:

public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {

    @Override
    public void hello(
      HelloRequest request, StreamObserver<HelloResponse> responseObserver) {

        String greeting = new StringBuilder()
          .append("Hello, ")
          .append(request.getFirstName())
          .append(" ")
          .append(request.getLastName())
          .toString();

        HelloResponse response = HelloResponse.newBuilder()
          .setGreeting(greeting)
          .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

如果我们将hello()的签名与我们在HellService.proto文件中编写的签名进行比较,我们会注意到它没有返回HelloResponse。相反,它将第二个参数作为StreamObserver<HelloResponse>,它是一个响应观察者,是服务器调用其响应的回调。

这样,客户端可以选择进行阻塞调用或非阻塞调用

gRPC 使用构建器来创建对象。我们使用HelloResponse.newBuilder()并设置问候文本来构建一个HelloResponse对象。我们将此对象设置给 responseObserver 的onNext()方法以将其发送给客户端。

最后,我们需要调用onCompleted()来指定我们已经完成了对 RPC 的处理,否则连接将被挂起,客户端将等待一致等待。

6.2. 启动 Grpc 服务器

接下来,我们需要启动 gRPC 服务器来监听传入的请求:

public class GrpcServer {
    public static void main(String[] args) {
        Server server = ServerBuilder
          .forPort(5000)
          .addService(new HelloServiceImpl()).build();

        server.start();
        server.awaitTermination();
    }
}

在这里,我们再次使用构建器在端口 5000上创建一个 gRPC 服务器,并添加我们定义的HelloServiceImpl服务。start()将启动服务器。在我们的示例中,我们将调用awaitTermination()以保持服务器在前台运行。

7. 创建客户端

gRPC 提供了一种通道,可以抽象出连接、连接池、负载平衡等底层细节。

我们将使用ManagedChannelBuilder创建一个频道。在这里,我们指定服务器地址和端口。

我们将使用没有任何加密的纯文本:

public class GrpcClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)
          .usePlaintext()
          .build();

        HelloServiceGrpc.HelloServiceBlockingStub stub 
          = HelloServiceGrpc.newBlockingStub(channel);

        HelloResponse helloResponse = stub.hello(HelloRequest.newBuilder()
          .setFirstName("奇点架构")
          .setLastName("gRPC")
          .build());

        channel.shutdown();
    }
}

接下来,我们需要创建一个Stub,用于对hello()进行实际的远程调用。存根是客户端与服务器交互的主要方式。当使用自动生成的Stub时,存根将具有用于包装通道的构造函数。

在这里,我们使用阻塞/同步Stub,以便 RPC 调用等待服务器响应,并且将返回响应或引发异常。gRPC 提供了另外两种类型的Stub,它们有助于非阻塞/异步调用。

最后,进行hello() RPC 调用。这里我们传递了HelloRequest。我们可以使用自动生成的set方法来设置HelloRequest对象的firstNamelastName属性。

我们取回从服务器返回的HelloResponse对象。

8. 结论

在本节中,我们看到了如何使用 gRPC 通过专注于定义服务并让 gRPC 处理所有样板代码来简化两个服务之间的通信开发。如果读者朋友之前么有接触过gRPC ,本节是一个很好的入门。因为Nacos服务端集群之间的通讯使用的是gRPC

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值