What is Dapr - Distributed Application Runtime?
Dapr, one of the newbies in the tech industry, aims to simplify microservice connectivity. Its end-goal is to create impact, disrupt processes, and advance innovation. How exactly?
Cloud service providers dominate the market. If someone wants to put out an application to the public, there is a good chance it will be hosted on a popular cloud platform. Using these tools, businesses can easily set up the infrastructure, select suitable plans for their use cases, and scale applications to meet customer needs.
Cloud services become extremely useful when combined with today's sought-after pattern - microservices that are distributed systems by nature. This architecture has many challenges, including scalability, resilience, and maintenance costs.
Solving these issues can take a lot of precious time from development. How much better would it be to only focus on the business logic and leave these obstacles for something else? This is where Dapr comes into the picture.
What is Dapr?
Dapr stands for distributed application runtime. It is a self-hosted, open-source project that offers extra functionality to cloud-native applications through APIs, called building blocks, that simplify microservice connectivity.
In practice, the project uses a sidecar pattern to integrate with our application. As Dapr sidecars are added to your system, you can leverage as many building blocks as you need. Dapr’s building block system works with components that run alongside the application as a separate container or process. This solution will mean that communication is accomplished over the local host.
Dapr using the sidecar pattern
What do we gain with this? Through either HTTP or gRPC, we can use the so-called Dapr building block APIs. These building blocks provide implementations for micro-services best practices that we would have had to manually develop otherwise.
- Service-service invocation - Provides reliable and secure service to service invocation with built-in service discovery.
- Actors - If we go with a highly concurrent system, we can use the Actor pattern. Actors are lightweight concurrent entities that communicate with each other asynchronously in an event-driven manner. We write the logic for the Dapr Actors building block, and the system handles the scalability and the communication between them.
- State management - Used to save, read, and query key/value pairs into some kind of store, enabling you to make stateful services.
- Publish & Subscribe - Enables microservices to communicate with each other using messaging systems. These pub sub modules support dead letter queues and guarantee at-least-once message delivery to every subscriber.
- Bindings - The API provides ways to trigger or get triggered by external systems such as databases, queues, and file systems.
- Secrets - Secrets building blocks API makes it easier for developers to consume application secrets from a secret store. We can use queries in the Dapr configuration files and our application code.
- Distributed locks - Enables the application to acquire exclusive access to resources where updates occur.
Under the hood, each API is backed with Dapr components. For example, state stores can be backed by AWS DynamoDb, PostgreSQL, or Redis, to name a few possibilities. One cool thing about this setup is that you can do it freely if you decide to change the component. The code remains the same, and Dapr will handle the implementation for you!
Is Dapr popular?
Although the project was only released in 2019 and many of the components are currently in alpha state, Dapr and its building block system are quickly gaining recognition.
We can see Dapr components being used for different use cases, and Dapr is starting to attract attention on popular trends lists. The developers have regular community meetings and even have an annual expo where members of the Dapr community can meet up with each other and talk about their projects.
There are several reasons behind this runtime gaining more and more popularity.
Focus on Business Logic
Dapr aims to make the life of developers easier. It’s being done by removing the burden of creating all the interactions and integrations between your application and external systems, which are required to make a microservice work.
With Dapr's building block, developers can focus on the business logic and keep their code free from SDKs and libraries that would be required otherwise. In these, Dapr system services will almost always have the building block needed.
As an additional benefit, the optionally will also produce portable applications. If you migrate from a cloud provider to a new one, you can change your components without touching the code.
On top of all that, Dapr building blocks provide configuration options to make the application highly responsive and data consistent. Here are some examples:
- If we decide to work with State management building blocks, we can set what kind of operations we would like to perform. These options include consistency level, transactions, and bulk reading.
- We can limit the concurrency setting, which will constrict all requests from Dapr.
- There is a rich list of resilience policies. We can define retry, circuit-breaker, and timeout policies. We can apply these policies to different components and let it be binding, pub/sub, or state management.
Dapr is flexible in many ways. If you have an application with microservices written in different languages, each team can set up Dapr as it supports all the popular languages and developer frameworks.
Because the environment doesn't really matter, Dapr can be self-hosted for local development purposes and even after you go live. Additionally, Dapr runs natively on Kubernetes and could also be launched on any cloud platform.
Out of the box, Dapr has many ways to track our application.
First, we have logging capabilities. We can see this feature done in real-time by Dapr's internal components on different granularity levels. We also have the option to enable the API logging with the --enable-API-logging switch. Using this, we'll be able to see how our application is communicating with the Dapr sidecar.
Enabling profiling in Dapr will create an endpoint. From there, we can fetch profiling sessions or attach profilers to inspect the application runtime. If we decide to go down this route, it is worth mentioning that Dapr is written in the Go language, so some expertise is required in that.
For a more detailed and visual look, Dapr also reports tracing data. While only the Zipkin format was initially supported, the newer versions have also been updated to support Open Telemetry Collectors.
Last but not least, Dapr exposes a Prometheus endpoint too. It emits Go-related process metrics and API-related statistics, including a number of calls and latencies.
What is Dapr used for?
At its core, Dapr reduces the complexity of distributed application development through its building block APIs. By letting Dapr sidecars take care of common challenges such as service discovery, message broker integration, and service invocation, Dapr allows one to build resilient systems for distributed applications.
More specifically, common use cases for Dapr instances include:
Dapr's component model benefits businesses and developers by decoupling your code from the chosen technology. This makes your application agnostic to the hosting environment and enables portability.
In the same tune, Dapr is great for multi-cloud scenarios where cloud assets, software, and applications are distributed across several cloud environments to use multiple cloud computing platforms in support of a single application or ecosystem.
Polyglot microservice architectures
Applications where provided building blocks are needed
Building microservices on the cloud has never been easier using Dapr. The project's flexibility can be attributed to the microservices that Dapr offers. From actor services to service invocation or pub-sub modules to state management systems, you could opt for all or some Dapr sidecar instances needed to run your application smoothly.
How to use Dapr with Java?
Java is a very popular language for server-side programming. It features a great combination of performance, readability, and maintainability, besides an extensive set of libraries. All in all, it is a good choice for a microservices architecture, where we can utilize what Dapr has to offer.
Fortunately for us, Java is also a supported language. We have an official SDK to communicate with the Dapr APIs alongside Spring support, which helps set up some components.
One of the advantages of Dapr, which shines when programming with Java, is getting rid of many library dependencies that we would use to set up integrations with external systems. We have to worry less about producers and consumers handling events; all we'll need to focus on are the SDK and business logic.
Once we import the io.dapr:dapr-SDK library, we can instantiate a DaprClient instance. This client will communicate with the Dapr sidecar via gRPC on the default port. If we are running multiple containers in local development, it can be changed with environment variables.
The other needed library is io.dapr:dapr-SDK-springboot. It enables the use of annotations for message consumer methods. It will automatically subscribe our application to the topics we are interested in.
There are more ways to fire up the Dapr sidecar. We can use docker compose alongside the application, or we can install a Dapr CLI on our machine. To start the sidecar, we will need 3 things:
- A list of component specifications ( the YAML files),
- The id that identifies the application,,
- And the port we are listening to.
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: inventory-state-store spec: type: state.aws.dynamodb version: v1 metadata: - name: table value: "inventory" - name: region value: "eu-central-1" - name: keyPrefix value: "none"
Example state management component
One con that could be mentioned is that although there are a lot of documentation on how to operate with Dapr, most of those are Azure-specific. This could be a bit foreign if, for example, we are more familiar with AWS.
Working With Dapr: Final Impressions
Dapr tries to achieve a lot. It has a lot of features that can have a huge impact on the way we work positively. It does involve most of the needed aspects of developing microservices. The question is how it will perform in a real-life situation.
I had a very first impression of Dapr when I first used it. The first surprise was when I started setting up my project. I was thinking about what kind of messaging system I will use and what library I should use. Then it struck me that I don't need to think about this; Dapr will handle it for me!
Of course, there were some strange issues I had to deal with. I had a hard time with state management because it could not fetch my pre-populated data. It turned out that Dapr uses some prefixes for the key values, which were only documented in Azure documentation and on Github. I also had to spend extra time finding out how to set up my Terraform code to work well with the Dapr components.
All in all, these minor inconveniences do not take away from my overall positive experience. I believe that Dapr delivers what it promises - to make the developer's life easier.
If you are interested in discovering more about Dapr and its suitability for your use case, do not hesitate to reach out to Netguru and discuss our Java development services.