一文详解gRPC快速整合SpringCloud
作者:东小西 发布时间:2021-12-11 05:01:02
gRPC
gRPC是由 google开发的一个高性能、通用的开源RPC框架,主要面向移动应用开发且基于HTTP/2协议标准而设计,同时支持大多数流行的编程语言。它是一种与语言、平台无关、可扩展的序列化结构数据。它的定位类似于JSON、XML,但是比他们更小、更快、更简单。
优势
gRPC基于HTTP/2协议传输,而HTTP/2相比HTTP1.x,还是有需要优势的:
HTTP/2采用二进制格式传输协议,而非HTTP1.x的文本格式。
多路复用
HTTP/2支持通过一个连接发送多个并发的请求。
服务器推送
服务端推送是一种在客户端请求之前发送数据的机制。在HTTP/2中,服务器可以对客户端的一个请求发送多个响应。而不像HTTP/1.X一样,只能通过客户端发起request,服务端才产生对应的response。
减少网络流量的头部压缩
HTTP/2对消息头进行了压缩传输,能够节省消息头占用的网络流量。
工作方式
从上图可以看出,简单了解一下grpc的工作模式。用gRPC来进行远程调用服务,客户端(client) 仅仅需要gRPC Stub ,通过Proto Request向gRPC Server发起服务调用,然后 gRPC Server通过Proto Response(s)将调用结果返回给调用的client。
使用场景
接口约束: 需要对接口有严格的管控,比如对外部提供接口时,并不希望客户端随意传递数据,这是我们就可以使用gRPC来对接口约束。
性能要求:对传输性能有较高要求,如果我们传输的消息体过大,或调度过于频繁不希望影响系统性能时,可以考虑使用gRPC,它的消息体比JSON或者文本传输要小的多。
Protobuf语法
基本规范
文件以.proto做为文件后缀,除结构定义外的语句以分号结尾
结构定义可以包含:message、service、enum
rpc方法定义结尾的分号可有可无
Message命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式
Enums类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式
Service与rpc方法名统一采用驼峰式命名
message SongServerRequest {
required string song_name = 1;
}
enum Foo {
FIRST_VALUE = 1;
SECOND_VALUE = 2;
}
限定修饰符
Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。
Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。
数据类型
.proto | C++ | Java | Python | Go | Ruby | C# |
---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double |
float | float | float | float | float32 | Float | float |
int32 | int32 | int | int | int32 | Fixnum or Bignum | int |
int64 | int64 | long | ing/long[3] | int64 | Bignum | long |
uint32 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum | uint |
uint64 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong |
sint32 | int32 | int | intj | int32 | Fixnum or Bignum | int |
sint64 | int64 | long | int/long[3] | int64 | Bignum | long |
fixed32 | uint32 | int[1] | int | uint32 | Fixnum or Bignum | uint |
fixed64 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong |
sfixed32 | int32 | int | int | int32 | Fixnum or Bignum | int |
sfixed64 | int64 | long | int/long[3] | int64 | Bignum | long |
bool | bool | boolean | boolean | bool | TrueClass/FalseClass | bool |
string | string | String | str/unicode[4] | string | String(UTF-8) | string |
bytes | string | ByteString | str | []byte | String(ASCII-8BIT) | ByteString |
gRPC整合SpringCloud & Nacos
其实,第一次了解gRPC,也是在Nacos2.0升级的时候,Nacos2.0版本相比1.X新增了gRPC的通信方式。
端口 | 与主端口的偏移量 | 描述 |
---|---|---|
9848 | 1000 | 客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求 |
9849 | 1001 | 服务端gRPC请求服务端端口,用于服务间同步等 |
核心依赖
<properties>
<java.version>8</java.version>
<nacos.version>2.2.5.RELEASE</nacos.version>
<mapstruct.version>1.3.1.Final</mapstruct.version>
<grpc.starter.version>2.10.1.RELEASE</grpc.starter.version>
<grpc.client.version>2.10.1.RELEASE</grpc.client.version>
<lombok.version>1.18.12</lombok.version>
<fastjson.version>1.2.76</fastjson.version>
<freemarker.verson>2.3.28</freemarker.verson>
<nacos.client>2.0.0</nacos.client>
</properties>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>${grpc.client.version}</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>${grpc.starter.version}</version>
</dependency>
项目结构
API
编写pom配置。
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</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>
<!--设置grpc生成代码到指定路径-->
<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>
<!-- 设置多个源文件夹 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<!-- 添加主源码目录 -->
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/src/main/gen</source>
<source>${project.basedir}/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
新建 src\main\proto 目录,编写 user.ptoto。
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.yx.grpc.user";
service UserService {
rpc queryUser(UserRequest) returns (UserReply) {}
}
message UserRequest {
int64 id = 2;
}
message UserReply {
int32 code = 1;
string msg = 2;
bool success = 3;
message Data {
UserPb userPb = 1;
}
Data data = 4;
}
message UserPb {
int64 id = 1;
string name = 2;
string sex = 3;
}
执行 mvn compile,生成代码。
服务端
核心服务实现类。
@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase{
@Override
public void queryUser(UserRequest request, StreamObserver<UserReply> responseObserver) {
UserReply.Builder userReply = UserReply.newBuilder();
TblUser tblUser = new TblUser(11L, "syx", "nan");
userReply.setCode(200).setMsg("SUCCESS").setSuccess(true);
userReply.setData(UserReply.Data.newBuilder()
.setUserPb(UserPb.newBuilder()
.setId(tblUser.getId())
.setName(tblUser.getName())
.setSex(tblUser.getSex())));
responseObserver.onNext(userReply.build());
responseObserver.onCompleted();
super.queryUser(request, responseObserver);
}
}
客户端
gRPC配置。
grpc:
client:
GLOBAL:
negotiation-type: plaintext
enable-keep-alive: true
keep-alive-without-calls: true
自定义请求转换器。
@Configuration
public class MessageConverter {
@Bean
public HttpMessageConverters protobufHttpMessageConverter() {
ProtobufHttpMessageConverter protobufHttpMessageConverter = new ProtobufHttpMessageConverter();
protobufHttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON, MediaType.parseMediaType(MediaType.TEXT_PLAIN_VALUE + ";charset=ISO-8859-1")));
return new HttpMessageConverters(protobufHttpMessageConverter);
}
}
请求测试类。
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@GrpcClient("yx-grpc-service")
UserServiceGrpc.UserServiceFutureStub futureStub;
@RequestMapping(value="/queryUser/{id}")
public UserReply queryUser(@PathVariable Integer id) {
UserReply userReply = null;
try {
userReply = futureStub.queryUser(UserRequest.newBuilder().setId(id).build()).get();
return userReply;
} catch (Exception e) {
}
return userReply;
}
}
测试
启动服务端和客户端,访问 http://localhost:8002/user/queryUser/1 。
Gitee 地址:gitee.com/renxiaoshi/…
来源:https://juejin.cn/post/7212942861519306789


猜你喜欢
- 本文实例分析了Android中ListView用法。分享给大家供大家参考,具体如下:通过在Layout中添加ListView Widget可
- 在Android中使用SQLite数据库的入门指南,打算分下面几部分与大家一起分享, 1、什么是SQLite 2、Android中使用SQL
- 一、扫雷扫雷小游戏主要是利用字符数组、循环语句和函数实现。设计思路:雷盘大小为9*9,但是为了后续能更好的统计出雷的个数在定义数组的时候定义
- 一、安装本地Maven选择你需要的maven版本下载:官网下载传送门我使用的是3.6.1版本:maven-3.6.1-bin.zip&nbs
- * 与过滤器在讲Spring boot之前,我们先了解一下过滤器和 * 。这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的
- 本文为大家分享了Java多线程实现Runnable方式的具体方法,供大家参考,具体内容如下(一)步骤 1.定义实现Runnable
- 前言很多人之前编写Java代码都是用的Eclipse,确实Eclipse是一个很好的工具,熟悉了之后用起来很方便,但是没办法,很多公司都强制
- 本文实例为大家分享了Android EventBus普通事件和粘性事件,供大家参考,具体内容如下展示效果 添加EventBus导入依赖com
- JAVA 枚举单例模式及源码分析的实例详解 单例模式的实现有很多种,网上也分析了
- LinkedList<T>是一个双向链表,其元素会指向它前面和后面的元素。这样,通过移动到下一个元素可以正向遍历链表,通过移动到
- 一、场景描述仪器数据文件的格式包含Pdf、Word、Excel等多种,不同种格式的文件其数据的采集方式不同,因此定义仪器数据采集接口,并定义
- Spring Boot 自动装配最重要的注解@SpringBootApplication@Target(ElementType.TYPE)@
- 布局中EditText在android布局中经常用到,对EditText中输入的内容也经常需要进行限制,我们可以通过TextWatcher去
- 方法有4种:使用 String 类的 valueOf() 方法使用字符串连接使用 Character 类的 toString() 方法使用字
- 什么是volatile关键字volatile是Java中用于修饰变量的关键字,其可以保证该变量的可见性以及顺序性,但是无法保证原子性。更准确
- 本文实例总结了C#遍历DataSet控件的方法。分享给大家供大家参考。具体方法如下:DataSet控件在.net主要是用来存储数据的,它更像
- pom.xml增加依赖包 <dependency> <groupId>io.springf
- 一、总体说明 XML和JSON 是最为常用的数据交换格式本例子演示如何将java对象,转成XML输出。二、流程1.在上文的例子中,创建一个包
- 我们首先看下BASEJDBC的写法实例:package com.dao;import java.sql.Connection;import
- 推荐教程IntelliJ IDEA 2020最新激活码(亲测有效,可激活至 2089 年)最新idea2021注册码永久激活(激活到2100