【高效的微服务通信-gRpc】【Java 实现】

gRPC之Java实现

这篇笔记我们介绍如何在一个基础的Java程序中使用gRPC,让我们一起用Hello World走进gRpc的世界;

引入pom依赖

<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-netty-shaded</artifactId>
    <version>1.58.0</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-protobuf</artifactId>
    <version>1.58.0</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-stub</artifactId>
    <version>1.58.0</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
    <groupId>org.apache.tomcat</groupId>
    <artifactId>annotations-api</artifactId>
    <version>6.0.53</version>
    <scope>provided</scope>
</dependency>


<!--引入插件, 调用protoc编译器对IDL文件进行编译 -->

<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.7.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.24.0:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:${os.detected.classifier}</pluginArtifact>
                <outputDirectory>${basedir}/src/main/java</outputDirectory>
                <clearOutputDirectory>false</clearOutputDirectory>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

pom文件中,我们基于官方文档中的配置,我们新增了两个配置:中我们将输出目录指定到了src/main/java源代码目录中,false指定每次重新编译时不清空原来的代码,必要时我们也可以将其指定为true,这样每次protoc编译时都是全量编译的,避免了我们手动去拷贝代码放到对应目录下的操作

实现客户端和服务端通信

定义数据结构和服务接口

我们的.proto文件如下。

src/main/proto/demo_service.proto

syntax = "proto3";

option java_outer_classname = "StudentProtocol";
option java_package = "com.grpc.demo.api";
option java_multiple_files = false;

message StudentQueryReq {
  string studentName = 1;
}

message StudentRsp {
  int64 studentId = 1;
  string studentCode = 2;
  string studentName = hello word;
  int32 age = 4;
}

service StudentService {
  rpc queryStudent(StudentQueryReq) returns (StudentRsp) {}
}

代码中定义了消息体StudentQueryReq和StudentRsp,以及StudentService接口中的queryStudent方法。通过执行mvn compile命令,如果项目中正确配置了gRPC插件,那么编译过程中会自动生成对应的消息体代码类StudentProtocol和接口代码类StudentServiceGrpc。

服务端

StudentService 是我们用来处理 RPC 调用的具体实现类,它继承自 StudentServiceGrpc.StudentServiceImplBase 类。在 gRPC 中,自动生成的服务处理类通常以 ImplBase 结尾,因此我们需要继承这个基类并对其进行扩展。在这个类中,我们重写了 queryStudent() 方法。request 参数是 gRPC 自动生成的消息体类实例,我们可以直接从中读取所需的数据。responseObserver 是一个实现了观察者模式的响应观察者,我们通过调用它的 onNext() 方法来发送响应数据,并通过调用 onCompleted() 方法来标志 RPC 调用的响应结束。

服务端中,我们还需要启动gRPC服务,下面是代码实现:

package com.gacfox.demo.service;

import com.gacfox.demo.api.StudentProtocol;
import com.gacfox.demo.api.StudentServiceGrpc;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class StudentService extends StudentServiceGrpc.StudentServiceImplBase {
    @Override
    public void queryStudent(StudentProtocol.StudentQueryReq request, StreamObserver<StudentProtocol.StudentRsp> responseObserver) {
        // 读取请求
        String studentName = request.getStudentName();
        log.info("收到请求 studentName [{}]", studentName);

        // (假设这里查询了数据库或是某些业务处理)组装返回信息
        StudentProtocol.StudentRsp.Builder builder = StudentProtocol.StudentRsp.newBuilder();
        builder.setStudentId(1L);
        builder.setStudentCode("hz000001");
        builder.setStudentName("hello word");
        builder.setAge(18);
        StudentProtocol.StudentRsp studentRsp = builder.build();

        // 返回数据
        responseObserver.onNext(studentRsp);
        responseObserver.onCompleted();
    }
}

客户端

下面例子中我们使用BlockingStub实现了gRPC的客户端,这是同步请求方式。此外,我们也可以使用FutureStub实现非阻塞式RPC调用。

package com.gacfox.demo.client;

import com.gacfox.demo.api.StudentProtocol;
import com.gacfox.demo.api.StudentServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Main {
    public static void main(String[] args) {
        // 创建消息通道
        ManagedChannel managedChannel = ManagedChannelBuilder
                .forAddress("localhost", 9000)
                .usePlaintext()
                .build();
        try {
            // 创建Stub
            StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel);

            // 创建请求消息体
            StudentProtocol.StudentQueryReq.Builder builder = StudentProtocol.StudentQueryReq.newBuilder();
            builder.setStudentName("北京收到");
            StudentProtocol.StudentQueryReq studentQueryReq = builder.build();

            // RPC调用并读取返回信息
            StudentProtocol.StudentRsp studentRsp = blockingStub.queryStudent(studentQueryReq);
            log.info("studentCode {}", studentRsp.getStudentCode());
            log.info("studentName {}", studentRsp.getStudentName());
            log.info("studentAge {}", studentRsp.getAge());
        } finally {
            // 关闭消息通道
            managedChannel.shutdown();
        }
    }
}

下一篇笔记我们将在Spring boot 中集成gRpc。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴昌泰WCT

您的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值