gRPC构建RPC服务

本文介绍如何使用Grpc来否件和发布RPC服务,本文在最后附上github地址。

1.下载&安装 java版本的protoc,下载地址

我这里使用的是3.3.0版本,如图所示

《gRPC构建RPC服务》 protoc-version.png

选择一个属于你的平台protoc来安装,安装很简单,只需要将下载好的protoc的zip包解压到指定目录即可。

2.创建maven工程

选择一款你喜欢的编译器创建一个maven工程,maven工程创建好之后,在其工程的根目录下的pom文件中添加如下内容:

pom.xml文件内容:

<properties>
    <grpc.version>1.6.1</grpc.version>
    <jdk.version>1.8</jdk.version>
    <protoc.version>3.3.0</protoc.version>
    <kr.motd.version>1.5.0.Final</kr.motd.version>
</properties>

<dependencies>
      <!--grpc工程依赖-->
       <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>${kr.motd.version}</version>
           </extension>
       </extensions>

       <plugins>
           <!-- protobuf插件 -->
           <plugin>
               <groupId>org.xolstice.maven.plugins</groupId>
               <artifactId>protobuf-maven-plugin</artifactId>
               <version>0.5.0</version>
               <!--在命令行直接编译即可-->
               <configuration>
                   <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
                   <pluginId>grpc-java</pluginId>
                   <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                   </pluginArtifact>
                   <!--protobuf文件路径,在这里我的proto3规则文件放在src/main/proto下-->
                   <protoSourceRoot>src/main/proto</protoSourceRoot>
                   <!--protoc可执行文件据对路径,在第一步中的下载的protoc安装包解压后得到的,这是我的安装路径-->
                   <protocExecutable>/Volumes/NETAC/soft/dev/protoc/bin/protoc</protocExecutable>
               </configuration>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>compile-custom</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>

           <!--jdk插件-->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <configuration>
                   <source>${jdk.version}</source>
                   <target>${jdk.version}</target>
               </configuration>
           </plugin>
       </plugins>
</build>

3.创建proto规则文件

在工程目录下的src/main下创建proto规则文件目录,目录结构如图所示:
《gRPC构建RPC服务》 proto_dir.png

3.1.创建规则文件

在这里我的proto规则文件中分别定义了Account和Greeter两个服务接口。

  • Greeter服务接口中定义了SayHello一个方法。
    在Greeter服务中的方法只是简单的打招呼方法。

  • Account服务接口中定义了addAccount和getAccoutByName两个方法。
    在Account服务中的这两个方法功能是模拟添加和查询账户。

3.1.1.定义服务

在创建好的proto目录下创建hello_account.proto规则文件,用于定义rpc服务,内容如下:

//使用proto3语法
syntax = "proto3";

//指定proto文件包名
package org.cooze.grpc.service;

//指定 java 包名
option java_package = "org.cooze.grpc.service";
//指定proto文件生成java文件后的类名
option java_outer_classname = "ServiceProto";

//开启多文件
option java_multiple_files = true;

//倒入指定的.proto文件
import "entity/req_res.proto";

//定义rpc服务接口
service Greeter {
  //服务端接口方法
  rpc SayHello (org.cooze.grpc.entity.HelloRequest) returns (org.cooze.grpc.entity.HelloReply);
}

//定义rpc服务接口
service Account {
   rpc addAccount(org.cooze.grpc.entity.Account) returns (org.cooze.grpc.entity.AccountResponse);
   rpc getAccoutByName(org.cooze.grpc.entity.Account) returns (org.cooze.grpc.entity.AccountResponse);
}

3.1.2.定义消息

在proto目录下的子目录entity中创建req_res.proto文件,用于定义rpc的消息类型,内容如下:

//使用proto3语法
syntax = "proto3";

//指定proto文件包名
package org.cooze.grpc.entity;

//指定 java 包名
option java_package = "org.cooze.grpc.entity";
//指定proto文件生成java文件后的类名
option java_outer_classname = "EntityProto";

//开启多文件
option java_multiple_files = true;

//请求参数
message HelloRequest {
  string name = 1;
}

//响应参数
message HelloReply {
  string message = 1;
}

message Account {
    string name = 1;
    string sex = 2;
    int32 age = 3;
}

message AccountResponse {
    string msg = 1;
    int32 code = 2;
    repeated Account results = 3;
}

proto3定义服务和消息,请参看我翻译的《gRPC之proto语法》,翻译难免带有个人主观色彩,请见谅.

3.2.编译proto文件

在创建好proto文件,并定义好消息类型和服务之后,结下来就是编译proto规则文件生成对应的java代码。

打开命令终端并切换到工程根目录下
我这里使用的编译器是idea编译器,所以打开终端很简单-_-!,终端打开后键入命令mvn compile编译即可,如图 :

《gRPC构建RPC服务》 compile.png

在执行完比那一命令之后,在工程根目录下会生成的target,而target目录的结构以及java类文件,如下图所示:

《gRPC构建RPC服务》 generator_code.png

其中,生成的服务定义接口类为:AccountGrpc和GreeterGrpc,消息类型类为:Account、AccountResponse、HelloReply、HelloRequest

4.Grpc服务和客户端实现

完成编辑proto规则文件和生成对应待grpc代码之后,就可以开始实现Grpc的服务端代码和客户端代码了。
创建java包结构和文件,如图所示:

《gRPC构建RPC服务》 create_package.png

4.1.Grpc服务端业务实现代码

不多说啥了,代码里有注释,所以上代码!

GreeterImpl.java代码如下:

public class GreeterImpl extends GreeterGrpc.GreeterImplBase {

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

        //处理接收到的消息
        String msg = reply.getMessage();
        System.out.println("服务端收到消息:" + msg);

        //响应消息
        HelloReply response = reply.toBuilder().setMessage("世界你好!").build();

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

    }
}

AccountImpl.java代码如下:

public class AccountImpl extends AccountGrpc.AccountImplBase {

    @Override
    public void addAccount(Account request, StreamObserver<AccountResponse> responseObserver) {
        //处理请求参数
        System.out.println(StringFormatter.format("新增用户:%s\n性别:%s\n年龄:%d岁", request.getName(), request.getSex(), request.getAge()).get());

        //处理响应参数
        AccountResponse response = AccountResponse.getDefaultInstance().toBuilder()
                .setCode(10000)
                .setMsg("success!").build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }

    @Override
    public void getAccoutByName(Account request, StreamObserver<AccountResponse> responseObserver) {
        //处理请求参数
        System.out.println(StringFormatter.format("请求查询用户名:%s", request.getName()).get());

        //处理响应参数
        List<Account> list = new ArrayList<>();
        Account account1 = Account.getDefaultInstance().toBuilder()
                .setName("张三")
                .setAge(20)
                .setSex("男").build();
        list.add(account1);

        Account account2 = Account.getDefaultInstance().toBuilder()
                .setAge(30)
                .setSex("男")
                .setName("李四").build();

        list.add(account2);

        AccountResponse response = AccountResponse.getDefaultInstance().toBuilder()
                .setCode(10000)
                .setMsg("success!")
                .addAllResults(list)
                .build();

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

4.2.Grpc客户端代码

不多说啥了,代码里有注释,所以上代码!

BaseClient.java代码如下:

public class BaseClient {

    private final ManagedChannel channel;
    private final GreeterGrpc.GreeterBlockingStub greeterBlockingStub;
    private final AccountGrpc.AccountBlockingStub accountBlockingStub;

    private BaseClient(ManagedChannel channel) {
        this.channel = channel;
        this.greeterBlockingStub = GreeterGrpc.newBlockingStub(channel);
        this.accountBlockingStub = AccountGrpc.newBlockingStub(channel);
    }

    /**
     * 构造客户端与Greeter 服务端连接 {@code host:port}
     *
     * @param host 主机地址
     * @param port 端口
     */
    public BaseClient(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)
                .build());
    }

    /**
     * 关闭函数
     * @throws InterruptedException
     */
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }

    public GreeterGrpc.GreeterBlockingStub getGreeterBlockingStub() {
        return greeterBlockingStub;
    }

    public AccountGrpc.AccountBlockingStub getAccountBlockingStub() {
        return accountBlockingStub;
    }
}

GreeterClient.java代码如下:

public class GreeterClient {

    private final GreeterGrpc.GreeterBlockingStub blockingStub;


    public GreeterClient(BaseClient client) {
        blockingStub = client.getGreeterBlockingStub();
    }

    public void greet(String name) {
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        HelloReply response;
        try {
            response = blockingStub.sayHello(request);
            String msg = response.getMessage();
            //接收到服务端返回的消息
            System.out.println("客户端收到消息:" + msg);
        } catch (StatusRuntimeException e) {
            return;
        }
    }
}

AccountClient.java代码如下:

public class AccountClient {

    private final AccountGrpc.AccountBlockingStub accountBlockingStub;

    private final BaseClient client;

    public AccountClient(BaseClient client) {
        this.client = client;
        this.accountBlockingStub = client.getAccountBlockingStub();
    }

    public void addAccount(String name, String sex, int age) {

        Account account = Account.getDefaultInstance().toBuilder()
                .setName(name)
                .setSex(sex)
                .setAge(age)
                .build();

        AccountResponse response = this.accountBlockingStub.addAccount(account);

        System.out.println(StringFormatter.format("返回消息:%s\n状态:%d", response.getMsg(), response.getCode()).get());
    }

    public void queryAccout(String name) {
        Account account = Account.getDefaultInstance().toBuilder()
                .setName(name).build();
        AccountResponse response = this.accountBlockingStub.getAccoutByName(account);

        System.out.println(StringFormatter.format("返回消息:%s\n状态:%d", response.getMsg(), response.getCode()).getValue());
        System.out.println("查询结果:");
        List<Account> list = response.getResultsList();
        for (Account acc : list) {
            System.out.println(StringFormatter.format("姓名:%s,性别:%s,年龄:%d", acc.getName(), acc.getSex(), acc.getAge()).get());
        }
    }
}

4.3.Grpc服务端启动引导类代码

不多说啥了,代码里有注释,所以上代码!

rpc引导类BootStrap.java代码如下:

public class BootStrap {

    private Server server;

    /**
     * 服务启动类
     *
     * @param port 端口
     * @throws IOException
     */
    private void start(int port) throws IOException {
        server = ServerBuilder.forPort(port)
                //注册服务
                .addService(new GreeterImpl())
                .addService(new AccountImpl())
                .build()
                .start();

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                System.err.println("*** JVM 关闭,导致gRPC服务关闭!");
                BootStrap.this.stop();
                System.err.println("*** 服务关闭");
            }
        });
    }

    /**
     * RPC 服务关闭
     */
    private void stop() {
        if (server != null) {
            server.shutdown();
        }
    }

    /**
     * 设置守护进程
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    /**
     * RPC服务启动main函数
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        final BootStrap server = new BootStrap();
        server.start(50051);
        server.blockUntilShutdown();
    }
}

4.3.Grpc测试

测试类代码如下:

public class Test {
    public static void main(String[] args) throws Exception {

        BaseClient client = new BaseClient("localhost", 50051);
        try {
            System.out.println("===============GreeterClient============");
            GreeterClient greeterClient = new GreeterClient(client);
            greeterClient.greet("Hello");

            System.out.println("===============AccountClient============");
            AccountClient accountClient = new AccountClient(client);

            System.out.println("===============新增============");
            accountClient.addAccount("张飞", "男", 45);

            System.out.println("===============查找============");
            accountClient.queryAccout("测试");


        } finally {
            client.shutdown();
        }
    }

}

好了,所有的前序工作都做完了,开始测试了!

  • 启动rpc引导类BootStrap.java
  • 运行客户端测试类

服务端效果图如下:

《gRPC构建RPC服务》 server.png

客户端效果图如下:

《gRPC构建RPC服务》 client.png

打完收工,项目代码地址

    原文作者:cooze
    原文地址: https://www.jianshu.com/p/46699d2f001f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞