Announcing gRPC Kotlin 1.0 for Android and Cloud

December 16, 2020


Link copied to clipboard

Posted by Louis Wasserman, Software Engineer and James Ward, Developer Advocate

Kotlin is now the fourth "most loved" programming language with millions of developers using it for Android, server-side / cloud backends, and various other target runtimes. At Google, we've been building more of our apps and backends with Kotlin to take advantage of its expressiveness, safety, and excellent support for writing asynchronous code with coroutines.

Since everything in Google runs on top of gRPC, we needed an idiomatic way to do gRPC with Kotlin. Back in April 2020 we announced the open sourcing of gRPC Kotlin, something we'd originally built for ourselves. Since then we've seen over 30,000 downloads and usage in Android and Cloud. The community and our engineers have been working hard polishing docs, squashing bugs, and making improvements to the project; culminating in the shiny new 1.0 release! Dive right in with the gRPC Kotlin Quickstart!

For those new to gRPC & Kotlin let's do a quick runthrough of some of the awesomeness. gRPC builds on Protocol Buffers, aka "protos" (language agnostic & high performance data interchange) and adds the network protocol for efficiently communicating with protos. From a proto definition the servers, clients, and data transfer objects can all be generated. Here is a simple gRPC proto:

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

In a Kotlin project you can then define the implementation of the Greeter's SayHello service with something like:

object : GreeterGrpcKt.GreeterCoroutineImplBase() {
    override suspend fun sayHello(request: HelloRequest) =
        HelloReply
            .newBuilder()
            .setMessage("hello, ${request.name}")
            .build()
}

You'll notice that the function has `suspend` on it because it uses Kotlin's coroutines, a built-in way to handle async / reactive IO. Check out the server example project.

With gRPC the client "stubs" are generated making it easy to connect to gRPC services. For the protoc above, the client stub can be used in Kotlin with:

val stub = GreeterCoroutineStub(channel)
val request = HelloRequest.newBuilder().setName("world").build()
val response = stub.sayHello(request)
println("Received: ${response.message}")

In this example the `sayHello` method is also a `suspend` function utilizing Kotlin coroutines to make the reactive IO easier. Check out the client example project.

Kotlin also has an API for doing reactive IO on streams (as opposed to requests), called Flow. gRPC Kotlin generates client and server stubs using the Flow API for stream inputs and outputs. The proto can define a service with unary streaming or bidirectional streaming, like:

service Greeter {
  rpc SayHello (stream HelloRequest) returns (stream HelloReply) {}
}

In this example, the server's `sayHello` can be implemented with Flows:

object : GreeterGrpcKt.GreeterCoroutineImplBase() {
  override fun sayHello(requests: Flow<HelloRequest>): Flow<HelloReply> {
    return requests.map { request ->
      println(request)
      HelloReply.newBuilder().setMessage("hello, ${request.name}").build()
    }
  }
}

This example just transforms each `HelloRequest` item on the flow to an item in the output / `HelloReply` Flow.

The bidirectional stream client is similar to the coroutine one but instead it passes a Flow to the `sayHello` stub method and then operates on the returned Flow:

val stub = GreeterCoroutineStub(channel)
val helloFlow = flow {
  while(true) {
    delay(1000)
    emit(HelloRequest.newBuilder().setName("world").build())
  }
}

stub.sayHello(helloFlow).collect { helloResponse ->
  println(helloResponse.message)
}

In this example the client sends a `HelloRequest` to the server via Flow, once per second. When the client gets items on the output Flow, it just prints them. Check out the bidi-streaming example project.

As you've seen, creating data transfer objects and services around them is made elegant and easy with gRPC Kotlin. But there are a few other exciting things we can do with this...

Android Clients

Protobuf compilers can have a "lite" mode which generates smaller, higher performance classes which are more suitable for Android. Since gRPC Kotlin uses gRPC Java it inherits the benefits of gRPC Java's lite mode. The generated code works great on Android and there is a `grpc-kotlin-stub-lite` artifact which depends on the associated `grpc-protobuf-lite`. Using the generated Kotlin stub client is just like on the JVM. Check out the stub-android example and android example.

GraalVM Native Image Clients

The gRPC lite mode is also a great fit for GraalVM Native Image which turns JVM-based applications into ahead-of-time compiled native images, i.e. they run without a JVM. These applications can be smaller, use less memory, and start much faster so they are a good fit for auto-scaling and Command Line Interface environments. Check out the native-client example project which produces a nice & small 14MB executable client app (no JVM needed) and starts, connects to the server, makes a request, handles the response, and exits in under 1/100th of a second using only 18MB of memory.

Google Cloud Ready

Backend services created with gRPC Kotlin can easily be packaged for deployment in Kubernetes, Cloud Run, or really anywhere you can run docker containers or JVM apps. Cloud Run is a cloud service that runs docker containers and scales automatically based on demand so you only pay when your service is handling requests. If you'd like to give a gRPC Kotlin service a try on Cloud Run:

  1. Deploy the app with a few clicks
  2. In Cloud Shell, run the client to connect to your app on the cloud:
    export PROJECT_ID=PUT_YOUR_PROJECT_ID_HERE
    docker run -it gcr.io/$PROJECT_ID/grpc-hello-world-mvn \
    "java -cp target/classes:target/dependency/* io.grpc.examples.helloworld.HelloWorldClientKt YOUR_CLOUD_RUN_DOMAIN_NAME"

Here is a video of what that looks like:

Check out more Cloud Run gRPC Kotlin examples

Thank You!

We are super excited to have reached 1.0 for gRPC Kotlin and are incredibly grateful to everyone who filed bugs, sent pull requests, and gave the pre-releases a try! There is still more to do, so if you want to help or follow along, check out the project on GitHub.

Also huge shoutouts to Brent Shaffer, Patrice Chalin, David Winer, Ray Tsang, Tyson Henning, and Kevin Bierhoff for all their contributions to this release!