gRPC for Android
gRPC
环境配置:
下载并参照 官方demo 配置。主要在 build.gradle 中配置 grpc 和 protobuf 开发环境。官方demo提供了Server端,clone之后可以直接完成通讯。
Client端调用:
1.创建channel
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();
代码解析: * Channel通过ip和端口注册了一个与Server端连接的通道(Connection)。 * usePlaintext(),指明是否跳过协商过程。 true: PLAINTEXT: 假设连接是 plaintext(非SSL) 而且远程端点直接支持HTTP2,不需要升级。 false: PLAINTEXT_UPGRADE: 使用 HTTP 升级协议为 plaintext(非SSL),从 HTTP/1.1 升级到 HTTP/2。 * Channel提供了方法获取当前连接状态或者监听连接状态。 // 获取连接状态 channel.getState(boolean requestConnection); // 监听连接状态 channel.notifyWhenStateChanged(channel.getState(true), new Runnable() { @Override public void run() { //TODO do something when state changed. } }); * 1.Channel可以复用,可以一个App只使用一个Channel来维持通信。 可以通过 channel.isShutdown() 和 channel.isTerminated() 来判断channel是否中断并重建。 2.理论上可以创建多个Channel,在调用时选择最优线路,使用连接池模式来提高整个通信的并发能力。 但是连接池需要自己开发维护,gRPC暂未提供,因为它违背了Http2的设计语义。 * 1.ManagedChannelBuilder.forAddress()指向 ManagedChannelProvider.provider().builderForAddress()。 2.ManagedChannelProvider有两个实现子类:OkHttpChannelProvider 和 NettyChannelProvider。 最终使用哪一个Provider是通过Provider.priority()方法来获取并确定。 3.源码流程: Okhttp -> priority -> return (GrpcUtil.IS_RESTRICTED_APPENGINE || isAndroid()) ? 8 : 3; Netty -> priority -> return 5; 4.由上可知,在Android客户端默认使用Okhttp作为Http2的封装层(当然也可以手动选择Netty)。 这也是为什么 build.gradle 只需要导入 grpc-okhttp,而不需要导入 grpc-netty的原因。
2.创建request
添加 proto 文件之后,build生成java调用文件, GRPC、Request、Reply都是从这里导入并引用的。
HelloRequest request = HelloRequest.newBuilder().build();
3.创建stub,发起请求得到response
Stub的创建成本很低,可以在每次请求时都通过channel创建新的stub。 也可以设置deadline来复用Stub,在每次使用时先检测deadline,再决定是否重建Stub。
3.1. FutureStub
GreeterGrpc.GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel); ListenableFuture<HelloReply> future = stub.sayHello(request); Futures.addCallback(future, new FutureCallback<HelloReply>() { @Override public void onSuccess(@Nullable HelloReply result) {} @Override public void onFailure(Throwable t) {} });
代码解析: * FutureStub: 一对一(一元)的非阻塞式响应。 * 可以通过addCallback()方法得到未来的执行结果。 * callback源码实现简述: 1. 在while循环里执行 future.get(); 2. 得到返回值时回调回调 onSuccess(); 3. 执行过程中抛出异常时回调 onFailure()。
3.2. BlockingStub
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(channel); // doInBackground HelloReply reply = stub.sayHello(request);
代码解析: * BlockingStub: 一对一(一元)的阻塞式响应。 * 内部也是基于FutureStub实现,只是在调用时就开启了while循环。 1. 创建feature; 2. while(future.isDone)监听; 3. 执行结束时,返回feture.get()。 所以BlockingStub的调用执行需要运行在子线程。
3.3. Stub
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel); stub.sayHello(request, new StreamObserver<HelloReply>() { @Override public void onNext(HelloReply value) {} @Override public void onError(Throwable t) {} @Override public void onCompleted() {} });
代码解析: * Stub: 一对多(流式)的非阻塞式响应。 * 通过显式传入StreamObserver实现未来消息的接收。 * StreamObserver源码实现简述: 1. 收到消息(onMessage())时,调用observer.onNext(); 2. 消息流关闭(onClose())时,判断连接状态(status.isOk()); 3. 状态正常调用observer.onCompleted(),否则调用observer.onError()。
参考文章:GRPC原理解析