什么是gRPC

gRPC谷歌开源基于go语言的一个现代的开源高性能RPC框架,可以在任何环境中运行。它可以有效地连接数据中心内和跨数据中心的服务,并提供可插拔的支持,以实现负载平衡,跟踪,健康检查和身份验证。它还适用于分布式计算的最后一英里,用于将设备,移动应用程序和浏览器连接到后端服务。

简单的服务定义:使用Protocol Buffers定义您的服务,这是一个功能强大的二进制序列化工具集和语言.

跨语言和平台工作:自动为各种语言和平台的服务生成惯用的客户端和服务器存根,当然单纯的java语言之间也是可以的。

一般主要是Java和Go,PHP,Python之间通讯。

快速启动并扩展:使用单行安装运行时和开发环境,并使用框架每秒扩展到数百万个RPC

双向流媒体和集成的身份验证:基于http/2的传输的双向流和完全集成的可插拔身份验证

官网地址:https://www.grpc.io/

这是一个可以运行的例子,本文基于此增加了一些代码:https://codenotfound.com/grpc-java-example.html

这个例子使用的jar是grpc-spring-boot-starter@io.github.lognet

这个例子也可以参考:https://github.com/yidongnan/grpc-spring-boot-starter

不过这个例子使用的是另一个jar是grpc-spring-boot-starter@net.devh

说明:Thrift也可以实现跨语言的通讯,有人对此做了对比参考:开源RPC(gRPC/Thrift)框架性能评测

服务定义

与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。默认情况下,gRPC使用Protocol Buffers作为接口定义语言(IDL)来描述服务接口和有效负载消息的结构。如果需要,可以使用其他替代方案。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。

它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

数据序列化和反序列化

序列化: 将数据结构或对象转换成二进制串的过程。

反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

这里有人翻译了官方文档:Protocol Buffers官方文档(开发指南)

也可以看IBM的文档:Google Protocol Buffer 的使用和原理

https://github.com/protocolbuffers/protobuf

https://developers.google.com/protocol-buffers/docs/javatutorial

参考官网指南:

如您所见,语法类似于C ++或Java。让我们浏览文件的每个部分,看看它的作用。

.proto文件以包声明开头,这有助于防止不同项目之间的命名冲突。在Java中,包名称用作Java包,除非您已经明确指定了a java_package,就像我们在这里一样。即使您提供了a java_package,您仍应定义一个法线package,以避免在Protocol Buffers名称空间和非Java语言中发生名称冲突。

在包声明之后,您可以看到两个特定于Java的选项: java_packagejava_outer_classname。 java_package指定生成的类应该以什么Java包名称存在。如果没有明确指定它,它只是匹配package声明给出的包名,但这些名称通常不是合适的Java包名(因为它们通常不以域名开头)。该java_outer_classname选项定义应包含此文件中所有类的类名。如果你没有java_outer_classname明确地给出,它将通过将文件名转换为camel case来生成。例如,默认情况下,“my_proto.proto”将使用“MyProto”作为外部类名。

接下来,您有消息定义。消息只是包含一组类型字段的聚合。许多标准的简单数据类型都可以作为字段类型,

包括boolint32floatdouble,和string。您还可以使用其他消息类型作为字段类型在消息中添加更多结构 - 在上面的示例中,Person消息包含PhoneNumber消息,而AddressBook消息包含Person消息。您甚至可以定义嵌套在其他消息中的消息类型 -​​ 如您所见,PhoneNumber类型在内部定义Personenum如果您希望其中一个字段具有预定义的值列表之一,您也可以定义类型 - 在此您要指定电话号码可以是其中之一MOBILEHOME或者WORK

每个元素上的“= 1”,“= 2”标记标识该字段在二进制编码中使用的唯一“标记”。标签号1-15需要少于一个字节来编码而不是更高的数字,因此作为优化,您可以决定将这些标签用于常用或重复的元素,将标签16和更高版本留给不太常用的可选元素。重复字段中的每个元素都需要重新编码标记号,因此重复字段特别适合此优化。

必须使用以下修饰符之一注释每个字段:

  • required:必须提供该字段的值,否则该消息将被视为“未初始化”。尝试构建一个未初始化的消息将抛出一个RuntimeException。解析未初始化的消息将抛出一个IOException。除此之外,必填字段的行为与可选字段完全相同。
  • optional:该字段可能已设置,也可能未设置。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像我们type在示例中为电话号码所做的那样。否则,使用系统默认值:数字类型为0,字符串为空字符串,bools为false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,其中没有设置其字段。调用访问器以获取尚未显式设置的可选(或必需)字段的值始终返回该字段的默认值。
  • repeated:该字段可以重复任意次数(包括零)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。

永远是必需的 你应该非常小心地将字段标记为required。如果您希望在某个时刻停止写入或发送必填字段,则将字段更改为可选字段会有问题 - 旧读者会认为没有此字段的邮件不完整,可能会无意中拒绝或丢弃它们。您应该考虑为缓冲区编写特定于应用程序的自定义验证例程。谷歌的一些工程师得出的结论是,使用required弊大于利; 他们更喜欢只使用optionalrepeated。但是,这种观点并不普遍。

.proto可以在Protocol Buffer Language Guide中找到编写文件的完整指南- 包括所有可能的字段类型。不要去寻找类继承类似的工具,但协议缓冲区不会这样做。

如果你还不是很理解,就只要参考下面这个例子就行了。

我们将使用以下工具/框架:

  • gRPC 1.16
  • Spring Boot 2.1
  • Maven 3.5

我们的项目具有以下目录结构:

使用Protocol Buffers定义服务

先看proto文件

syntax = "proto3";option java_multiple_files = true;
package com.codenotfound.grpc.helloworld;message Person {string first_name = 1;string last_name = 2;
}message Greeting {string message = 1;
}message A1 {int32  a = 1;int32  b = 2;
}message A2 {int32  message = 1;
}service HelloWorldService {rpc sayHello (Person) returns (Greeting);rpc addOperation (A1) returns (A2);
}

注意:A1,A2是我定义的,其实就是定义入参出参,1和2那是表示是第几个参数而已,数据类型有string和int32。

Maven设置

我们使用Maven构建并运行我们的示例。

下面显示的是POM文件中Maven项目的XML表示。它包含编译和运行示例所需的依赖项。

为了配置和公开Hello World gRPC服务端点,我们将使用Spring Boot项目。

为了便于管理不同的Spring依赖项,使用了Spring Boot Starters。这些是一组方便的依赖项描述符,您可以在应用程序中包含这些描述符。

我们包含spring-boot-starter-web依赖项,该依赖项自动设置将托管我们的gRPC服务端点的嵌入式Apache Tomcat。

spring-boot-starter-test包括用于包括测试启动的应用程序的依赖关系的JUnit,Hamcrest和的Mockito。

用于gRPC框架的Spring启动启动程序自动配置并运行嵌入式gRPC服务器,@GRpcService启用Beans作为Spring Boot应用程序的一部分。启动器支持Spring Boot版本1.5.X和2.XX我们通过包含grpc-spring-boot-starter依赖项来启用它。

协议缓冲区支持许多编程语言中生成的代码。本教程重点介绍Java。

有多种方法可以生成基于protobuf的代码,在本例中,我们将使用grobc-java GitHub页面上记录的protobuf-maven-plugin。

我们还包括os-maven-plugin扩展,它可以生成各种有用的平台相关项目属性。由于协议缓冲区编译器是本机代码,因此需要此信息。换句话说,protobuf-maven-plugin需要为正在运行的平台获取正确的编译器。

最后,插件部分包括spring-boot-maven-plugin。这允许我们构建一个可运行的超级jar。这是执行和传输代码的便捷方式。此外,该插件允许我们通过Maven命令启动示例。

项目的pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.codenotfound</groupId><artifactId>grpc-java-hello-world</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>grpc-java-hello-world</name><description>gRPC Java Example</description><url>https://codenotfound.com/grpc-java-example.html</url><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.0.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><grpc-spring-boot-starter.version>3.0.0</grpc-spring-boot-starter.version><os-maven-plugin.version>1.6.1</os-maven-plugin.version><protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>io.github.lognet</groupId><artifactId>grpc-spring-boot-starter</artifactId><version>${grpc-spring-boot-starter.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><extensions><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>${os-maven-plugin.version}</version></extension></extensions><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>${protobuf-maven-plugin.version}</version><configuration><protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact><pluginId>grpc-java</pluginId><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.16.1:exe:${os.detected.classifier}</pluginArtifact></configuration><executions><execution><goals><goal>compile</goal><goal>compile-custom</goal></goals></execution></executions></plugin></plugins></build></project>

注意:必须使用这个编译compile工具生成特定语言(比如我们这里是java)的执行代码。

手动执行以下Maven命令,应在target / generated-sources / protobuf /下生成不同的消息和服务类。

mvn compile

也可以使用IDE编译。

编译执行:

会生成编译后的文件:

注意上面的文件是自动编译生成的,不是你自己写的!

Spring Boot安装程序

创建一个SpringGrpcApplication包含一个main()方法,该方法使用Spring Boot的SpringApplication.run()方法来引导应用程序。

package com.codenotfound;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class SpringGrpcApplication {public static void main(String[] args) {SpringApplication.run(SpringGrpcApplication.class, args);}
}

创建服务器

定义Service,我增加了一个加法运算方法,需要注意的是输入,输出参数都写在方法定义里。

服务实现在HelloWorldServiceImplPOJO中定义,该POJO实现HelloWorldServiceImplBase从HelloWorld.proto文件生成的类。

我们覆盖该sayHello()方法并Greeting根据Person请求中传递的名字和姓氏生成响应。

请注意,响应是一个StreamObserver对象。换句话说,该服务默认是异步的。在接收响应时是否要阻止是客户的决定,我们将在下面进一步了解。

我们使用响应观察者的onNext()方法返回Greeting,然后调用响应观察者的onCompleted()方法告诉gRPC我们已经完成了写响应。

HelloWorldServiceImplPOJO标注有@GRpcService其自动配置到端口露出指定GRPC服务默认端口6565。

request:入参

responseObserver:出参

格式按照标准

package com.codenotfound.grpc;import com.codenotfound.grpc.helloworld.A1;
import com.codenotfound.grpc.helloworld.A2;
import org.lognet.springboot.grpc.GRpcService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codenotfound.grpc.helloworld.Greeting;
import com.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;
import com.codenotfound.grpc.helloworld.Person;
import io.grpc.stub.StreamObserver;@GRpcService
public class HelloWorldServiceImplextends HelloWorldServiceGrpc.HelloWorldServiceImplBase {private static final Logger LOGGER= LoggerFactory.getLogger(HelloWorldServiceImpl.class);@Overridepublic void sayHello(Person request,StreamObserver<Greeting> responseObserver) {LOGGER.info("server received {}", request);String message = "Hello " + request.getFirstName() + " "+ request.getLastName() + "!";Greeting greeting= Greeting.newBuilder().setMessage(message).build();LOGGER.info("server responded {}", greeting);System.out.println("message>>>" + message);responseObserver.onNext(greeting);responseObserver.onCompleted();}@Overridepublic void addOperation(A1 request,StreamObserver<A2> responseObserver) {LOGGER.info("server received {}", request);int message = request.getA() + request.getB();A2 a2 = A2.newBuilder().setMessage(message).build();LOGGER.info("server responded {}", a2);System.out.println("message>>>" + message);responseObserver.onNext(a2);responseObserver.onCompleted();}
}

服务端使用了@GRpcService注解.

也可以使用这个jar注解@GrpcService就可以,代码和前面一种一致的

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

https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/README-zh.md

源码在这里:

 你可以自定义端口:

grpc:port: 9090

创建客户端

下面是客户端代码,这里为了简单服务端和客户端写一个项目,实际开发肯定是分开,不然也就没必要用Grpc这么麻烦了。

客户端代码在HelloWorldClient类中指定。

@Component如果启用了自动组件扫描,我们将使用Spring 注释客户端,这将导致Spring自动创建并将下面的bean导入容器(将@SpringBootApplication注释添加到主SpringWsApplication类等同于使用@ComponentScan)。

要调用gRPC服务方法,我们首先需要创建一个stub。

有两种类型的stub可用:

  • 一个阻塞/同步stub,将等待服务器响应
  • 一个非阻塞/异步stub使非阻塞调用到服务器,其中,所述响应是异步返回。

在此示例中,我们将实现阻塞stub。

为了传输消息,gRPC使用http/2和其间的一些抽象层。这种复杂性隐藏在MessageChannel处理连接的背后。

一般建议是为每个应用程序使用一个通道并在服务stub之间共享它。

我们使用一个init()带注释的方法@PostConstruct,以便MessageChannel在bean初始化之后构建一个新的权限。然后使用该通道创建helloWorldServiceBlockingStub

gRPC默认使用安全连接机制,如TLS。因为这是一个简单的开发测试将使用usePlaintext(),以避免必须配置不同的安全工件,如密钥/信任存储。

sayHello()方法使用Builder模式创建Person对象,我们在其上设置'firstname'和'lastname'输入参数。

helloWorldServiceBlockingStub则用来发送走向世界您好GRPC服务的请求。结果是一个Greeting对象,我们从中返回包含消息。

package com.codenotfound.grpc;import com.codenotfound.grpc.helloworld.A1;
import com.codenotfound.grpc.helloworld.A2;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.codenotfound.grpc.helloworld.Greeting;
import com.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;
import com.codenotfound.grpc.helloworld.Person;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;@Component
public class HelloWorldClient {private static final Logger LOGGER= LoggerFactory.getLogger(HelloWorldClient.class);private HelloWorldServiceGrpc.HelloWorldServiceBlockingStub helloWorldServiceBlockingStub;@PostConstructprivate void init() {ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9090).usePlaintext().build();helloWorldServiceBlockingStub = HelloWorldServiceGrpc.newBlockingStub(managedChannel);}public String sayHello(String firstName, String lastName) {Person person = Person.newBuilder().setFirstName(firstName).setLastName(lastName).build();LOGGER.info("client sending {}", person);Greeting greeting = helloWorldServiceBlockingStub.sayHello(person);LOGGER.info("client received {}", greeting);return greeting.getMessage();}public int addOperation(int a, int b) {A1 a1 = A1.newBuilder().setA(a).setB(b).build();A2 a2 = helloWorldServiceBlockingStub.addOperation(a1);return a2.getMessage();}
}

注意如果使用自定义端口需要修改这个,默认是6565,保持和你修改的配置文件一致,或者你不配置用默认的就行:

gRPC测试用例

package com.codenotfound;import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.codenotfound.grpc.HelloWorldClient;@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringGrpcApplicationTests {@Autowiredprivate HelloWorldClient helloWorldClient;@Testpublic void testSayHello() {assertThat(helloWorldClient.sayHello("Grpc", "Java")).isEqualTo("Hello Grpc Java!");assertThat(helloWorldClient.addOperation(1, 2)).isEqualTo(3);}
}

2个断言:

一个是原始的就是字符串输出,第二个是我增加的做个简单的加法运算1+2=3就对了。

运行测试用例:

故意修改为和4比较结果,报错就对了。

总结:建议先跑完整的例子,不要陷入grpc太深。

定义好.proto,再生成对应编译文件,再写实现类,定义服务端,使用客户端调用。

SpringBoot整合Grpc实现跨语言RPC通讯相关推荐

  1. server如何调用 thrift_一文带你了解 Thrift,一个可伸缩的跨语言 RPC 框架(pinpoint 源码分析系列)...

    Thrift 是什么研究分布式调用链工具pinpoint的时候,在源码里看到了Thrift相关的代码,所以来了兴趣,想研究研究这个框架.Thrift 目前是 Apache 的一个项目,但是它是由fac ...

  2. 跨语言rpc框架Thrift

    RPC 全称 Remote Procedure Call--远程过程调用.RPC技术简单说就是为了解决远程调用服务 的一种技术,使得调用者像调用本地服务一样方便透明 Thrift的定义   Thrif ...

  3. 跨语言RPC框架Thrift详解

    一. 概念 Apache的Thrift软件框架,是用来进行可伸缩的.跨语言的服务开发,它通过一个代码生成引擎来构建高效.无缝的服务,这些服务能够实现跨语言调度,目前支持的语言有: C++, Java, ...

  4. 跨语言RPC框架Hessian、Thrift、Protocol Buffer之间的选择

    为什么80%的码农都做不了架构师?>>>    总结在几者之间选择的考量: 1. 如果你不需要很多语言相互调用, 希望保持清晰的java接口代码(无任何业务不相关的接口继承和方法,属 ...

  5. SpringBoot整合atomikos实现跨库事务

    背景 框架之前完成了多数据源的动态切换及事务的处理,想更近一步提供一个简单的跨库事务处理功能,经过网上的搜索调研,大致有XA事务/SEGA事务/TCC事务等方案,因为业务主要涉及政府及企业且并发量不大 ...

  6. SpringBoot整合RPC框架---Thrift

    文章目录 什么是Thrift 架构 支持的通讯协议 支持的传输协议 支持的服务模型 Thrift的优点 SpringBoot整合Thrift 为什么会出现RPC框架 常见的RPC框架集成套路 开撸 官 ...

  7. springboot整合Thrift

    什么是Thrift Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务.它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为"大规模跨语言服务开发 ...

  8. 什么是 Thrift(RPC)?一种接口描述语言和二进制通讯协议,用来定义和创建跨语言的服务

    Table of Contents 什么是Thrift 架构 什么是RPC框架? Thrift的协议栈结构 优点 创建一个Thrift服务 Thrift的第一个java小实例 Thrift是一种接口描 ...

  9. go语言rpc,grpc介绍

    目录 rpc RPC调用 net/rpc RPC over HTTP 和 RESTful server client RPC over TCP 和 RESTful server client 序列化/ ...

最新文章

  1. 突发!5G 标准进程延后 3 个月
  2. html获取cookie_知了汇智《XSS攻击-盗取cookie实战》课程文档讲解
  3. html设置文字超过字数_css限制文字显示字数长度,超出部分自动用省略号显示,防止溢出到第二行...
  4. qml中loader加载页面会闪屏_Qml动态语言切换
  5. Spring boot 中pom.xml 各个节点详解
  6. LeetCode 1506. Find Root of N-Ary Tree(异或)
  7. PID控制器改进笔记之二:改进PID控制器之手自动切换
  8. python rs232_使用Python進行RS-232通信返回垃圾信息
  9. mysql开机自启动设置
  10. 使用Maven构建Web项目-测试
  11. oracle 分组 top10 sql,oracle sql 合龙 分组 聚合函数
  12. 如何使用SQL查询视图,Postico使用技巧分享~
  13. 案例:手动输入一个字符串,打散放进一个列表,小写字母反序 大写字母保持不变...
  14. nginx 配置反向代理
  15. [Book]《云计算核心技术剖析》读书笔记
  16. 拯救者 linux 无线网卡驱动下载,联想y7000无线网卡驱动下载-联想拯救者y7000无线网卡驱动v19.51.22.2 官方版 - 极光下载站...
  17. Hive-WARN: Establishing SSL connection without server‘s identity verification is not recommended. Ac
  18. pandas级联与合并
  19. lego-loam 跑 kitti00包(kitti2bag+lego-loam+evo)详细版
  20. 网传人人车破产,或是改变销售模式?-千氪

热门文章

  1. 安川g7接线端子图_西门子SIWAREX称重模块安装接线注意事项!
  2. linux 修改mysql root密码_Linux mysql如何更改root密码
  3. python如何控制mysql_python如何操作mysql
  4. data es集群master_Kubernetes Helm3 部署 ElasticSearch amp; Kibana 7 集群
  5. Java pinyin4j 汉字转拼音包括——多音字
  6. 通过打印学习Linux内核之sysfs(0)
  7. HDU - 5877 Weak Pair 2016 ACM/ICPC 大连网络赛 J题 dfs+树状数组+离散化
  8. 吴恩达神经网络和深度学习——第四周笔记
  9. java8的jvm优化_基于JDK8 版本的SpringBoot 启动参数优化(建议收藏)
  10. 怎样用python画雪花_python使用turtle库与random库绘制雪花