7
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

AndroidでgRPC通信を行う

はじめに

AbemaTV Advent Calendar 2017 12日目の記事です。
最近、個人的にgRPCについて調査する機会があったので、自分への勉強の意味も込めて記事にしたいと思います。

ちなみに、この内容はこちらのLTで発表した物になります。

gRPCとは

gRPCとはRemoteProcedureCallの一つで、ClientとServer間でProtocolBuffersを使って通信します。
HTTP/2を使用するため、双方向ストリーミング、フロー制御、ヘッダー圧縮、多重リクエストなどHTTP/2の特徴を活かすことができます。
また、Protoファイルと呼ばれる通信をする上でのMessage(型)やService(メソッド)の定義ファイルを、Server側とClinet側で共通して使うことになるため、お互いが共通の認識を持って通信を行うことができます。

公式サイトはこちら

手順

  1. Protoファイルの定義
  2. Protoファイルからシリアライズ/デシリアライズ用のJavaファイルを生成する
  3. AndroidでgRPC通信を行う

Protoファイルの定義

まずはProtoファイルの定義を行います。
Protoファイルは、ClinentとServerがどのように通信をするのかを定義するためのファイルです。

まずはMessageと呼ばれるProtocolBuffersで通信をする上での型を定義します。

↓はリクエスト時とレスポンス時に使用する型をProtoファイルに定義しています。
StringのNameを持ったCoffeeRequestという型でRequestを送り、レスポンスとして、price, name, messageを持ったCoffeeResponseとして受け取っています。
プロパティに割り振られている数字はタグのようなものになります。ユニークなnumberを割り当てます。


message CoffeeRequest {
    string name = 1;
}


message CoffeeResponse {
    int32 price = 1;
    string name = 2;
    string message = 3;
}

次にgRPC通信を行うために必要なServiceというものを定義します。ここに定義したServiceを通してClientとServerがやりとりします。

ここでは、OrderというServiceを定義しました。
Orderの引数には先程のCoffeeRequestを指定し、レスポンスとして、CoffeeResponseを受け取ります。


service Coffee {
    rpc Order (CoffeeRequest) returns (CoffeeResponse) {}
}

全体で見ると↓のようになると思います。


syntax = "proto3";

option java_package = "com.takusemba.grpc.android.protos";
option go_package = "protos";

package Coffee;

message CoffeeRequest {
    string name = 1;
}

message CoffeeResult {
    string message = 1;
}

service Coffee {
    rpc Order (CoffeeRequest) returns (CoffeeResult) {}
}

Serviceの種類

ここでServiceの種類について紹介します。

Unary RPCs


service Coffee {
    rpc Order (CoffeeRequest) returns (CoffeeResponse) {}
}

先程定義したServiceはUnary RPCsと呼ばれるServiceでClientが一つリクエストを送るとServer側が一つレスポンスを返す基本的な形です。

Server streaming RPCs その1


service Coffee {
    rpc Order (stream CoffeeRequest) returns (CoffeeResponse) {}
}

Orderの引数にstreamというものがつきました。これは、Server streaming RPCsと呼ばれるもので、Clientが複数のリクエストを送ることができ、Server側は全てのリクエストを受け取り、一つのレスポンスを返すことができます。

Server streaming RPCs その2


service Coffee {
    rpc Order (CoffeeRequest) returns (stream CoffeeResponse) {}
}

また、レスポンス側にもstreamをつけることができます。 この場合は、クライアントが一つのリクエストを送るのに対して、server側は複数のレスポンスを送ることができます。このサーバーが複数のレスポンスを返却するような形を利用すると、サーバープッシュのような、あるイベントをclientが購読するような仕組みを作ることができます。

Bidirectional streaming RPCs


service Coffee {
    rpc Order (stream CoffeeRequest) returns (stream CoffeeResponse) {}
}

また、リクエスト側とレスポンス側の両方にもstreamをつけることができます。これは、Bidirectional streaming RPCsと呼ばれています。server側はclientが全てリクエストを送りきったあとにレスポンスを送ることもできますし、severとclientが交互にメッセージを送り合うこともできます。

Protoファイルからシリアライズ/デシリアライズ用のJavaファイルを生成する

今度は、先程定義したProtoファイルからシリアライズ/デシリアライズ用のJavaファイルを生成します。この生成されたファイルを使用して、ClientとServerが通信することになります。

protocコマンド

protocコマンドとは、ProtocolBuffers通信を行う部分のファイルを生成してくれます。
Protoファイルで言うとMessageの部分です。
まずは、protocコマンドが使える状態にします。

curl -OL https://github.com/google/protobuf/releases/download/v3.5.0/protobuf-java-3.5.0.tar.gz
tar -zxvf protobuf-java-3.5.0.tar.gz
cd checkout -b protobuf-3.5.0 origin/protobuf-3.5.0
./configure
make
sudo make install
protoc —version // -> libprotoc 3.5.0 :)

protoc-gen-grpc-javaプラグイン

Protoファイルで定義したService層のファイルを生成するためにはこのprotoc-gen-grpc-javaプラグインが必要になります。

git clone git@github.com:grpc/grpc-java.git
cd grpc-java
git checkout -b v1.7.0
cd compiler
make
../gradlew java_pluginExecutable
cp build/exe/java_plugin/protoc-gen-grpc-java /usr/local/bin

Javaファイルの生成

protocprotoc-gen-grpc-javaが使えるようになると、以下のコマンドを利用して、Javaファイルを生成することができます。

protoc coffee.proto --grpc-java_out=lite:. --java_out=. --plugin=protoc-gen-grpc-java=/usr/local/bin/protoc-gen-grpc-java

するとこののようなファイルが生成されていると思います。

AndroidでgRPC通信を行う

最後にAndroidでgRPC通信を実装していきたいと思います。

まず、build.gradleを編集します。

build.gradle
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.2'
app/build.gradle
implementation "io.grpc:grpc-okhttp:1.7.0"
implementation "io.grpc:grpc-protobuf:1.7.0"
implementation "io.grpc:grpc-stub:1.7.0"
implementation "javax.annotation:javax.annotation-api:1.2"

app/build.gradleで追加するio.grpc:grpc-xxxのバージョンは先程利用した、protoc-gen-grpc-javaと同じバージョンで揃える必要があります。

次に通信部分の実装について見ていきます。

val channel = ManagedChannelBuilder.forAddress("10.0.2.2", 8080)
   .usePlaintext(true)
   .build()

val stub = CoffeeGrpc.newBlockingStub(channel)

val request = CoffeeOuterClass.CoffeeRequest.newBuilder()
   .setName("hot coffee")
   .build()

Single
   .create<CoffeeOuterClass.CoffeeResponse> {
     it.onSuccess(stub.order(request))
   }.subscribeOn(Schedulers.io())
   .observeOn(AndroidSchedulers.mainThread())
   .subscribe(
       { /** do something */ },
       { /**  error handling */ }
   )

まず、ipアドレスとportを指定して、channelを生成しています。ここでは、エミュレータからlocalhostアクセスするため、portに10.0.2.2を指定しています。

Stubには、non-blockingまたは、blockingなものなど複数種類があるのですが、ここでは、単純にblockingStubを生成しています。
詳しくは公式のサイトを御覧ください。

実際にリクエストする部分では、RxのSingleを使って、order()を呼び出し、onNextで結果を受け取っています。

さいごに

今回、gRPCで通信をするまでの流れをざっと書いてきましたが、今回の説明で使った例を使ったサンプルがありますので、よかったら見てみてください。

Clientサイド(java): https://github.com/TakuSemba/grpc-android

Serverサイド(Go): https://github.com/TakuSemba/grpc-go

Protoファイル: https://github.com/TakuSemba/grpc-proto

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
7
Help us understand the problem. What are the problem?