| = Section 3: Create the gRPC Cart service |
| :page-supergroup-java-scala: Language |
| |
| include::ROOT:partial$include.adoc[] |
| |
| We will create the gRPC `ShoppingCartService` for the *Cart Service* shown in the architectural overview below. The motivation for using gRPC as the inter-service protocol is explained in xref:concepts:akka-grpc.adoc[Akka gRPC concepts]. The {akka-blog}/news/2020/04/08/akka-grpc-intro-video[Introduction to Akka gRPC video {tab-icon}, window="tab"] is also a good starting point for learning Akka gRPC. |
| |
| image::example-grpc-server.svg[Example gRPC service] |
| |
| First, we will create the `ShoppingCartService` with a single operation that adds items to a cart. The implementation will only log the calls for now. Later, we'll expand the service with more operations and real implementations. On this page you will learn how to: |
| |
| * define the interface of a gRPC service |
| * implement the service interface |
| * initialize and run an HTTP server and the service locally |
| * interact with the service from the command line |
| * deploy to the cloud |
| |
| == Akka Workshop |
| The first video of the |
| https://info.lightbend.com/akka-platform-workshop-part-1-on-demand-recording.html[Akka Workshop Series {tab-icon}, window="tab"] covers creating the gRPC series. |
| |
| == Source downloads |
| |
| If you prefer to simply view and run the example, download a zip file containing the completed code: |
| |
| [.tabset] |
| Java:: |
| + |
| **** |
| * link:_attachments/0-shopping-cart-start-java.zip[Source] with the initial project template. |
| * link:_attachments/1-shopping-cart-grpc-java.zip[Source] with the steps on this page completed. |
| **** |
| |
| Scala:: |
| + |
| **** |
| * link:_attachments/0-shopping-cart-start-scala.zip[Source] with the initial project template. |
| * link:_attachments/1-shopping-cart-grpc-scala.zip[Source] with the steps on this page completed. |
| **** |
| |
| |
| |
| :sectnums: |
| |
| == Create service definition |
| |
| gRPC takes a schema-first approach, where the protocol is declared in a Protobuf service descriptor. From the service descriptor the source code for the messages, client and server stubs are generated. |
| |
| First, create a protobuf service descriptor file: `src/main/protobuf/ShoppingCartService.proto`. In it, define the service interface as shown below: |
| |
| [source,protobuf] |
| ---- |
| include::example$01-shopping-cart-service-scala/src/main/protobuf/ShoppingCartService.proto[] |
| ---- |
| |
| <1> Defines the requests a client may send to the service in the service definition. |
| <2> Describes the request to add an item to the shopping cart. |
| |
| == Generate code |
| |
| The Akka gRPC plugin will generate code from the service descriptor when you compile the project. Run [.group-scala]#`sbt`# [.group-java]#`mvn`# to compile: |
| |
| [.group-scala] |
| [source,shell script] |
| ---- |
| sbt compile |
| ---- |
| |
| [.group-java] |
| [source,shell script] |
| ---- |
| mvn compile |
| ---- |
| |
| TIP: You might have to synchronize the [.group-scala]#`build.sbt`# [.group-java]#`pom.xml`# in IntelliJ after this so that it adds the source directories for the generated code. |
| |
| == Implement the service |
| |
| Let's implement the `ShoppingCartService` [.group-scala]#trait# [.group-java]#interface#. Create a `ShoppingCartServiceImpl` class that extends the generated `ShoppingCartService` [.group-scala]#trait# [.group-java]#interface#. Implement the `addItem` method from the [.group-scala]#trait# [.group-java]#interface# by logging the call and return a successful [.group-scala]#`Future`# [.group-java]#`CompletionStage`#. |
| |
| [.tabset] |
| Java:: |
| + |
| .src/main/java/shopping/cart/ShoppingCartServiceImpl.java: |
| [source,java,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-java/src/main/java/shopping/cart/ShoppingCartServiceImpl.java[] |
| ---- |
| |
| Scala:: |
| + |
| .src/main/scala/shopping/cart/ShoppingCartServiceImpl.scala: |
| [source,scala,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-scala/src/main/scala/shopping/cart/ShoppingCartServiceImpl.scala[] |
| ---- |
| |
| <1> The method corresponding to the `rpc AddItem` in the service definition. Defined in the generated `proto.ShoppingCartService`. |
| |
| [#server] |
| == Initialize HTTP Server |
| |
| We will run the gRPC service implementation in an Akka HTTP server. Add the following server initialization code in a `ShoppingCartServer` [.group-scala]#`object`# [.group-java]#`class`#: |
| |
| [.tabset] |
| Java:: |
| + |
| .src/main/java/shopping/cart/ShoppingCartServer.java: |
| [source,java,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-java/src/main/java/shopping/cart/ShoppingCartServer.java[] |
| ---- |
| |
| Scala:: |
| + |
| .src/main/scala/shopping/cart/ShoppingCartServer.scala |
| [source,scala,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-scala/src/main/scala/shopping/cart/ShoppingCartServer.scala[] |
| ---- |
| |
| <1> Concatenate the service implementation with `ServerReflection` for better `grcurl` support. |
| <2> Start the HTTP server for the gRPC service. |
| <3> Some logging at startup. |
| |
| == Main method |
| |
| To run the service we need a class with a `main` method. Edit the `Main` class that is included from the template project. It should initialize the `ActorSystem` and the `ShoppingCartServer` like this: |
| |
| [.tabset] |
| Java:: |
| + |
| [source,java,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-java/src/main/java/shopping/cart/Main.java[] |
| ---- |
| |
| Scala:: |
| + |
| [source,scala,indent=0] |
| ---- |
| include::example$01-shopping-cart-service-scala/src/main/scala/shopping/cart/Main.scala[] |
| ---- |
| |
| <1> Start an `ActorSystem` with the `Main` actor `Behavior`. |
| <2> Initialization of Akka Management that is used for forming the Akka Cluster. |
| <3> Initialize the gRPC server. This is the code you should add to the existing `Main`. |
| |
| The `grpc.port` configuration is defined in `local1.conf`, which is included in the generated template project. |
| |
| == Run locally |
| |
| You can run this service with: |
| |
| [.group-scala] |
| [source,shell script] |
| ---- |
| sbt -Dconfig.resource=local1.conf run |
| ---- |
| |
| [.group-java] |
| [source,shell script] |
| ---- |
| # make sure to compile before running exec:exec |
| mvn compile exec:exec -DAPP_CONFIG=local1.conf |
| ---- |
| |
| Note the log output from the `ShoppingCartServer` |
| |
| ---- |
| Shopping online at gRPC server 127.0.0.1:8101 |
| ---- |
| |
| === Exercise the service |
| |
| // # tag::exercise[] |
| Use `https://github.com/fullstorydev/grpcurl[grpcurl]` to exercise the service: |
| |
| . Add 3 socks to a cart: |
| + |
| [source,shell script] |
| ---- |
| grpcurl -d '{"cartId":"cart1", "itemId":"socks", "quantity":3}' -plaintext 127.0.0.1:8101 shoppingcart.ShoppingCartService.AddItem |
| ---- |
| |
| . Add 2 t-shirts to the same cart: |
| + |
| [source,shell script] |
| ---- |
| grpcurl -d '{"cartId":"cart1", "itemId":"t-shirt", "quantity":2}' -plaintext 127.0.0.1:8101 shoppingcart.ShoppingCartService.AddItem |
| ---- |
| // # end::exercise[] |
| |
| Note the logging from the `ShoppingCartServiceImpl` in the console. |
| |
| === Stop the service |
| |
| You can stop the service with `ctrl-c`. |
| |
| NOTE: The following steps for cloud deployment are optional. If you are only running locally, you can skip to the next section of the tutorial. |
| |
| [#kubernetes] |
| == Run in Kubernetes |
| |
| We suggest that you read the xref:deployment:index.adoc[] section to get an understanding of how deployment with the Akka Cloud Platform works. |
| |
| === Akka Workshop |
| The fifth video of the https://info.lightbend.com/akka-platform-workshop-part-5-on-demand-recording.html[Akka Workshop Series {tab-icon}, window="tab"] walks you through the process of running in Kubernetes on AWS or GCP. It will take you step-by-step to aid you in understanding the process. |
| |
| |
| Before following the steps below, create a Kubernetes cluster and install the Akka Operator. Used the instructions below for: |
| |
| [.tabset] |
| GCP:: |
| + |
| Follow the instructions in https://cloud.google.com/container-registry/docs/using-with-google-cloud-platform[Using Container Registry with Google Cloud {tab-icon}, window="tab"] |
| to deploy Docker images on GCP's container registry. We suggest that using the Container Registry https://cloud.google.com/container-registry/docs/quickstart[Quickstart {tab-icon}, window="tab"] if you aren't already familiar with the registry. You may also use the https://cloud.google.com/artifact-registry[Artifact Registry {tab-icon}, window="tab"]. There is guidance on https://cloud.google.com/artifact-registry/docs/transition/transition-from-gcr[transitioning {tab-icon}, window="tab"] as well as a https://cloud.google.com/artifact-registry/docs/docker/quickstart[Docker {tab-icon}, window="tab"] quickstart for the Artifact Registry. |
| |
| AWS:: |
| + |
| Follow the instructions in xref:deployment:aws-ecr.adoc[Amazon Elastic Container Registry] to deploy Docker images on AWS's container registry. |
| |
| === Additional steps for Docker and AWS |
| |
| If you are using AWS, you will also need to complete the following procedures. |
| |
| The [.group-scala]#`build.sbt and project/plugins.sbt`# [.group-java]#`pom.xml`# contains build plugin settings for building a Docker image. |
| |
| include::partial$build-docker-for-kube.adoc[] |
| |
| include::deployment:partial$git-init.adoc[] |
| |
| |
| === Update the deployment descriptor |
| |
| In `kubernetes/shopping-cart-service-cr.yml` you will find the deployment descriptor of the `PekkoMicroservice` that the Akka Operator will use when deploying the application in Kubernetes. |
| |
| .kubernetes/shopping-cart-service-cr.yml: |
| [source,yaml] |
| ---- |
| include::example$01-shopping-cart-service-scala/kubernetes/shopping-cart-service-cr.yml[] |
| ---- |
| |
| <1> Replace `<docker-registry>` with your docker registry address and update the image reference with the image tag from the output of the Docker build above, for example: `803424716218.dkr.ecr.eu-central-1.amazonaws.com/shopping-cart-service:20201209-135004-363ae2b`. |
| |
| |
| === Apply to Kubernetes |
| |
| Deploy the `shopping-cart-service-cr.yml` to Kubernetes: |
| |
| [source,shell script] |
| ---- |
| kubectl apply -f kubernetes/shopping-cart-service-cr.yml |
| ---- |
| |
| It will also create the namespace `shopping` |
| |
| It can be convenient to set that namespace as current instead of specifying the `--namespace` parameter in all `kubectl` commands. |
| |
| [source,shell script] |
| ---- |
| kubectl config set-context --current --namespace=shopping |
| ---- |
| |
| The Akka Operator will notice the deployment descriptor and deploy the `shopping-cart-service`. You can see progress by viewing the status: |
| |
| [source,shell script] |
| ---- |
| kubectl get akkamicroservices/shopping-cart-service |
| ---- |
| |
| See xref:deployment:troubleshooting.adoc[troubleshooting deployment status] for more details. |
| |
| === Exercise the service in Kubernetes |
| |
| include::partial$prepare-to-exercise-in-kube.adoc[] |
| |
| include::grpc-service.adoc[tag=exercise] |
| |
| Note the logging from the `ShoppingCartServiceImpl` in the console that is running `kubectl logs -f`. |
| |
| :!sectnums: |
| == Learn more |
| |
| * xref:concepts:akka-grpc.adoc[Akka gRPC concepts] |
| * {akka-grpc}/server/index.html[Akka gRPC server reference documentation {tab-icon}, window="tab"]. |