1、gRPC简介
在介绍gRPC以前先说一下RPC(Remote Procedure Call),也叫远程过程调用协议,它是一种经过网络从远程计算机程序上请求服务,而不须要了解底层网络技术的协议。相比HTTP协议来讲,它主要是基于TCP/IP协议的的,传输效率更高,可以跨语言,典型的RPC框架有RMI、Hessian、Dubbo等。想要深刻了解它们之间区别的话能够看这篇博客。html
gRPC由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。具有如下特性:java
- 基于HTTP/2,HTTP/2 提供了链接多路复用、双向流、服务器推送、请求优先级、首部压缩等机制。能够节省带宽、下降TCP连接次数、节省CPU,帮助移动设备延长电池寿命等。gRPC 的协议设计上使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其余的控制信息则用Header 表示。
- IDL使用ProtoBuf ,gRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(相似于XML、JSON、hessian)。ProtoBuf可以将数据进行序列化,并普遍应用在数据存储、通讯协议等方面。压缩和传输效率高,语法简单,表达力强。
- 多语言支持(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java),gRPC支持多种语言,并可以基于语言自动生成客户端和服务端功能库。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它语言的版本正在积极开发中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等语言,grpc-java已经支持Android开发。
2、认识protocol buffers
2.1简介
protocol buffers(简称protobuf),是由google开源的,在 RPC (远程方法调用)里很是流行的二进制编解码格式,相似xml,json等。主要有如下几个优势:编程
- 性能好/效率高
- 代码生成机制,好比能够将proto文件编译为Java文件
- 支持多种编程语言
- 支持“向后兼容”和“向前兼容”
2.2语法
2.2.1 定义一个消息类型
syntax = "proto3";
message SearchRequest {
required string query = 1;
optional int32 page_number = 2;
optional int32 result_per_page = 3;
}
第一行指定了使用proto3语法,若是没有指定,编译器会使用proto2。这个指定语法行必须是文件的非空非注释的第一个行。json
SearchRequest至关于咱们Java中的一个实体类,里面有三个字段分别为String 类型query,int类型page_number和int类型result_per_page。服务器
后面的数字1,2,3是标识号,必须从1开始。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不可以再改 变。注:[1,15]以内的标识号在编码的时候会占用一个字节。[16,2047]以内的标识号则占用2个字节。因此应该为那些频繁出现的消息元素保留 [1,15]以内的标识号微信
字段的第一个是修饰符,必须是required,optional,repeated其中一个。网络
- required:一个格式良好的消息必定要含有1个这种字段。表示该值是必需要设置的;
- optional:表示可选的,能够有值也能够没有。
- repeated:在一个格式良好的消息中,这种字段能够重复任意屡次(包括0次)。重复的值的顺序会被保留。表示该值能够重复,至关于java中的List;
字段类型与Java类型对应关系以下图:
框架
2.2.2 定义一个服务(service)
若是想要将消息类型用在RPC(远程方法调用)系统中,能够在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不一样语言生成服务接口代码及存根。如,想要定义一个RPC服务并具备一个方法,该方法可以接收 SearchRequest并返回一个SearchResponse,此时能够在.proto文件中进行以下定义:maven
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
更多相关语法能够查看这篇博客编程语言
3、完成Hello World
项目结构如图:

3.1新建项目
新建一个maven项目grpc_demo,而后在pom文件引入grpc和代码生成插件,内容以下。
- <properties>
- <grpc.version>1.0.3</grpc.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-netty</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-protobuf</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- <dependency>
- <groupId>io.grpc</groupId>
- <artifactId>grpc-stub</artifactId>
- <version>${grpc.version}</version>
- </dependency>
- </dependencies>
-
- <build>
- <extensions>
- <extension>
- <groupId>kr.motd.maven</groupId>
- <artifactId>os-maven-plugin</artifactId>
- <version>1.4.1.Final</version>
- </extension>
- </extensions>
- <plugins>
- <plugin>
- <groupId>org.xolstice.maven.plugins</groupId>
- <artifactId>protobuf-maven-plugin</artifactId>
- <version>0.5.0</version>
- <configuration>
- <protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact>
- <pluginId>grpc-java</pluginId>
- <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- <goal>compile-custom</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
3.2编写proto文件
syntax = "proto3";
option java_multiple_files = true;
option java_package = "cn.sp.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1;
}
message HelloReply {
string message = 1; }
运行命令mvn clean compile,就能够看到编译后生成的Java文件。
控制台输出:

目录结构

将这些代码复制到src/main/java/cn/sp目录下
3.3完成HelloWorldClient和HelloWorldServer
客户端代码
- package cn.sp.client;
-
- import cn.sp.GreeterGrpc;
- import cn.sp.HelloReply;
- import cn.sp.HelloRequest;
- import io.grpc.ManagedChannel;
- import io.grpc.ManagedChannelBuilder;
- import io.grpc.StatusRuntimeException;
-
- import java.util.concurrent.TimeUnit;
-
- public class HelloWorldClient {
-
-
- private final ManagedChannel channel;
-
-
- private final GreeterGrpc.GreeterBlockingStub blockingStub;
-
-
- public HelloWorldClient(int port,String host){
- this(ManagedChannelBuilder.forAddress(host,port).usePlaintext(true));
- }
-
- private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder){
- channel = channelBuilder.build();
- blockingStub = GreeterGrpc.newBlockingStub(channel);
- }
-
- public void shutDown()throws InterruptedException{
- channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
- }
-
-
- public void greet(String name){
- HelloRequest request = HelloRequest.newBuilder().setName(name).build();
- HelloReply response;
- try{
- response = blockingStub.sayHello(request);
- }catch (StatusRuntimeException e){
- System.out.println("RPC调用失败:"+e.getMessage());
- return;
- }
-
- System.out.println("服务器返回信息:"+response.getMessage());
- }
-
- public static void main(String[] args)throws Exception {
- HelloWorldClient client = new HelloWorldClient(50051,"127.0.0.1");
- try {
- for (int i=0;i<5;i++){
- client.greet("world:"+i);
- }
- }finally {
- client.shutDown();
- }
-
- }
-
- }
-
服务器端代码
- package cn.sp.server;
-
- import cn.sp.GreeterGrpc;
- import cn.sp.HelloReply;
- import cn.sp.HelloRequest;
- import io.grpc.Server;
- import io.grpc.ServerBuilder;
- import io.grpc.stub.StreamObserver;
-
- public class HelloWorldServer {
-
-
- private int port = 50051;
- private Server server;
-
-
-
-
-
- private void start()throws Exception{
- server = ServerBuilder.forPort(port)
- .addService(new GreeterImpl())
- .build().start();
- System.out.println("service start ....");
- Runtime.getRuntime().addShutdownHook(new Thread(){
- @Override
- public void run() {
- System.err.println("*** shutting down gRPC server since JVM is shutting down");
- HelloWorldServer.this.stop();
- System.err.println("*** server shut down");
- }
- });
- }
-
- private void stop(){
- if (server != null){
- server.shutdown();
- }
- }
-
- private void blockUntilShutDown()throws InterruptedException{
- if (server != null){
- server.awaitTermination();
- }
- }
-
-
- private class GreeterImpl extends GreeterGrpc.GreeterImplBase{
-
- public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver){
- System.out.println("收到的信息:"+req.getName());
-
-
-
-
-
- HelloReply reply = HelloReply.newBuilder().setMessage("Hello: " + req.getName()).build();
- responseObserver.onNext(reply);
- responseObserver.onCompleted();
- }
- }
-
- public static void main(String[] args)throws Exception {
- HelloWorldServer server = new HelloWorldServer();
- server.start();
- server.blockUntilShutDown();
- }
- }
-
3.4运行测试
先运行服务端的main方法,再运行客户端,能够看到服务端控制台输出信息以下:
service start ....
收到的信息:world:0
收到的信息:world:1
收到的信息:world:2
收到的信息:world:3
收到的信息:world:4
客户端控制台输出信息:
服务器返回信息:Hello: world:0
服务器返回信息:Hello: world:1
服务器返回信息:Hello: world:2
服务器返回信息:Hello: world:3
服务器返回信息:Hello: world:4
说明客户端发出的五次问候请求服务端都接收到了,而且返回了响应。
4、总结
gRPC中proto文件的编写就显得十分重要了,要多加注释至关于接口文档,目前代码调用过程看着貌似有些复杂,后面整合SpringBoot以后就很简单了,不过我仍是喜欢HTTP。。。。