【Java】gRPC与Spring boot继承实现示例

上篇文章介绍了如何通过一个简单的maven项目实现gRPC客户端与服务端通信协议测试。但是这个只是一个普通的Java工程,我们在Java web开发的今天大多数Java程序员都是通过SpringCloud及SpringBoot开启微服务或者单个应用的开发,我们最好通过springboot继承gRPC,这样一个工程既可以支持web开发,也可以支持gRPC协议通信。庆幸的是有大佬yidongnan早为我们准备好了grpc相关starter,对应的开源代码地址如下:

https://github.com/yidongnan/grpc-spring-boot-starter

grpc-client-spring-boot-starter:client端start,实现自动注入stub的实现

grpc-client-spring-boot-autoconfigure:client端grpc Springboot配置文件自动加载注入

grpc-server-spring-boot-starter:server端启动starter,实现自动注入XXXServiceGrpc

grpc-server-spring-boot-autoconfigure:server端grpc springboot配置文件自动加载注入

这里是大佬yidongnan维护的项目:gRPC-Spring-Boot-Starter 文档及源码

gRPC-Spring-Boot-Starter 文档 | grpc-spring

具体server端实现和client端实现完全可以参考大佬的examples,完全足够让你理解其实现的便捷性,你也可以按照其参考实现,提供maven和gradle两种构建方式。如果你在看的过程中还有些许不懂,下面是我按照自己的理解把之前gRPC逻辑重新实现了一下,看这篇文章之前,建议先看一下我之前的一篇文章:
【JAVA】protobuf在Java中代码测试用例-CSDN博客

我们主要做以下工作

创建一个父项目(springboot-rpc),包括以下几个module

  1. 定义服务调用接口程序(rpc-interface),一个普通工程
  2. 创建server端Springboot服务(rpc- server),依赖接口程序
  3. 创建client端Springboot服务(rpc-client),依赖接口程序

一、创建父项目

1.父项目pom.xml,在springboot-rpc工程配置子module需要用到的maven依赖

    org.example
    springboot-rpc
    1.0-SNAPSHOT
    pom
    
        rpc-interface
        rpc-server
        rpc-client
    
    
        UTF-8
        1.59.1
        3.24.0
        3.24.0
        
        1.8
        1.8
    
    
        
            
                io.grpc
                grpc-netty-shaded
                ${grpc.version}
                runtime
            
            
                io.grpc
                grpc-protobuf
                ${grpc.version}
            
            
                io.grpc
                grpc-services
                ${grpc.version}
            
            
                io.grpc
                grpc-stub
                ${grpc.version}
            
             
                org.apache.tomcat
                annotations-api
                6.0.53
                provided
            
            
                com.google.protobuf
                protobuf-java
                ${protobuf.version}
            
            
                com.google.protobuf
                protobuf-java-util
                ${protobuf.version}
            
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.1
                
                    1.8
                    1.8
                    UTF-8
                
            
        
    

二、定义接口程序

1.定义接口定义程序rpc-interface maven依赖配置pom.xml

    
        org.example
        springboot-rpc
        1.0-SNAPSHOT
    

    rpc-interface

    
        UTF-8
        
        1.8
        1.8
    


    
        
            io.grpc
            grpc-protobuf
            
        
        
            io.grpc
            grpc-stub
            
        
        
            com.google.protobuf
            protobuf-java
        
        
            com.google.protobuf
            protobuf-java-util
        
        
            
            jakarta.annotation
            jakarta.annotation-api
            1.3.5
            true
        
    

    
        
            
                kr.motd.maven
                os-maven-plugin
                1.7.1
            
        
        
            
                org.codehaus.mojo
                build-helper-maven-plugin
                3.3.0
                
                    
                        test
                        generate-sources
                        
                            add-source
                        
                        
                            
                                ${project.basedir}/target/generated-sources
                            
                        
                    
                
            

            
                com.github.os72
                protoc-jar-maven-plugin
                3.11.4
                
                    
                        generate-sources
                        
                            run
                        
                        
                            protoc
                            true
                            ${protobuf.version}
                            true
                            
                            
                            
                                src/main/protobuf
                            
                            
                                
                                    java
                                
                                
                                    grpc-java
                                    io.grpc:protoc-gen-grpc-java:1.48.1
                                
                            
                            
                            src/main/java
                        
                    
                
            
        
    

2.定义proto文件

生成源码需要依赖proto文件,文件内容如下:

syntax="proto3";
option go_package="./;student"; //关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,student代表了生成的go文件的包名是student

option java_multiple_files = true; //表示下面的message需要编译成多个java文件
option java_package = "grpc.student"; //指定该proto文件编译成的java源文件的包名
option java_outer_classname = "StudentProto"; // 表示下面的message编译成的java类文件的名字

package student; // 定义作用域

service DemoService {
  rpc Sender(StudentRequest) returns (StudentResponse){}
}

message StudentRequest {
  string Id = 1;
}

message StudentResponse {
  bytes result =1;
}

message Student {
  int64 Id = 1;
  string Name =2;
  string No =3;
}

3.生成源码程序

依赖student.proto文件,执行编译生成源码文件,执行如下命令

mvn clean compile #当前rpc-interface工程执行

将生成的源码拷贝到src/main/grpc/student包下,工程结构如下:

rpc-interface
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── grpc
    │   │       └── student
    │   │           ├── DemoServiceGrpc.java
    │   │           ├── Student.java
    │   │           ├── StudentOrBuilder.java
    │   │           ├── StudentProto.java
    │   │           ├── StudentRequest.java
    │   │           ├── StudentRequestOrBuilder.java
    │   │           ├── StudentResponse.java
    │   │           └── StudentResponseOrBuilder.java
    │   ├── protobuf
    │   │   └── student.proto
    │   └── resources

三、创建gRPC Server服务

server端为了能依赖spring自动注入我们需要的Service实现,需要加入依赖grpc-server-spring-boot-starter,此starter自动根据springboot配置文件自动注入我们需要的service。

1.rpc-server pom.xml配置如下

    
        org.example
        springboot-rpc
        1.0-SNAPSHOT
    

    rpc-server
    

        
            net.devh
            grpc-server-spring-boot-starter
            2.15.0.RELEASE
        
        
        
            org.springframework.boot
            spring-boot-starter-web
            2.7.16
        
        
        
            org.example
            rpc-interface
            1.0-SNAPSHOT
        
    

2.rpc-server配置文件application.yml

spring:
  application:
    name: student-rpc-server  #rpc client根据此名称配置

server:
  servlet:
    context-path: /
  port: 9999

#grpc server端暴露的端口
grpc:
  server:
    port: 10005

3.编写server端的接口实现

在rpc- interface中定义的接口sender()方法需要在这里实现,并标注@GrpcService注解,代码如下:

@GrpcService
public class RpcServerImpl extends DemoServiceGrpc.DemoServiceImplBase {
    private Logger log = LoggerFactory.getLogger(RpcServerImpl.class.getName());
    @Override
    public void sender(StudentRequest request, StreamObserver responseObserver) {
        if (Strings.isNullOrEmpty(request.getId())){
            log.warn("request get param id is null");
            return;
        }
        int id = Integer.parseInt(request.getId());
        //build a student object then serialize it
//            Student student = Student.newBuilder().build().;
        Student.Builder builder = Student.newBuilder();
        builder.setId(id);
        builder.setName("easton");
        builder.setNo("10001");
        Student student = builder.build();
        //try catch 属于测试序列化与反序列化代码块
        ByteString jsonBs = null;
        try {
            //protobuf 序列化
            String jsonStr = JsonFormat.printer().print(student);
            log.info("json format:"+jsonStr);
            jsonBs =  ByteString.copyFromUtf8(jsonStr);
            //反序列化
            byte[] bytes = student.toByteArray();
            Student student1 = Student.parseFrom(bytes);

        } catch (InvalidProtocolBufferException e) {
            throw new RuntimeException(e);
        }
//            ByteString bs =  student.toByteString();
        StudentResponse response = StudentResponse.newBuilder().setResult(jsonBs).build();
        responseObserver.onNext(response);
        //需要告诉客户端数据写完,否则客户端会一直等待数据回传结束
        responseObserver.onCompleted();
        log.info("server method run  finish");
    }

}

4.编写rpc-server启动类 

@SpringBootApplication
public class BootStrarpApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootStrarpApplication.class,args);
    }
}

四、创建gRPC client服务

创建完server端后接着编写客户端springboot服务

1.rpc-client pom.xml配置

跟服务端相对应需要引入依赖:grpc-client-spring-boot-starter,此starter会自动注入客户端stub实现,依赖配置为:

    
        org.example
        springboot-rpc
        1.0-SNAPSHOT
    

    rpc-client

    
        
            net.devh
            grpc-client-spring-boot-starter
            2.15.0.RELEASE
        
        
        
            org.springframework.boot
            spring-boot-starter-web
            2.7.16
        
        
        
            org.example
            rpc-interface
            1.0-SNAPSHOT
        
    

2.rpc-client stub注入的代码调用实现

定义方法,此方法用来gRPC调用服务端sender()方法

@Service
public class StudentClientService {

    //初始化student-rpc-server对应的stub,如果需要多个可以在这里注入
    @GrpcClient("student-rpc-server")
    private DemoServiceGrpc.DemoServiceBlockingStub blockingStub;

    Logger logger = LoggerFactory.getLogger(StudentClientService.class);
    public String sendToServer(int id){
        logger.info("Will try to send " + id + " ...");
        StudentRequest request = StudentRequest.newBuilder().setId(String.valueOf(id)).build();
        StudentResponse response;
        try{
            response = blockingStub.sender(request);

        }catch (StatusRuntimeException e){
            e.printStackTrace();
            logger.warn("RPC failed: {0}", e.getStatus());
            return "";
        }
        ByteString byteString = response.getResult();
        String result = byteString.toStringUtf8();
        logger.info("Result: " +result);
        return result;
    }
}

3.rpc-client启动类

@SpringBootApplication
public class BootStrapApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootStrapApplication.class,args);
    }
}

4.rpc-client 配置文件application.yml配置

server:
  port: 8080
  servlet:
    context-path: /
spring:
  application:
    name: student-rpc-client
grpc:
  client:
    student-rpc-server:   #服务名称不能写错,这个需要和server端spring.application.name的定义的名称一致,否默认的negotiationType:tls
      address: 'static://localhost:10005'
      # 是否开启保持连接(长连接)
      enableKeepAlive: true
      # 保持连接时长(默认20s)
      keepAliveTimeout: 20s
      # 没有RPC调用时是否保持连接(默认false,可禁用避免额外消耗CPU)
      keepAliveWithoutCalls: false
      # 客户端负载均衡策略(round_robin(默认), pick_first)
      defaultLoadBalancingPolicy: round_robin
      # 通信类型
      # plaintext | plaintext_upgrade | tls
      # 明文通信且http/2 | 明文通信且升级http/1.1为http/2 | 使用TLS(ALPN/NPN)通信
      negotiationType: plaintext
#    GLOBAL: 可以指定所有grpc通用配置

5.定义一个测试接口

由于rpc- client开启了web服务,为了模拟接口调用,以Restful协议请求rpc-client http接口,http接口调用grpc server服务,创建接口如下:

@RestController
public class DemoClientController {

    @Autowired
    private StudentClientService clientService;
    @RequestMapping(value = "/getResult",method = RequestMethod.GET)
    public String getResult(@RequestParam("id") String id){
        return  clientService.sendToServer(Integer.valueOf(id));
    }
}

五、调用测试

1.启动服务端rpc-server

2.启动客户端rpc-client

3.通过浏览器URL请求测试:http://localhost:8080/getResult?id=111

{ "Id": "111", "Name": "easton", "No": "10001" }

 

你可能感兴趣的:(RPC,JAVA语言,java,spring,boot,spring,cloud)