锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 【Java】gRPC与Spring boot继承实现示例

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

0
  • 软件开发
  • 发布于 2024-08-16
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

上篇文章介绍了如何通过一个简单的 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 依赖
<groupId>org.example</groupId>
    <artifactId>springboot-rpc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
        <module>rpc-interface</module>
        <module>rpc-server</module>
        <module>rpc-client</module>
    </modules>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <grpc.version>1.59.1</grpc.version><!-- CURRENT_GRPC_VERSION -->
        <protobuf.version>3.24.0</protobuf.version>
        <protoc.version>3.24.0</protoc.version>
        <!-- required for JDK 8 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-netty-shaded</artifactId>
                <version>${grpc.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-protobuf</artifactId>
                <version>${grpc.version}</version>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-services</artifactId>
                <version>${grpc.version}</version>
            </dependency>
            <dependency>
                <groupId>io.grpc</groupId>
                <artifactId>grpc-stub</artifactId>
                <version>${grpc.version}</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>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java</artifactId>
                <version>${protobuf.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.protobuf</groupId>
                <artifactId>protobuf-java-util</artifactId>
                <version>${protobuf.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

二、定义接口程序

  1. 定义接口定义程序 rpc-interface maven 依赖配置 pom.xml
<parent>
        <groupId>org.example</groupId>
        <artifactId>springboot-rpc</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>rpc-interface</artifactId>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- required for JDK 8 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
 
 
    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <!--<version>1.59.1</version>-->
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <!--<version>1.59.1</version>-->
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
        </dependency>
        <dependency>
            <!-- Java 9+ compatibility - Do NOT update to 2.0.0 -->
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>1.3.5</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
 
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.7.1</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <id>test</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.basedir}/target/generated-sources</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
 
            <plugin>
                <groupId>com.github.os72</groupId>
                <artifactId>protoc-jar-maven-plugin</artifactId>
                <version>3.11.4</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <protocCommand>protoc</protocCommand><!-- brew install protobuf -->
                            <optimizeCodegen>true</optimizeCodegen>
                            <protocVersion>${protobuf.version}</protocVersion>
                            <includeStdTypes>true</includeStdTypes>
                            <!-- 导入其他proto文件
                            <includeDirectories>
                                <include>src/main/more_proto_imports</include>
                            </includeDirectories>
                            -->
                            <!--指定proto文件 -->
                            <inputDirectories>
                                <directory>src/main/protobuf</directory>
                            </inputDirectories>
                            <outputTargets>
                                <outputTarget>
                                    <type>java</type>
                                </outputTarget>
                                <outputTarget>
                                    <type>grpc-java</type>
                                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.48.1</pluginArtifact>
                                </outputTarget>
                            </outputTargets>
                            <!--生成代码输出目录-->
                            <outputDirectory>src/main/java</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
  1. 定义 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;
}
  1. 生成源码程序

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

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

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

<parent>
        <groupId>org.example</groupId>
        <artifactId>springboot-rpc</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>rpc-server</artifactId>
    <dependencies>
 
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>2.15.0.RELEASE</version>
        </dependency>
        <!--
            如果需要提供web服务可以加入此依赖,版本注意按照grpc-server-spring-boot-starter依赖的spring-boot版本保持一致即可。
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.16</version>
        </dependency>
        <!--
            依赖接口定义
        -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>rpc-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

三、创建 gRPC Server 服务

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

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

spring:
  application:
    name: student-rpc-server  #rpc client根据此名称配置
 
server:
  servlet:
    context-path: /
  port: 9999
 
#grpc server端暴露的端口
grpc:
  server:
    port: 10005

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

@GrpcService
public class RpcServerImpl extends DemoServiceGrpc.DemoServiceImplBase {
    private Logger log = LoggerFactory.getLogger(RpcServerImpl.class.getName());
    @Override
    public void sender(StudentRequest request, StreamObserver<StudentResponse> 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");
    }
 
}
  1. 编写 server 端的接口实现

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

@SpringBootApplication
public class BootStrarpApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootStrarpApplication.class,args);
    }
}
  1. 编写 rpc-server 启动类 
<parent>
        <groupId>org.example</groupId>
        <artifactId>springboot-rpc</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
 
    <artifactId>rpc-client</artifactId>
 
    <dependencies>
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>2.15.0.RELEASE</version>
        </dependency>
        <!--
            web服务需要依赖此starter
        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.16</version>
        </dependency>
        <!--
            依赖接口定义
        -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>rpc-interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

四、创建 gRPC client 服务

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

1.rpc-client pom.xml 配置

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

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

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

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

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

3.rpc-client 启动类

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通用配置

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

@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- 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" }
标签: #Spring Boot 173 #软件开发 1171 #JAVA 991
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.