This documentation regards version 2.4.4, however the current version is 2.5.1.
Walkthrough
Setting up
To get started, you must obtain the .proto file(s) that describe the interface you want to use and add those files to your project.
Add .proto files to your project’s src/main/protobufsrc/main/protosrc/main/proto directory. See the detailed chapters on sbt, Gradle and Maven for information on picking up .proto definitions from dependencies automatically.
Then add the following configuration to your build:
<project><modelVersion>4.0.0</modelVersion><name>Project name</name><groupId>com.example</groupId><artifactId>my-grpc-app</artifactId><version>0.1-SNAPSHOT</version><properties><akka.grpc.version>2.4.4</akka.grpc.version><grpc.version>1.63.2</grpc.version><project.encoding>UTF-8</project.encoding></properties><repositories><repository><id>akka-repository</id><name>Akka library repository</name><url>https://repo.akka.io/maven</url></repository></repositories><pluginRepositories><pluginRepository><id>akka-repository</id><name>Akka library repository</name><url>https://repo.akka.io/maven</url></pluginRepository></pluginRepositories><dependencies><dependency><groupId>com.lightbend.akka.grpc</groupId><artifactId>akka-grpc-runtime_2.13</artifactId><version>${akka.grpc.version}</version></dependency><!-- for loading of cert, issue #89 --><dependency><groupId>io.grpc</groupId><artifactId>grpc-testing</artifactId><version>${grpc.version}</version></dependency></dependencies><build><plugins><plugin><groupId>com.lightbend.akka.grpc</groupId><artifactId>akka-grpc-maven-plugin</artifactId><version>${akka.grpc.version}</version><!-- Hook the generate goal into the lifecycle,
automatically tied to generate-sources --><executions><execution><goals><goal>generate</goal></goals></execution></executions></plugin></plugins></build></project>
For a complete overview of the configuration options see the chapter for your build tool, sbt, Gradle or Maven.
Dependencies
The Akka gRPC plugin makes your code depend on the akka-grpc-runtime library.
The table below shows direct dependencies of it and the second tab shows all libraries it depends on transitively. Be aware that the io.grpc.grpc-api library depends on Guava.
To use a service, such as the Hello World service described in the server documentation, you only need the protobuf definition (the .proto files) of the service. No additional dependencies to the server project are needed.
For example, this is the definition of a Hello World service:
sourcesyntax ="proto3";
option java_multiple_files =true;
option java_package ="example.myapp.helloworld.grpc";
option java_outer_classname ="HelloWorldProto";package helloworld;////////////////////////////////////// The greeting service definition.
service GreeterService{//////////////////////// Sends a greeting //////////*****/////////// HELLO //////////*****/////////
rpc SayHello(HelloRequest) returns (HelloReply){}// Comment spanning// on several lines
rpc ItKeepsTalking(stream HelloRequest) returns (HelloReply){}/*
* C style comments
*/
rpc ItKeepsReplying(HelloRequest) returns (stream HelloReply){}/* C style comments
* on several lines
* with non-empty heading/trailing line */
rpc StreamHellos(stream HelloRequest) returns (stream HelloReply){}}// The request message containing the user's name.
message HelloRequest{string name =1;}// The response message containing the greetings
message HelloReply{string message =1;}
From this definition, Akka gRPC generates interfaces that look like this:
source // Generated by Akka gRPC. DO NOT EDIT.package example.myapp.helloworld.grpc
import akka.annotation.ApiMayChangeimport akka.grpc.AkkaGrpcGenerated/**
* #services
* //////////////////////////////////// The greeting service definition.
*/@AkkaGrpcGenerated
trait GreeterService{/**
* ////////////////////
* Sends a greeting //
* //////*****/////////
* HELLO //
* //////*****/////////
*/def sayHello(in: example.myapp.helloworld.grpc.HelloRequest): scala.concurrent.Future[example.myapp.helloworld.grpc.HelloReply]/**
* Comment spanning
* on several lines
*/def itKeepsTalking(in: akka.stream.scaladsl.Source[example.myapp.helloworld.grpc.HelloRequest, akka.NotUsed]): scala.concurrent.Future[example.myapp.helloworld.grpc.HelloReply]/**
* C style comments
*/def itKeepsReplying(in: example.myapp.helloworld.grpc.HelloRequest): akka.stream.scaladsl.Source[example.myapp.helloworld.grpc.HelloReply, akka.NotUsed]/**
* C style comments
* on several lines
* with non-empty heading/trailing line */def streamHellos(in: akka.stream.scaladsl.Source[example.myapp.helloworld.grpc.HelloRequest, akka.NotUsed]): akka.stream.scaladsl.Source[example.myapp.helloworld.grpc.HelloReply, akka.NotUsed]}@AkkaGrpcGeneratedobjectGreeterServiceextends akka.grpc.ServiceDescription{
val name ="helloworld.GreeterService"
val descriptor: com.google.protobuf.Descriptors.FileDescriptor=
example.myapp.helloworld.grpc.HelloworldProto.javaDescriptor;objectSerializers{import akka.grpc.scaladsl.ScalapbProtobufSerializer
val HelloRequestSerializer=newScalapbProtobufSerializer(example.myapp.helloworld.grpc.HelloRequest.messageCompanion)
val HelloReplySerializer=newScalapbProtobufSerializer(example.myapp.helloworld.grpc.HelloReply.messageCompanion)}@ApiMayChange@AkkaGrpcGeneratedobjectMethodDescriptors{import akka.grpc.internal.Marshallerimport io.grpc.MethodDescriptorimportSerializers._
val sayHelloDescriptor:MethodDescriptor[example.myapp.helloworld.grpc.HelloRequest, example.myapp.helloworld.grpc.HelloReply]=MethodDescriptor.newBuilder().setType(MethodDescriptor.MethodType.UNARY
).setFullMethodName(MethodDescriptor.generateFullMethodName("helloworld.GreeterService","SayHello")).setRequestMarshaller(newMarshaller(HelloRequestSerializer)).setResponseMarshaller(newMarshaller(HelloReplySerializer)).setSampledToLocalTracing(true).build()
val itKeepsTalkingDescriptor:MethodDescriptor[example.myapp.helloworld.grpc.HelloRequest, example.myapp.helloworld.grpc.HelloReply]=MethodDescriptor.newBuilder().setType(MethodDescriptor.MethodType.CLIENT_STREAMING
).setFullMethodName(MethodDescriptor.generateFullMethodName("helloworld.GreeterService","ItKeepsTalking")).setRequestMarshaller(newMarshaller(HelloRequestSerializer)).setResponseMarshaller(newMarshaller(HelloReplySerializer)).setSampledToLocalTracing(true).build()
val itKeepsReplyingDescriptor:MethodDescriptor[example.myapp.helloworld.grpc.HelloRequest, example.myapp.helloworld.grpc.HelloReply]=MethodDescriptor.newBuilder().setType(MethodDescriptor.MethodType.SERVER_STREAMING
).setFullMethodName(MethodDescriptor.generateFullMethodName("helloworld.GreeterService","ItKeepsReplying")).setRequestMarshaller(newMarshaller(HelloRequestSerializer)).setResponseMarshaller(newMarshaller(HelloReplySerializer)).setSampledToLocalTracing(true).build()
val streamHellosDescriptor:MethodDescriptor[example.myapp.helloworld.grpc.HelloRequest, example.myapp.helloworld.grpc.HelloReply]=MethodDescriptor.newBuilder().setType(MethodDescriptor.MethodType.BIDI_STREAMING
).setFullMethodName(MethodDescriptor.generateFullMethodName("helloworld.GreeterService","StreamHellos")).setRequestMarshaller(newMarshaller(HelloRequestSerializer)).setResponseMarshaller(newMarshaller(HelloReplySerializer)).setSampledToLocalTracing(true).build()}}
source // Generated by Akka gRPC. DO NOT EDIT.package example.myapp.helloworld.grpc;import akka.grpc.ProtobufSerializer;import akka.grpc.javadsl.GoogleProtobufSerializer;import akka.grpc.AkkaGrpcGenerated;/**
* //////////////////////////////////// The greeting service definition.
*/publicinterfaceGreeterService{/**
* ////////////////////
* Sends a greeting //
* //////*****/////////
* HELLO //
* //////*****/////////
*/
java.util.concurrent.CompletionStage<example.myapp.helloworld.grpc.HelloReply> sayHello(example.myapp.helloworld.grpc.HelloRequestin);
java.util.concurrent.CompletionStage<com.google.api.HttpBody> sayHelloHttp(example.myapp.helloworld.grpc.HelloRequestin);/**
* Comment spanning
* on several lines
*/
java.util.concurrent.CompletionStage<example.myapp.helloworld.grpc.HelloReply> itKeepsTalking(akka.stream.javadsl.Source<example.myapp.helloworld.grpc.HelloRequest, akka.NotUsed>in);/**
* C style comments
*/
akka.stream.javadsl.Source<example.myapp.helloworld.grpc.HelloReply, akka.NotUsed> itKeepsReplying(example.myapp.helloworld.grpc.HelloRequestin);/**
* C style comments
* on several lines
* with non-empty heading/trailing line */
akka.stream.javadsl.Source<example.myapp.helloworld.grpc.HelloReply, akka.NotUsed> streamHellos(akka.stream.javadsl.Source<example.myapp.helloworld.grpc.HelloRequest, akka.NotUsed>in);staticString name ="helloworld.GreeterService";static akka.grpc.ServiceDescription description =new akka.grpc.internal.ServiceDescriptionImpl(name,HelloWorldProto.getDescriptor());@AkkaGrpcGeneratedpublicstaticclassSerializers{publicstaticProtobufSerializer<example.myapp.helloworld.grpc.HelloRequest>HelloRequestSerializer=newGoogleProtobufSerializer<>(example.myapp.helloworld.grpc.HelloRequest.parser());publicstaticProtobufSerializer<example.myapp.helloworld.grpc.HelloReply>HelloReplySerializer=newGoogleProtobufSerializer<>(example.myapp.helloworld.grpc.HelloReply.parser());publicstaticProtobufSerializer<com.google.api.HttpBody> google_api_HttpBodySerializer =newGoogleProtobufSerializer<>(com.google.api.HttpBody.parser());}}
and model case classes for HelloRequest and HelloResponse.
The service interface is the same for the client and the server side. On the server side, the service implements the interface, on the client side the Akka gRPC infrastructure implements a stub that will connect to the remote service when called.
There are 4 different types of calls:
unary call - single request that returns a FutureCompletionStage with a single response, see sayHello in above example
client streaming call - Source (stream) of requests from the client that returns a FutureCompletionStage with a single response, see itKeepsTalking in above example
server streaming call - single request that returns a Source (stream) of responses, see itKeepsReplying in above example
client and server streaming call - Source (stream) of requests from the client that returns a Source (stream) of responses, see streamHellos in above example
Writing a Client Program
Let’s use these 4 calls from a client. Start by generating code from the .proto definition with:
sourcepackage example.myapp.helloworld
import akka.{Done,NotUsed}import akka.actor.ActorSystemimport akka.grpc.GrpcClientSettingsimport akka.stream.scaladsl.Sourceimport example.myapp.helloworld.grpc._
import scala.concurrent.Futureimport scala.concurrent.duration._
import scala.util.{Failure,Success}objectGreeterClient{def main(args:Array[String]):Unit={// Boot akkaimplicit val sys =ActorSystem("HelloWorldClient")implicit val ec = sys.dispatcher
// Configure the client by code:
val clientSettings =GrpcClientSettings.connectToServiceAt("127.0.0.1",8080).withTls(false)// Or via application.conf:// val clientSettings = GrpcClientSettings.fromConfig(GreeterService.name)// Create a client-side stub for the service
val client:GreeterService=GreeterServiceClient(clientSettings)// Run examples for each of the exposed service methods.
runSingleRequestReplyExample()
runStreamingRequestExample()
runStreamingReplyExample()
runStreamingRequestReplyExample()
sys.scheduler.scheduleWithFixedDelay(1.second,1.second){()=> runSingleRequestReplyExample()}def runSingleRequestReplyExample():Unit={
sys.log.info("Performing request")
val reply = client.sayHello(HelloRequest("Alice"))
reply.onComplete {caseSuccess(msg)=>
println(s"got single reply: $msg")caseFailure(e)=>
println(s"Error sayHello: $e")}}def runStreamingRequestExample():Unit={
val requests =List("Alice","Bob","Peter").map(HelloRequest(_))
val reply = client.itKeepsTalking(Source(requests))
reply.onComplete {caseSuccess(msg)=>
println(s"got single reply for streaming requests: $msg")caseFailure(e)=>
println(s"Error streamingRequest: $e")}}def runStreamingReplyExample():Unit={
val responseStream = client.itKeepsReplying(HelloRequest("Alice"))
val done:Future[Done]=
responseStream.runForeach(reply => println(s"got streaming reply: ${reply.message}"))done.onComplete {caseSuccess(_)=>
println("streamingReply done")caseFailure(e)=>
println(s"Error streamingReply: $e")}}def runStreamingRequestReplyExample():Unit={
val requestStream:Source[HelloRequest,NotUsed]=Source.tick(100.millis,1.second,"tick").zipWithIndex
.map {case(_, i)=> i }.map(i =>HelloRequest(s"Alice-$i")).take(10).mapMaterializedValue(_ =>NotUsed)
val responseStream:Source[HelloReply,NotUsed]= client.streamHellos(requestStream)
val done:Future[Done]=
responseStream.runForeach(reply => println(s"got streaming reply: ${reply.message}"))done.onComplete {caseSuccess(_)=>
println("streamingRequestReply done")caseFailure(e)=>
println(s"Error streamingRequestReply: $e")}}}}