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原理解析