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.
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
gRPC services use Protobuf as their language-agnostic schema and which translate to efficient serialization. Protobuf can also be used as a serialization format for messages on Kafka topics. So that 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.
|This guide doesn’t cover how REST services can be implemented with Akka HTTP.|
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 is the mechanism service clients will use to locate and consume a movable service. Service Discovery is a key building block to Location Transparency . 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 lookup an address resolution given a hostname. In DNS, the most common registries are
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 are they are moved. One last characteristic of
DNS-SRV records is the addition of extra metadata including the application protocol (aka
service) and the transport protocol (aka
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, then 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.
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.