Skip to content

Latest commit

 

History

History
244 lines (193 loc) · 6.95 KB

wire_grpc.md

File metadata and controls

244 lines (193 loc) · 6.95 KB

Wire gRPC

The Wire compiler will generate interfaces for your service RPCs defined in your protobuf schema; under the condition that the target is set to Kotlin.

Getting Started

You can configure how your services are generated via the Gradle plugin:

wire {
  // Kotlin target will generate code for services.
  kotlin {
    // `client` to generate interfaces best suited to sending outbound calls.
    // `server` to generate interfaces best suited to receiving inbound calls.
    rpcRole = 'server'
    // Server only
    // `suspending` to generate coroutines APIs that require a Kotlin coroutines context.
    // `blocking` to generate blocking APIs callable by Java and Kotlin.
    rpcCallStyle = 'suspending'
    // Server only
    // True for emitted services to generate one interface per RPC. 
    singleMethodServices = false
  }
}

The generated code varies depending on your RPC role, namely client or server.
We'll use the following schema to demonstrate how they differ:

syntax = "proto2";

package routeguide;

service RouteGuide {
  // A simple RPC.
  rpc GetFeature(Point) returns (Feature) {}

  // A server-to-client streaming RPC.
  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  // A client-to-server streaming RPC.
  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  // A Bidirectional streaming RPC.
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

gRPC for Clients

For the schema RouteGuide, when rpcRole is client, the Wire compiler will generate the following interface:

interface RouteGuideClient : Service {
  fun GetFeature(): GrpcCall<Point, Feature>

  fun ListFeatures(): GrpcStreamingCall<Rectangle, Feature>

  fun RecordRoute(): GrpcStreamingCall<Point, RouteSummary>

  fun RouteChat(): GrpcStreamingCall<RouteNote, RouteNote>
}

For streaming APIs, we return a GrpcStreamingCall, and a GrpcCall otherwise. With these objects, the caller can communicate with the server. Both blocking and suspending APIs are provided.

Wire runtime

To help you instantiate your service, we provide a small runtime, namely GrpcClient:

val grpcClient = GrpcClient.Builder()
  .client(OkHttpClient.Builder().protocols(listOf(Protocol.H2_PRIOR_KNOWLEDGE)).build())
  .baseUrl(serverUrl)
  .build()
val routeGuideClient = grpcClient.create(RouteGuideClient::class)

gRPC for Servers

Blocking APIs

Following this configuration:

wire {
  kotlin {
    rpcRole = 'server'
    rpcCallStyle = 'blocking'
  }
}

the Wire compiler will generate the following interface for the server to implement:

interface RouteGuideBlockingServer : Service {
  fun GetFeature(request: Point): Feature

  fun ListFeatures(request: Rectangle, response: MessageSink<Feature>)

  fun RecordRoute(request: MessageSource<Point>): RouteSummary

  fun RouteChat(request: MessageSource<RouteNote>, response: MessageSink<RouteNote>)
}

For streaming connections, the server can use Wire's blocking APIs: MessageSource and MessageSink.

Suspending APIs

Following this configuration:

wire {
  kotlin {
    rpcRole = 'server'
    rpcCallStyle = 'suspending'
  }
}

the Wire compiler will generate the following interface for the server to implement:

interface RouteGuideServer : Service {
  suspend fun GetFeature(request: Point): Feature

  fun ListFeatures(request: Rectangle, response: SendChannel<Feature>)

  fun RecordRoute(request: ReceiveChannel<Point>): RouteSummary

  fun RouteChat(request: ReceiveChannel<RouteNote>, response: SendChannel<RouteNote>)
}

The server can use Kotlin coroutines' suspend and Channels mechanisms to execute suspending network calls.

Implementing Client Interfaces

Wire has helper functions to make it easier to implement its client interfaces. This can be particularly useful for testing. It supports both streaming and non-streaming APIs in Kotlin:

class FakeRouteGuideClient : RouteGuideClient {
  override fun GetFeature(): GrpcCall<Point, Feature> {
    return GrpcCall { request: Point ->
      return@GrpcCall Feature(name = "test", location = request)
    }
  }

  override fun RouteChat(): GrpcStreamingCall<RouteNote, RouteNote> {
    return GrpcStreamingCall { requests: ReceiveChannel<RouteNote>, responses: SendChannel<RouteNote> ->
      try {
        requests.consumeEach { routeNote: RouteNote ->
          responses.send(RouteNote(message = "ACK: ${routeNote.message}"))
        }
      } finally {
        responses.close()
      }
    }
  }

  ...
}

The GrpcCall and GrpcStreamingCall functions are well suited to expression functions:

class FakeRouteGuideClient : RouteGuideClient {
  override fun GetFeature() =
      GrpcCall<Point, Feature> { request ->
        return@GrpcCall Feature(name = "test", location = request)
      }

  override fun RouteChat() =
      GrpcStreamingCall<RouteNote, RouteNote> { requests, responses ->
        try {
          requests.consumeEach { routeNote ->
            responses.send(RouteNote(message = "ACK: ${routeNote.message}"))
          }
        } finally {
          responses.close()
        }
      }

  ...
}

The client interface may also be implemented in Java. Wire only offers a non-streaming helper function. The GrpcStreamingCall above uses coroutines which is Kotlin-only.

public class FakeRouteGuideClient implements RouteGuideClient {
  @Override public GrpcCall<Point, Feature> GetFeature() {
    return GrpcCalls.grpcCall(new Function1<Point, Feature>() {
      @Override public Feature invoke(Point request) {
        return new Feature.Builder()
            .name("test")
            .location(request)
            .build();
      }
    });
  }

  ...
}

These similarly interact nicely with Java lambdas.

Sample

Check out our sample project demonstrating bidirectional communication between an Android application and a server.

Get it

Use Wire runtime Gradle coordinates:

implementation("com.squareup.wire:wire-runtime:LATEST_VERSION")

In addition, to use Wire gRPC for clients, use these new Gradle coordinates:

implementation("com.squareup.wire:wire-grpc-client:LATEST_VERSION")