大略的做事定义:利用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包,除非您已经明确指定了ajava_package,就像我们在这里一样。纵然您供应了ajava_package,您仍应定义一个法线package,以避免在Protocol Buffers名称空间和非Java措辞中发生名称冲突。
在包声明之后,您可以看到两个特定于Java的选项: java_package和java_outer_classname。 java_package指定天生的类该当以什么Java包名称存在。如果没有明确指定它,它只是匹配package声明给出的包名,但这些名称常日不是得当的Java包名(由于它们常日不以域名开头)。该java_outer_classname选项定义应包含此文件中所有类的类名。如果你没有java_outer_classname明确地给出,它将通过将文件名转换为camel case来天生。例如,默认情形下,“my_proto.proto”将利用“MyProto”作为外部类名。
接下来,您有定义。只是包含一组类型字段的聚合。许多标准的大略数据类型都可以作为字段类型,
包括bool,int32,float,double,和string。您还可以利用其他类型作为字段类型在中添加更多构造 - 在上面的示例中,Person包含PhoneNumber,而AddressBook包含Person。您乃至可以定义嵌套在其他中的类型 - 如您所见,PhoneNumber类型在内部定义Person。enum如果您希望个中一个字段具有预定义的值列表之一,您也可以定义类型 - 在此您要指定电话号码可以是个中之一MOBILE,HOME或者WORK。
每个元素上的“= 1”,“= 2”标记标识该字段在二进制编码中利用的唯一“标记”。标签号1-15须要少于一个字节来编码而不是更高的数字,因此作为优化,您可以决定将这些标签用于常用或重复的元素,将标签16和更高版本留给不太常用的可选元素。重复字段中的每个元素都须要重新编码标记号,因此重复字段特殊适宜此优化。
必须利用以下润色符之一注释每个字段:
required:必须供应该字段的值,否则该将被视为“未初始化”。考试测验构建一个未初始化的将抛出一个RuntimeException。解析未初始化的将抛出一个IOException。除此之外,必填字段的行为与可选字段完备相同。optional:该字段可能已设置,也可能未设置。如果未设置可选字段值,则利用默认值。对付大略类型,您可以指定自己的默认值,就像我们type在示例中为电话号码所做的那样。否则,利用系统默认值:数字类型为0,字符串为空字符串,bools为false。对付嵌入式,默认值始终是的“默认实例”或“原型”,个中没有设置其字段。调用访问器以获取尚未显式设置的可选(或必需)字段的值始终返回该字段的默认值。repeated:该字段可以重复任意次数(包括零)。重复值的顺序将保留在协议缓冲区中。将重复字段视为动态大小的数组。永久是必需的 你该当非常小心地将字段标记为required。如果您希望在某个时候停滞写入或发送必填字段,则将字段变动为可选字段会有问题 - 旧读者会认为没有此字段的邮件不完全,可能会无意中谢绝或丢弃它们。您该当考虑为缓冲区编写特定于运用程序的自定义验证例程。谷歌的一些工程师得出的结论是,利用required弊大于利; 他们更喜好只利用optional和repeated。但是,这种不雅观点并不普遍。
您.proto可以在Protocol Buffer Language Guide中找到编写文件的完全指南- 包括所有可能的字段类型。不要去探求类继续类似的工具,但协议缓冲区不会这样做。
如果你还不是很理解,就只要参考下面这个例子就行了。
我们将利用以下工具/框架:
gRPC 1.16Spring Boot 2.1Maven 3.5我们的项目具有以下目录构造:
利用Protocol Buffers定义做事
先看proto文件
syntax ="proto3";option java_multiple_files =true;packagecom.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 {rpcsayHello(Person)returns(Greeting);rpcaddOperation(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"?><projectxmlns="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 /下天生不同的和做事类。
mvncompile
也可以利用IDE编译。
编译实行:
会天生编译后的文件:
把稳上面的文件是自动编译天生的,不是你自己写的!
Spring Boot安装程序
创建一个SpringGrpcApplication包含一个main()方法,该方法利用Spring Boot的SpringApplication.run()方法来勾引运用程序。
packagecom.codenotfound;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublicclassSpringGrpcApplication{publicstaticvoidmain(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:出参
格式按照标准
packagecom.codenotfound.grpc;importcom.codenotfound.grpc.helloworld.A1;importcom.codenotfound.grpc.helloworld.A2;importorg.lognet.springboot.grpc.GRpcService;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importcom.codenotfound.grpc.helloworld.Greeting;importcom.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;importcom.codenotfound.grpc.helloworld.Person;importio.grpc.stub.StreamObserver;@GRpcServicepublicclassHelloWorldServiceImplextendsHelloWorldServiceGrpc.HelloWorldServiceImplBase{privatestaticfinalLogger LOGGER= LoggerFactory.getLogger(HelloWorldServiceImpl.class);@OverridepublicvoidsayHello(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();}@OverridepublicvoidaddOperation(A1 request,StreamObserver<A2> responseObserver){LOGGER.info("server received {}", request);intmessage = 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工具,我们从中返回包含。
packagecom.codenotfound.grpc;importcom.codenotfound.grpc.helloworld.A1;importcom.codenotfound.grpc.helloworld.A2;importjavax.annotation.PostConstruct;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.stereotype.Component;importcom.codenotfound.grpc.helloworld.Greeting;importcom.codenotfound.grpc.helloworld.HelloWorldServiceGrpc;importcom.codenotfound.grpc.helloworld.Person;importio.grpc.ManagedChannel;importio.grpc.ManagedChannelBuilder;@ComponentpublicclassHelloWorldClient{privatestaticfinalLogger LOGGER= LoggerFactory.getLogger(HelloWorldClient.class);privateHelloWorldServiceGrpc.HelloWorldServiceBlockingStub helloWorldServiceBlockingStub;@PostConstructprivatevoidinit(){ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",9090).usePlaintext().build();helloWorldServiceBlockingStub = HelloWorldServiceGrpc.newBlockingStub(managedChannel);}publicStringsayHello(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);returngreeting.getMessage();}publicintaddOperation(inta,intb){A1 a1 = A1.newBuilder().setA(a).setB(b).build();A2 a2 = helloWorldServiceBlockingStub.addOperation(a1);returna2.getMessage();}}
把稳如果利用自定义端口须要修正这个,默认是6565,保持和你修正的配置文件同等,或者你不配置用默认的就行:
gRPC测试用例:
packagecom.codenotfound;importstaticorg.assertj.core.api.Assertions.assertThat;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.context.junit4.SpringRunner;importcom.codenotfound.grpc.HelloWorldClient;@RunWith(SpringRunner.class)@SpringBootTestpublic class SpringGrpcApplicationTests {@Autowiredprivate HelloWorldClient helloWorldClient;@Testpublic void testSayHello() {assertThat(helloWorldClient.sayHello("Grpc", "Java")).isEqualTo("HelloGrpcJava!");assertThat(helloWorldClient.addOperation(1, 2)).isEqualTo(3);}}
2个断言:
一个是原始的便是字符串输出,第二个是我增加的做个大略的加法运算1+2=3就对了。
运行测试用例:
故意修正为和4比较结果,报错就对了。
总结
建议先跑完全的例子,不要陷入grpc太深。
定义好.proto,再天生对应编译文件,再写实现类,定义做事端,利用客户端调用。