gRPC
参考URL: https://blog.csdn.net/weiwangchao_/article/details/82023191
gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
gRPC已经应用在Google的云服务和对外提供的API中,其主要应用场景如下:
在 HTTP/1.1 协议中 「浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞」。
HTTP/2 的多路复用(Multiplexing) 则允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
HTTP/2 传输的数据是二进制的。
相比 HTTP/1.1 的纯文本数据,二进制数据一个显而易见的好处是:更小的传输体积。
首部压缩(Header Compression)
HTTP是无状态协议。简而言之,这意味着每个请求必须要携带服务器需要的所有细节,而不是让服务器保存住之前请求的元数据。因为http2没有改变这个范式,所以它也需要这样(携带所有细节),因此 HTTP 请求的头部需要包含用于标识身份的数据比如 cookies,而这些数据的量也在随着时间增长。每一个请求的头部都包含这些大量的重复数据,无疑是一种很大的负担。对请求头部进行压缩,将会大大减轻这种负担,尤其对移动端来说,性能提高非常明显。
。。。
1.语言中立,支持多种语言;
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 SayHello(HelloRequest) returns (HelloResponse){
}
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
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语法版本,这样生成时不会出现警告,
[推荐]用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-java
io.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插件:
总结: 注意maven引入,一个是引入grpc一个是引入protobuf,两个版本不一样。使用protobuf-maven-plugin插件,默认会去src\main\proto下找。
发现放在IDAE放在target下的源码也会duplicate检测,所以每次根据.proto生成完代码后,把.proto移动到其它地方,防止每次编译都生成源码,需要的时候再生成。- -!
google protobuf 简单实例
参考URL: https://www.cnblogs.com/zhuawang/p/3971839.html
生成的代码放到了target目录下generated-sources\protobuf\java下
[推荐,写的比较细致和全面]gRPC (1):入门及服务端创建和调用原理
参考URL: https://www.cnblogs.com/wxlevel/p/9154246.html
代码转自: gRPC学习记录(二)–Hello World
参考URL:https://www.jianshu.com/p/46d600e5a1b1
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);
}
//客户端方法
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 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();
}
}
}