What makes up a service?

A service is made up by its API (request and response types, strict vs stream, synchronous vs asynchronous), and discovery information (URL, topic name).

To communicate with the API consistently, we recommend using schema-first approaches. The schemas are to be shared by the service implementation and its clients. From the schema the implementing code is generated and used to implement the service and the client code. With this approach, the service and the clients don’t share any code or dependencies.

Synchronous Services

This guide illustrates building service APIs for synchronous communication for use cases which require an instant reply. Most often synchronous communication issues a single request and expects a single reply. Other cases open communication channels for continued communication with possibly ongoing replies.

When using Akka APIs, such as Akka gRPC or Akka HTTP, synchronous communication is still not blocking the caller’s thread. The call returns a handle to a future reply (a Java CompletionStage, Scala Future, or Akka Stream).

gRPC

gRPC services use Protobuf as their language-agnostic schema, which translates to efficient serialization. Protobuf can also be used as a serialization format for messages on Kafka topics. Both producer and consumer agree on the format.

Akka gRPC adds plug-ins to the build system of the application to generate Java or Scala classes corresponding to the Protobuf schema.

Akka gRPC explains how to use gRPC for service APIs.

The Section 3: Create the gRPC Cart service and Section 8: Projection calling gRPC service sections in the tutorial illustrate how to implement a gRPC server and client.

HTTP with JSON / RESTful

The ubiquitous communication protocol is HTTP with text-based messages in JSON. Even though JSON lacks a commonly used schema description, its simplicity, human-readability and support in JavaScript has made the de-facto standard for synchronous communication over system boundaries.

This guide doesn’t cover how REST services can be implemented with Akka HTTP.

Asynchronous Services

Asynchronous service communication decouples services at a higher degree. Sending and receiving is decoupled in time and both parts do not necessarily act at the same time. To ensure delivery of messages asynchronous communication may require extra measures like sending acknowledgements.

The Section 7: Projection publishing to Kafka in the tutorial illustrates how to use Kafka for asynchronous communication between to Microservices.

Service Discovery

Service Discovery is the mechanism service clients will use to locate and consume a movable service. Service Discovery is a key building block to Location Transparency new tab. Service clients relying on Service Discovery no longer need to have previous knowledge of the address of a service and, instead, use a discovery mechanism any time they need to use that address.

The most common discovery mechanism is DNS in which clients look up an address given a hostname. In DNS, the most common registries are DNS-A and DNS-AAAA records. The DNS-SRV records not only resolve the address but also a port. The DNS-SRV record type is a perfect match for orchestration platforms where services not only move across hosts but may also be bound to random ports as they are moved. One last characteristic of DNS-SRV records is the addition of extra metadata new tab including the application protocol (aka service) and the transport protocol (aka proto). DNS-SRV-based Service Discovery is a common strategy in Kubernetes deployments.

Service Discovery look-ups hit a Service Registry that keeps an up-to-date list of addresses providing a given service. In Kubernetes, for example, when a Pod is ready the DNS registry is updated, so a new Service Discovery look-up for that service can now return this new Pod address and port. The Service Registry may resolve the look-up request with one or many valid addresses, so the client can use any load-balancing or caching mechanism it prefers with that list.

The above describes a scenario of client-side Service Discovery where the service publishes a detailed registry of all the instances serving it.

With server-side Service Discovery, on the other hand, the service implementor provides a single, stable entry point and handles the discovery, but also the load-balancing, internally.

Protecting our Service

A Service may experience transient failure or degradation due to excessive load, GC pauses, etc…​ in that case, clients will retry the failed request, which will increase the load. Instead, in order to let the service recover from the transient failure as fast as possible, clients should use Circuit Breakers.