Google开源的基于HTTP/2和ProtoBuf的通用RPC框架 http://www.infoq.com/cn/news/2015/03/grpc-google-http2-protobuf

http://www.grpc.io/docs/quickstart/go.html 这个略复杂,还会生成server/client stub代码,和JAXB有点像。

我们为什么从 REST 转向 gRPC

The official Go site discusses GOPATH and how to lay out a workspace directory.

先要设置GOPATH环境变量:

export GOPATH="$HOME/your-workspace-dir/" -- run it in your shell, then add it to ~/.bashrc or equivalent so it will be set for you in the future. Go will install packages under src/, bin/, and  pkg/, subdirectories there. You'll want to put your own packages somewhere under $GOPATH/src, like $GOPATH/src/github.com/myusername/ if you want to publish to GitHub. You'll also probably want export PATH=$PATH:$GOPATH/bin in your .bashrc so you can run compiled programs under $GOPATH. 

gRPC的产生动机和设计原则 http://www.jianshu.com/p/8cc077f6dbb9

对于hello world例子,使用tcpcatcher监控,和一般的REST很不一样啊:

这就是ProtoBuf?

能export普通的REST么?https://github.com/coreos/etcd/blob/master/Documentation/dev-guide/api_grpc_gateway.md

这个协议不只是传输数据,而且也用在分布式和网络应用中。

tcpcatcher-1

tcpcatcher-2

关于HTTP/2,这里又详细的介绍 https://imququ.com/post/http2-traffic-in-wireshark.html

https://http2.github.io/faq/里面列出了很多HTTP2的专有特点

https://blog.buoyant.io/2017/01/11/http2-grpc-and-linkerd/

In early 2015, Google announced gRPC, a “universal RPC framework” which combines the ubiquitous foundation of HTTP with the performance gains of HTTP/2 and the portable interfaces of protobuf. It is novel in that:

  • it transmit metadata through HTTP envelopes, allowing orthogonal features to be layered in (like authentication, distributed tracing, deadlines, and routing proxies ;),
  • it provides operational affordances like multiplexed streaming, back-pressure, & cancellation,
  • it will be available everywhere HTTP/2 is (like web browsers),
  • and it abstracts the details of communication from application code (unlike REST).

背压机制:在数据流从上游生产者向下游消费者传输的过程中,上游生产速度大于下游消费速度,导致下游的 Buffer 溢出,这种现象就叫做 Backpressure 出现。

遇到这个问题能做啥呢?https://www.zhihu.com/question/49618581/answer/117107570

Backpressure,就是消费者需要多少,生产者就生产多少。这有点类似于TCP里的流量控制,接收方根据自己的接收窗口的情况来控制接收速率,并通过反向的ACK包来控制发送方的发送速率。

https://www.zhihu.com/question/49618581/answer/117107570 其他的方法还有节流(Throttling))debounce, 打包(buffer和window)

可能这个只是HTTP/2的特征。

Spring WebFlux 也支持背压,似乎这个大多是处理流数据。

https://www.cncf.io/blog/2018/10/24/grpc-web-is-going-ga/

https://news.ycombinator.com/item?id=18295392

有了 gRPC-Web,这样 JavaScript 能直接调用后端了。因为兼容性考虑,这里还是要有个 proxy。

grpc-web

gRPC Spring Boot demo演练

https://github.com/LogNet/grpc-spring-boot-starter/tree/master/grpc-spring-boot-starter-demo

fanMBP:grpc-spring-boot-starter fan$ cd grpc-spring-boot-starter-demo/

特么用的是gradle,不是maven之类的。用intelJ,这个好像支持很完善。导入的时候可以选中gradle wrapper。

下载gradle,https://gradle.org/gradle-download/ 用个新的命令:sdkman

然后根目录下运行gradle,它会下载maven的一些pom包。因为sprint boot是用mvn构建的?

gradle tasks 列出所有任务。但是gradle文件里面一个task都没有,所以这些是内置的了?

尝试运行gradle bootRun,可以启动服务器

打开新窗口,运行gradle test,全部failed。关掉前面的bootRun,再运行test,成功。

加了个测试例子,

GreeterGrpc.newFutureStub(channel).sayHello(GreeterOuterClass.HelloRequest.newBuilder().setName("name").build()).get().getMessage();
double result = CalculatorGrpc.newFutureStub(channel) .calculate(CalculatorOuterClass.CalculatorRequest.newBuilder().setNumber1(1).setNumber2(1).build()).get().getResult();
assertEquals(2.0f, result, 0.0f);

运行gradle test,failed,但是控制台没有有用信息。打开grpc-spring-boot-starter/grpc-spring-boot-starter-demo/build/reports/tests/test/index.html,可以看到详细信息。在 IDEA里面也可以看到。

服务器用的是netty

里面有用到lombok.extern.slf4j.Slf4j,然后使用@Slf4j,这样这个类就自动注入了 log 的变量,很神奇吧?有点类似 JavaScript 的 Babel 编译器:引入新的语法,然后转换成老的标准的语言。https://projectlombok.org/features/index.html 当然,在 IDE 里面会显示找不到 log 变量的错误,In addition to having Lombok plugin installed, also make sure that “Enable annotation processing” checkbox is ticked under: Preferences -> Compiler -> Annotation Processors

思考

这种摒弃 tomcat 的方式挺有趣,这是真正的分布式调用,当然上面的还没有加上 etcd 这种集中的管理。

一般的业务在分离前,都是独立的三层,而服务间的调用很少,这个只有划分微服务之后才会出现服务间的调用。

增加了系统的复杂度。

这样只有会话层才会暴露 REST 接口?能否做个透明代理,把 gRPC 直接转换成 REST 风格?这里有个很好的例子 https://coreos.com/blog/gRPC-protobufs-swagger.html 它不仅会生成stub,也会生成proxy。

grpc-rest

尝试把这种 gateway 方法引入到 Java 中

在/Users/fan/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protoc下面会有二进制的 protobuff 编译命令,maven 还能下载二进制的包?Google为什么还非得搞成二进制的?Go语言写的?

fanMBP:proto fan$ /Users/fan/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protoc/3.0.0/31a580cdff228f572e27920c0abcfa71cd3d95e4/protoc-3.0.0-osx-x86_64.exe  calculator.proto --java_out=aaa 

calculator.proto:26:12: Option "(google.api.http)" unknown. 

感觉得自己写一个 mvn 的 plugin,添加新的 grpc plugin,然后调用 protoc 的时候引用新的参数。mvn这一套使用起来很容易,一个命令就把依赖和打包解决了,但是我要定制下就复杂了。

https://github.com/google/protobuf-gradle-plugin/blob/master/src/main/groovy/com/google/protobuf/gradle/GenerateProtoTask.groovy

好像不是很复杂。

gradle generateProto --stacktrace --debug 

23:22:44.277 [INFO] [org.gradle.api.Task] [/Users/fan/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protoc/3.0.0/31a580cdff228f572e27920c0abcfa71cd3d95e4/protoc-3.0.0-osx-x86_64.exe, -I/Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/src/main/proto, -I/Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/build/extracted-protos/main, -I/Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/build/extracted-include-protos/main, --java_out=/Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/src//main/protoGen, --grpc_out=/Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/src//main/protoGen, --plugin=protoc-gen-grpc=/Users/fan/.gradle/caches/modules-2/files-2.1/io.grpc/protoc-gen-grpc-java/1.0.0/704f025630f04fcf31ceb5c94176653780c16c1d/protoc-gen-grpc-java-1.0.0-osx-x86_64.exe, /Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/src/main/proto/calculator.proto, /Users/fan/dev/grpc-spring-boot-starter/grpc-spring-boot-starter-demo/src/main/proto/greeter.proto] 

里面当然没有https://github.com/grpc-ecosystem/grpc-gateway完整的参数:

protoc -I/usr/local/include -I. \ 
-I$GOPATH/src \ 
-I$GOPATH/src/[github.com/grpc-ecosystem/](http://github.com/grpc-ecosystem/)grpc-gateway/third_party/googleapis \ 
--grpc-gateway_out=logtostderr=true:. \ 
path/to/your_service.proto 

搞忘记了,gateway需要First you need to install ProtocolBuffers 3.0.0-beta-3 or later.

先试试https://github.com/grpc-ecosystem/grpc-gateway这里的命令行,注意import "google/api/annotations.proto”;$GOPATH/bin is in your $PATH这两点否则总会出现Option “(google.api.http)” unknown.的错误。

fanMBP:proto fan$ /Users/fan/Downloads/protoc-3.1.0-osx-x86_64/bin/protoc -I/usr/local/include -I.  -I$GOPATH/src  -I$GOPATH/src/[github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis](http://github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis)  --grpc-gateway_out=logtostderr=true:. calculator.proto  --java_out=aaa 
fanMBP:proto fan$ ls 
aaa               calculator.pb.gw.go     calculator.proto     greeter.proto 
fanMBP:proto fan$ vi aaa/io/grpc/examples/CalculatorOuterClass.java 

这个 Java 不知道是干啥的,难道这个 plugin 只能生成 go 的反向代理?从protoc-gen-grpc-gateway/gengateway/template.go里面看,确实只有 go 的 template。

或者像我写个一个rest匹配 api/* 的转发层,由其根据proto文件转发所有的后面的微服务,其应该能根据 IDL 生成有用的信息给 Swagger UI,否则就意义不大了。但是好像不行,因为 java 调用 grpc 都是用 stub 来的,所以没法动态调用了。所以还是得根据 IDL每个借口都生成一个 rest。

Create a plugin for Google Protocol Buffer http://www.expobrain.net/2015/09/13/create-a-plugin-for-google-protocol-buffer/ 如果要写 gradle 的插件,可能要用到这个,因为你不可能把上面的 plugin 装到用户的$GOPATH里面去。

Dubbo使用Thrift,其用样在 IDL 上面生成代码。所以如果要转成 REST 都会遇到这个问题。

因为 grpc 有 android 和 ios 的客户端,一般业务也没有必要暴露成 REST 了。只是可惜 javascript 没法调用。