文章目录
- grpc入门
- 特性
- HTTP2.0 特性
- 与其他rpc比较
- gRPC优缺点
- 快速开始
- 在 Windows 下安装并使用 Protocol Buffers
- Intellij IDEA中使用Protobuf
- gRPC Java简单示例
- 通过protobuf 直接从字节码转换成你要的对象
- 定义一个服务 helloworld.proto
grpc入门 gRPC
参考URL: https://blog.csdn.net/weiwangchao_/article/details/82023191
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
特性
- 基于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支持多种语言,并能够基于语言自动生成客户端和服务端功能库。
- 低延迟、高扩展性、分布式的系统
- 同云服务器进行通信的移动应用客户端
- 设计语言独立、高效、精确的新协议
- 便于各方面扩展的分层设计,如认证、负载均衡、日志记录、监控等
- 多路复用 (Multiplexing)
HTTP/2 的多路复用(Multiplexing) 则允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
- HTTP/2 传输的数据是二进制的。
相比 HTTP/1.1 的纯文本数据,二进制数据一个显而易见的好处是:更小的传输体积。
- 首部压缩(Header Compression)
HTTP是无状态协议。简而言之,这意味着每个请求必须要携带服务器需要的所有细节,而不是让服务器保存住之前请求的元数据。因为http2没有改变这个范式,所以它也需要这样(携带所有细节),因此 HTTP 请求的头部需要包含用于标识身份的数据比如 cookies,而这些数据的量也在随着时间增长。每一个请求的头部都包含这些大量的重复数据,无疑是一种很大的负担。对请求头部进行压缩,将会大大减轻这种负担,尤其对移动端来说,性能提高非常明显。
。。。
2. 基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
3. 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
4. 序列化支持 PB(Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB, 保障了 RPC 调用的高性能。
缺点:
1)GRPC尚未提供连接池,需要自行实现
2)尚未提供“服务发现”、“负载均衡”机制
3)因为基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx1.9版本已支持)
4) Protobuf二进制可读性差(貌似提供了Text_Fromat功能)
默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持)
快速开始 【推荐-可以快速开始】gRPC 官方文档中文版
参考URL: http://doc.oschina.net/grpc?t=58009
使用思路:
使用与thrift相同的设计,通过工具生成响应必要的代码,自己写代码实现其生成代码的接口。
定义一个服务, 指定其可以被远程调用的方法及其参数和返回类型。gRPC 默认使用 protocol buffers 作为接口定义语言,来描述服务接口和有效载荷消息结构。如果有需要的话,可以使用其他替代方案。
gRPC 提供 protocol buffer 编译插件,能够从一个服务定义的 .proto 文件生成客户端和服务端代码。通常 gRPC 用户可以在服务端实现这些API,并从客户端调用它们。
对于开发者而言:
1)需要使用protobuf定义接口,即.proto文件
2)然后使用compile工具生成特定语言的执行代码,比如JAVA、C/C++、Python等。类似于thrift,为了解决跨语言问题。
3)启动一个Server端,server端通过侦听指定的port,来等待Client链接请求,通常使用Netty来构建,GRPC内置了Netty的支持。
4)启动一个或者多个Client端,Client也是基于Netty,Client通过与Server建立TCP长链接,并发送请求;Request与Response均被封装成HTTP2的stream Frame,通过Netty Channel进行交互。
gRPC 允许你定义四类服务方法:
- 单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- 客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。
···
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
··· - 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
在 Windows 下安装并使用 Protocol Buffers
- Protocol 编译器安装
直接在 GitHub 上下载已经编译好了的 binary。下载地址:https://github.com/protocolbuffers/protobuf/releases 。在这里我们下载protoc-3.7.0-win64.zip
- 执行命令
生成java文件。
protoc.exe -I=E:\code--java_out=e:/tmp E:\code\lianxi\src\main\java\grpc\proto\route_guide.proto
-I 这个路径需要是.proto文件所在目录
···
protoc.exe -I=E:\ --java_out=e:/tmp E:\code\lianxi\src\main\java\grpc\proto\route_guide.proto
···
** 使用工具时需要注意**
a. 需要指定一个–proto_path路径,否则报错–proto_path (or -I). You must specify a --proto_path which encompasses this file.
b. 需要在.proto文件中指定它的proto语法版本,这样生成时不会出现警告,
Intellij IDEA中使用Protobuf
[推荐]用Maven实现一个protobuf的Java例子
参考URL: https://www.cnblogs.com/kaituorensheng/p/9022591.html
Intellij IDEA中使用Protobuf的正确姿势
参考URL: https://blog.csdn.net/u010674648/article/details/80673208
- .proto文件语法高亮显示
需要安装Protobuf Support插件
- 将.proto文件转成Java类
一般的做法,是执行protoc命令,依次将.proto文件转成Java类:
protoc.exe -I=d:/tmp --java_out=d:/tmp d:/tmp/monitor_data.proto
不过gRPC官方推荐了一种更优雅的使用姿势,可以通过maven轻松搞定
采用IDEA的插件方便执行PB的文件的JAVA编译:
需要对Protobuf Support插件进行配置.,maven的pom文件添加下面的代码
com.google.protobuf
protobuf-java
3.1.0
io.grpc
grpc-netty
${grpc.version}
io.grpc
grpc-protobuf
${grpc.version}
io.grpc
grpc-stub
${grpc.version}
kr.motd.maven
os-maven-plugin
1.4.1.Final
org.xolstice.maven.plugins
protobuf-maven-plugin
0.5.0
com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
grpc-javaio.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
compile
compile-custom
使用maven的编译命令,即可在target中看到根据.proto文件生成的Java类,如下所示:
protobuf-maven-plugin插件,会默认去main下proto目录找文件,否则报错如下:
E:\code\lianxi\src\main\proto does not exist. Review the configuration or consider disabling the plugin.
protobuf-maven-plugin插件:
- 默认会去src\main\proto下找.proto文件。
- 会根据maven中你的配置下载两个exe文件:protoc-3.7.0-windows-x86_64.exe、protoc-gen-grpc-java-1.19.0-windows-x86_64.exe到target的protoc-plugins目录下。
- 生成的代码放到了target目录下generated-sources\protobuf下。
发现放在IDAE放在target下的源码也会duplicate检测,所以每次根据.proto生成完代码后,把.proto移动到其它地方,防止每次编译都生成源码,需要的时候再生成。- -!
gRPC Java简单示例
通过protobuf 直接从字节码转换成你要的对象 google protobuf 简单实例
参考URL: https://www.cnblogs.com/zhuawang/p/3971839.html
生成的代码放到了target目录下generated-sources\protobuf\java下
定义一个服务 helloworld.proto [推荐,写的比较细致和全面]gRPC (1):入门及服务端创建和调用原理
参考URL: https://www.cnblogs.com/wxlevel/p/9154246.html
【grpc入门-快速开始】代码转自: gRPC学习记录(二)–Hello World
参考URL:https://www.jianshu.com/p/46d600e5a1b1
- proto文件
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}// The request message containing the user's name.
message HelloRequest {
string name = 1;
}// The response message containing the greetings
message HelloReply {
string message = 1;
}
- 编写服务端代码
public class HelloWorldServer {private int port = 50051;
private Server server;
/**
* 启动服务
* @throws IOException
*/
private void start() throws IOException {
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();
}
}// block 一直到退出程序
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}public static void main(String[] args) throws IOException, InterruptedException {
final HelloWorldServer server = new HelloWorldServer();
server.start();
server.blockUntilShutdown();
}// 实现 定义一个实现服务接口的类
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {public void sayHello(HelloRequest req, StreamObserver responseObserver) {
//获取参数
System.out.println("收到的信息:"+req.getName());
//这里可以放置具体业务处理代码 start//这里可以放置具体业务处理代码 end//构造返回
HelloReply reply = HelloReply.newBuilder().setMessage(("Hello: " + req.getName())).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
}
- 客户端代码
public class HelloWorldClient {private final ManagedChannel channel;
//一个gRPC信道
private final GreeterGrpc.GreeterBlockingStub blockingStub;
//阻塞/同步 存根//初始化信道和存根
public HelloWorldClient(String host,int port){
this(ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext(true));
}/** Construct client for accessing RouteGuide server using the existing channel. */
private HelloWorldClient(ManagedChannelBuilder> channelBuilder) {
channel = channelBuilder.build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}//客户端方法
publicvoid 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 InterruptedException {
HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
try {
for(int i=0;
i<5;
i++){
client.greet("world:"+i);
}
}finally {
client.shutdown();
}
}
}