Microservices and cloud native applications are the flavor of the season. Modern applications based on the microservices design pattern come with their own set of challenges. Each service has to implement a class of datastore such as relational, key/value, NoSQL, and graph database aligned with the functionality. Microservices need to have a robust service discovery mechanism for dynamic connectivity. They need to be loosely coupled for autonomy and independent scaling. Microservices are polyglot where each service is implemented in the most appropriate language, framework, and runtime.
Though adopting containers and orchestration engines such as Kubernetes tackle the challenges in packaging, deployment, and scaling, the development process remains complex.
Late last year, Microsoft announced a new approach to developing modern applications based on Distributed Application Runtime (Dapr) which is a platform and language agnostic runtime for microservices and cloud native applications.
The architecture of Dapr is fascinating for developers especially for those who are familiar with COM or Enterprise Java Beans (EJB) of 1990s. Similar to how COM/DCOM and EJB provided the plumbing for distributed applications running on Microsoft Windows and Java, Dapr does the heavy lifting required by containerized, cloud native, microservices-based applications.
A Closer Look at Dapr
Dapr has three elements — Runtime, service building blocks, native libraries and SDKs.
Dapr runtime can be installed in any machine that can run Docker Engine. The core runtime is containerized which can be deployed on x86/ARM processors running Linux, Windows, or Mac operating systems. It can be installed in a multinode Kubernetes cluster or on resource-constrained edge device such as a Raspberry Pi Zero. The only requirement is the host environment should be capable of running Docker. Check out the supported versions and platforms in Dapr Releases.
Dapr Building Blocks
Dapr makes it extremely simple to consume other microservices running in the same host, internal or external data services, and messaging services based on the pub/sub pattern. Actors, event-driven resource binding, and distributed tracing are the other building blocks available in the initial release. Dapr building blocks decouple internal consumers from external services. For example, without changing a line of code, you can switch from using Amazon DyanamoDB to Azure ComosDB. Building blocks are exposed via standard REST or gRPC endpoints.
Since Dapr uses the sidecar pattern, it makes it easy to integrate microservices running on Kubernetes to leverage the building blocks. When a pod is annotated with Dapr-specific labels, the runtime automatically injects the sidecar. We will take a closer look at the building blocks in the next section.
Native Libraries and SDKs
Dapr Building Blocks
Dapr building blocks act as an intermediary layer between internal/external service providers configured as components and internal consumers. Since each service consumer always talks to a well-known endpoint, Dapr simply redirects the call to the currently configured component.
Components encapsulate the implementation of a building block API. For example, implementations for the state building block may include Redis, etcd, MongoDB, Amazon DynamoDB, and Azure CosmosDB. Many of the components are pluggable so that one implementation can be easily swapped out for another.
Currently, in the alpha release of Dapr, the below building block services are supported. This list will grow as Dapr matures.
Resilient service-to-service invocation enables method calls, including retries, on remote services wherever they are located in the supported hosting environment. This resembles the functionality of popular service mesh implementations such as Istio and Linkerd.
With state management for storing key/value pairs, long-running, highly available, stateful services can be easily written alongside stateless services in your application. The state store is pluggable and can include Azure CosmosDB, AWS DynamoDB or Redis among others.
Publishing events and subscribing to topics between services enables event-driven architectures to simplify horizontal scalability and make them resilient to failure. Dapr provides at least once message delivery guarantee. This building block abstracts services such as Redis, RabbitMQ, Azure Service Bus, and NATS.
Event-driven Resource Bindings
Resource bindings with triggers extend event-driven architectures for scale and resiliency by receiving and sending events to and from any external resource such as databases, queues, and file systems. Resource bindings provide a declarative mechanism to send and receive events in a standard Cloud Events specification.
Dapr supports distributed tracing to easily diagnose and observe inter-service calls in production using the W3C Trace Context standard. Since Dapr has visibility into the call chain, it can provide insights into the tracing information which can be exported in OpenTelemetry format.
Actors is a pattern for stateful and stateless objects that make concurrency simple with the method and state encapsulation. Dapr provides many capabilities in its actor runtime including concurrency, state, life-cycle management for actor activation/deactivation and timers and reminders to wake-up actors. Developers can easily implement the actor pattern for microservices running on top of Dapr.
Dapr brings some of the proven techniques and best practices to microservice development. It does what ODBC and JDBC did to data-driven client/server applications of the 90s through a plug-and-play model for consuming most common services needed by modern, cloud native applications.
In the next part of Dapr series, I will walk you through a tutorial of using Redis and etcd as the backend to deal with stateful microservices. Stay tuned.
Janakiram MSV’s Webinar series, “Machine Intelligence and Modern Infrastructure (MI2)” offers informative and insightful sessions covering cutting-edge technologies. Sign up for the upcoming MI2 webinar at http://mi2.live.