十六.SpringCloudAlibaba极简入门-整合Grpc代替OpenFeign

前言

他来了他来了,停了快2个月了终于又开始更新文章啦,这次带来的绝对是干货!!!。由于公司项目进行重构的时候考虑到,OpenFeign做为服务通信组件在高并发情况下有一定的性能瓶颈,所以将其替换为更高性能的通信组件Grpc,Grpc也是业界比较流行的服务通信组件,底层采用HTTP/2进行网络通信性能上高于基于Http/1.1的OpenFeign。 我将把Grpc的整合过程整理成文字分享给大家,喜欢请三连!!!,你的鼓励是我坚持下去的动力。

认识Grpc

OpenFeign基于 HTTP/1.1 协议。主要使用 JSON 或 XML 格式进行数据交换,这些格式更加人性化但分装臃肿效率较低。其主要用于 RESTful API 调用,支持声明式的 Web 服务客户端。

而 gRPC 是一个高性能、开源和通用的 RPC(远程过程调用)框架,基于 Protocol Buffers 序列化协议开发,在内部实现上,它采用了 HTTP/2 协议作为传输层协议的一部分来实现高效的双向流通信的能力等特性让它成为很多开发者热衷的工具库之一;gRPC 在处理大量数据时表现优异,适用于需要快速响应的应用场景,特别是在微服务架构中。

gRPC:

  • 强调高效的数据传输和低延迟通信。如金融交易、在线游戏等对延迟要求极高的场景
  • 适合构建高性能的分布式系统和服务间通信。在大规模微服务架构中,gRPC 可以显著提高系统的整体性能。
  • 支持流式传输,可以实现长连接和实时数据推送。

OpenFeign:

  • 注重简化开发者的代码编写工作。
  • 更加灵活,可以轻松地集成到现有的 Spring Boot 项目中。
  • 适用于轻量级的 RESTful API 调用。

总结来说,gRPC 和 OpenFeign 各有优势,选择哪种技术取决于具体的业务需求和技术栈。如果你需要高性能、低延迟的服务间通信,gRPC 是更好的选择;如果你希望简化开发流程并快速集成 RESTful API,OpenFeign 则更为合适。

案例实战

1.搭建工程

首先搭建一个SpringBoot父工程,父工程下分别有:grpc-api ,grpc-provider ,grpc-consumer 三个模块,分别代表:Grpc接口,生产者,消费者。父工程管理的依赖如下

<properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>21</java.version>
        <skipTests>true</skipTests>
        <spring-cloud.version>2023.0.1</spring-cloud.version>
        <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
        <hutool.version>5.8.28</hutool.version>
        <lombok.version>1.18.32</lombok.version>
    </properties>

    <!--SpringBoot依赖-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
    </parent>

    <dependencies>
        <!--   常用工具类     -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <!--SpringCloud 2020.* 禁用了bootstrap 而注册中心等配置文件需要 bootstrap.yml 为名的配置文件-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <!--注解配置器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2.API模块

api模块需要导入grpc相关依赖,以及编写proto文件,我们根据proto文件自动生成Grpc的API接口文件,需要导入的依赖如下

<dependencies>
        <!-- https://mvnrepository.com/artifact/net.devh/grpc-server-spring-boot-starter -->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</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.12.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}</pluginArtifact>
                    <outputDirectory>${project.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>

grpc-server-spring-boot-starter 和 grpc-client-spring-boot-starter 是Grpc的服务端和客户端的依赖,javax.annotation-api是对Grpc代码生成注解的支持,不导入该包会报错。然后导入了os-maven-plugin插件,以及 protobuf-maven-plugin 插件用来根据proto文件生成API代码的。

接着我们再API模块中main目录下创建一个proto目录,并创建一个文件User.proto,内容如下

syntax = "proto3";
/** 生产的代码合并到一个文件 **/
option java_multiple_files = false;
/** 生产代码输出的包路径 **/
option java_package = "cn.whale.api.User";


/** API接口 **/
service UserApi {

  /** Grpc API 接口 **/
  rpc getById(GetUserReq) returns (GetUserRep) {}
}

/** 响应对象 **/
message GetUserReq {
  /** Long 用户ID **/
  int64 id = 1;
}

/** 结果对象 **/
message GetUserRep {
  /** Long 用户ID **/
  int64 id = 1;
  /** String 用户名字 **/
  string name = 2;
}

该文件是用来定义Grpc Api接口的,具体请看上面的注释,需要注意的是定义Protobuf 的 message 对象的字段类型可不能使用Java的类型,int64 对应Long, string 对应 String 具体请看:https://protobuf.com.cn/programming-guides/proto3/#specifying-types

编写好proto文件后,对api模块进行compile(IDEA 右侧 - api模块 - lifecycle - compile) ,然后会自动在API模块中生成Grpc相关的 API接口。
在这里插入图片描述

3.提供者服务

提供者服务首先是需要向Nacos注册(其他注册中心也行)的,然后导入api模块,编写Grpc的提供者服务,导入如下依赖

    <dependencies>
        <!--grpc api 模块 -->
        <dependency>
            <groupId>cn.whale</groupId>
            <artifactId>grpc-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <!--nacos服务发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    
        <!--配置管理 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!--grpc服务端依赖,api模块中引入了,这里其实可以不引-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
    </dependencies>

接着对提供者服务进行配置,启动类正常写,没有什么新东西,bootstrap.yml配置如下

server:
  port: 8081 #tomcat 端口
grpc:
  server:
    port: 7071 #Grpc端口

  client: #Grpic客户端配置
    GLOBAL:
      negotiation-type: plaintext #协议类型,明文传输,也可以使用TLS进行加密传输,服务内部通信使用明文即可
      enable-keep-alive: true
      keep-alive-without-calls: true
spring:
  application:
    name: grpc-provider #服务名
  cloud:
    nacos:
      discovery: #服务注册
        server-addr: nacos地址:8848
      config: #配置管理
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yml

接下来编写提供者服务的Grpc实现,创建一个类UserApiImpl 继承UserApiGrpc,具体如下

package cn.whale.grpc;

import cn.whale.api.User.User;
import cn.whale.api.User.UserApiGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

/**
 * 标记Grpc提供者服务
 */
@GrpcService
public class UserApiImpl extends UserApiGrpc.UserApiImplBase {

    /**
     * 接口方法实现
     * @param request :请求对象
     * @param responseObserver : 消息流对象,用来响应结果
     */
    @Override
    public void getById(User.GetUserReq request, StreamObserver<User.GetUserRep> responseObserver) {
        long id = request.getId();
        //TODO :去数据查询数据
        //封装结果数据
        User.GetUserRep userRep = User.GetUserRep.newBuilder().setId(id).setName("zs").build();
        //响应结果
        responseObserver.onNext(userRep);
        responseObserver.onCompleted();
        //异常情况
        //responseObserver.onError(exception);

    }
}

  • @GrpcService :Grpc的服务端注解
  • UserApiGrpc.UserApiImplBase :根据Proto自动生成的API接口
  • getById :我们定义的API接口方法,具体请看代码注释

到这里提供者就编写完成了,启动提供者服务,可以看到Grpc的端口

在这里插入图片描述

4.消费者服务

消费者服务也是要做服务注册,然后引入api模块,以及Grpc客户端依赖,具体如下

<dependencies>
        <dependency>
            <groupId>cn.whale</groupId>
            <artifactId>grpc-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>3.0.0.RELEASE</version>
        </dependency>
    </dependencies>

编写启动类和创建bootstrap.yml文件

server:
  port: 8082
grpc:
  server:
    port: 7072
  client:
    GLOBAL:
      negotiation-type: plaintext
      enable-keep-alive: true
      keep-alive-without-calls: true
spring:
  application:
    name: grpc-consumer
  cloud:
    nacos:
      discovery:
        server-addr: nacos地址:8848
      config:
        server-addr: ${spring.cloud.nacos.discovery.server-addr}
        file-extension: yml

接着我们编写一个Conroller来实现和提供者的Grpc通信,代码如下

@RestController
@Slf4j
public class BusinessController {

    //指向提供者的服务名
    @GrpcClient(value = "grpc-provider")
    private UserApiGrpc.UserApiBlockingStub userApiBlockingStub;

    @GetMapping("/user")
    public String getUserById(){
        User.GetUserRep getUserRep = userApiBlockingStub.getById(User.GetUserReq.newBuilder().setId(1L).build());
        log.info("查询到用户信息,id =  {},name = {}",getUserRep.getId(),getUserRep.getName());
        return getUserRep.toString();
    }
}
  • @GrpcClient(value = “grpc-provider”) : 客户端注解,value指向了注册中心的提供者服务的名字,底层会自动进行负载均衡
  • UserApiGrpc.UserApiBlockingStub : Grpc 同步接口,它也支持异步调用UserApiGrpc.UserApiFutureStub

到这里消费者就编写完成了,启动消费者,通过浏览器访问/user,就可以拿到提供者返回的数据了

在这里插入图片描述

5.注意事项

最后说几个注意事项

  1. Grpc生成的对象只提供了Builder的方式设置值,所以没办法通过BeanUtils等工具进行对象之间的自动转换的,需要手动给Proto对象设置值。见生产者 User.GetUserRep
  2. 如果给Proto对象设置了空值会报错,所以在给对象设置值的时候建议先判断空值,然后给一个默认值,比如:String为null,就指定一个“”空字符串
  3. Grpc是无法返回一个null对象的,所以消费者端到底有没有拿到一个有效的结果不能直接用null来判断,需要取出具体的值来判断

就写到这把,剩下的坑大家自己去踩,如果文章对你有帮助请给个好评!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/919424.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】机器学习中用到的高等数学知识-7.信息论 (Information Theory)

熵 (Entropy)&#xff1a;用于评估信息的随机性&#xff0c;常用于决策树和聚类算法。交叉熵 (Cross-Entropy)&#xff1a;用于衡量两个概率分布之间的差异&#xff0c;在分类问题中常用。 信息论作为处理信息量和信息传输的数学理论&#xff0c;在机器学习中具有广泛的应用。…

高亮变色显示文本中的关键字

效果 第一步&#xff1a;按如下所示代码创建一个用来高亮显示文本的工具类&#xff1a; public class KeywordUtil {/*** 单个关键字高亮变色* param color 变化的色值* param text 文字* param keyword 文字中的关键字* return*/public static SpannableString highLigh…

【图像处理识别】数据集合集!

本文将为您介绍经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 CNN-ImageProc-Robotics 机器人 更新时间&#xff1a;2024-07-29 访问地址: GitHub 描述&#xff1a; 通过 CNN 和图像处理进行机器人对象识别项目侧重于集成最先进的深度学习技术和…

VTK知识学习(10)- 渲染引擎

1、前言 vtkProp; vtkAbstractMapper; vtkProperty; vtkCamera; vtkLight; vtkRenderer; vtkRenderWindow; vtkRenderWindowInteractor; vtkTransform; vtkLookupTable;………… 这些类都是与数据显示或渲染相关的。 用计算机图形学的专业词汇来说&#xff0c;就是它…

网络基础(3)https和加密

http其它的报头 直接看图片&#xff1a; 上图中的第一个和第二个类型之前已经使用过了也就不多做说明了&#xff0c;第三个报头类型使用的很少了。第四个报头类型主要就使用在一些灰度更新的应用上&#xff0c;确定用户使用的软件的版本不让其访问该版本不能访问的功能。下一个…

高阶C语言之五:(数据)文件

目录 文件名 文件类型 文件指针 文件的打开和关闭 文件打开模式 文件操作函数&#xff08;顺序&#xff09; 0、“流” 1、字符输出函数fputc 2、字符输入函数fgetc 3、字符串输出函数fputs 4、 字符串输入函数fgets 5、格式化输入函数fscanf 6、格式化输出函数fpr…

C#获取视频第一帧_腾讯云媒体处理获取视频第一帧

一、 使用步骤&#xff1a; 第一步、腾讯云开启万象 第二步、安装Tencent.QCloud.Cos.Sdk 包 第三步、修改 腾讯云配置 图片存储目录配置 第四步、执行获取图片并保存 二、封装代码 using System.Text; using System.Threading.Tasks;using COSXML.Model.CI; using COSXML.A…

分词器的概念(通俗易懂版)

什么是分词器&#xff1f;简单点说就是将字符序列转化为数字序列&#xff0c;对应模型的输入。 通常情况下&#xff0c;Tokenizer有三种粒度&#xff1a;word/char/subword word: 按照词进行分词&#xff0c;如: Today is sunday. 则根据空格或标点进行分割[today, is, sunda…

Jenkins更换主题颜色+登录页面LOGO图片

默认主题和logo图片展示 默认主题黑色和白色。 默认LOGO图片 安装插件 Login ThemeMaterial Theme 系统管理–>插件管理–>Available plugins 搜不到Login Theme是因为我提前装好了 没有外网的可以参考这篇离线安装插件 验证插件并修改主题颜色 系统管理–>A…

11.19.2024刷华为OD

文章目录 HJ51HJ53 杨辉三角HJ56HJ57 高精度整数加法HJ58HJ60 简单题HJ63 DNA序列&#xff08;简单题&#xff09;语法知识记录 HJ51 https://www.nowcoder.com/practice/54404a78aec1435a81150f15f899417d?tpId37&tags&title&difficulty0&judgeStatus0&…

C语言零基础入门

一、输入输出 &#xff08;1&#xff09;scanf scanf 是C语言中的一个标准库函数&#xff0c;用于从标准输入&#xff08;通常是键盘&#xff09;读取数据。scanf 函数定义在 <stdio.h> 头文件中。 #include <stdio.h>int main(void) {//读取整数 int num;print…

应聘美容师要注意什么?博弈美业收银系统/管理系统/拓客系统分享建议

随着美容行业的不断发展&#xff0c;成为一名优秀的美容师需要具备一系列重要的技能和品质。无论是在面试过程中还是在实际工作中&#xff0c;以下建议将帮助你在应聘美容师职位时脱颖而出&#xff1a; ▶ 专业技能和资格 首先&#xff0c;确保你具备所需的专业技能和资格。这…

JVM性能分析工具JProfiler的使用

一、基本概念 JProfiler&#xff1a;即“Java Profiler”&#xff0c;即“Java分析器”或“Java性能分析工具”。它是一款用于Java应用程序的性能分析和调试工具&#xff0c;主要帮助开发人员识别和解决性能瓶颈问题。 JVM&#xff1a;即“Java Virtual Machine”&#xff0c…

css鼠标移动效果高亮追随效果

如图所示&#xff0c;鼠标移动有一块高亮随着鼠标移动。代码如下&#xff1a;(vue3篇) <div class"container"><span class"use-hover-hglh-element trail" :style"isShow ? dyStyle : { opacity: 0 }"></span></div>…

PHP屏蔽海外IP的访问页面(源代码实例)

PHP屏蔽海外IP的访问页面&#xff08;源代码实例&#xff09;&#xff0c;页面禁用境外IP地址访问 <?php/*** 屏蔽海外ip访问* 使用ip2long函数得到ip转为整数的值&#xff0c;判断值是否在任一一个区间中* 以下是所有国内ip段* 调用方法&#xff1a;IschinaIp($ALLIPS)* …

现代分布式系统新法宝:基于单元的架构

- 前言 - 数十年来&#xff0c;IT 业界一直在努力掌握分布式系统。然而&#xff0c;随着系统日益复杂&#xff0c;给开发数字产品的组织带来巨大挑战。可以说&#xff0c;分布式系统最棘手的方面之一是面对故障时的可靠性&#xff0c;特别是现代分布式系统使用大量物理与虚拟资…

2.8 群辉 黑群晖 意味断电 抱歉,您所指定的页面不存在。

实验室组装的黑群晖施工时不小心被意味断电&#xff0c;然后出现了如下图&#xff1a; 对于7.1.1的系统来说&#xff0c;这个是由于libsynopkg.so.1和libsynoshare.so.7这两个文件出问题所致。 因此&#xff0c;解决方法也比较简单就是把好的文件恢复到/lib文件夹下即可。 这…

【视频讲解】Python深度神经网络DNNs-K-Means(K-均值)聚类方法在MNIST等数据可视化对比分析...

全文链接&#xff1a;https://tecdat.cn/?p38289 分析师&#xff1a;Cucu Sun 近年来&#xff0c;由于诸如自动编码器等深度神经网络&#xff08;DNN&#xff09;的高表示能力&#xff0c;深度聚类方法发展迅速。其核心思想是表示学习和聚类可以相互促进&#xff1a;好的表示会…

K8S资源限制之ResourceQuota

ResourceQuota介绍 在K8S中&#xff0c;大部分资源都可以指定到一个名称空间下&#xff0c;因此可以对一个名称空间的计算资源&#xff0c;存储资源&#xff0c;资源数量等维度做资源限制。 如限制pod数量、svc数量&#xff0c;控制器数量&#xff0c;限制PVC请求的存储量 注…

【Android原生问题分析】夸克、抖音划动无响应问题【Android14】

1 问题描述 偶现问题&#xff0c;用户打开夸克、抖音后&#xff0c;在界面上划动无响应&#xff0c;但是没有ANR。回到Launcher后再次打开夸克/抖音&#xff0c;发现App的界面发生了变化&#xff0c;但是仍然是划不动的。 2 log初分析 复现问题附近的log为&#xff1a; 用户…