Microservices: Service-to-service to invocation with Dapr, docker-compose, and .NET

unbalanced-tree

Ravi Vijay

Posted on August 30, 2024

Microservices: Service-to-service to invocation with Dapr, docker-compose, and .NET

A lazy wolf

Wolf Fact: Arctic wolves have white coats all year round. This contrasts with Arctic foxes who are white for most of the year but develop a grey coat in the summer months.


Prerequisites: C#, basic of Web API, Visual Studio, Docker Desktop

Have you heard of microservices or Dapr and want to learn more? Perhaps you’d like to know some code to get started. I can assist you.

Microservices:- It is an architectural style that structures an application as a collection of services that are

  1. Highly maintainable and testable
  2. Loosely coupled
  3. Independently deployable
  4. Organized around business capabilities

Microservice is a way to implement a distributed system. All microservices are distributed systems but all distributed systems are not microservices.

Hey, but why do I need this? The more people work on a deployable unit, the less efficient it becomes. Microservice helps in this case, where smaller teams can work on a single deployable unit. These units may or may not be affected by other units. They communicate with one another through various means. That’s where Dapr comes into the picture.

**Dapr**:- Dapr codifies the best practices for building microservice applications into open, independent APIs called building blocks that enable you to create portable applications with the language and framework of your choice. One microservice, for example, is written in Python, while another is written in .NET. They can also easily communicate using Dapr.

Dapr provides so many necessary functionalities for microservice architecture.

Dapr

Dapr


In this article, I’ll primarily discuss service-to-service invocation.

[Dapr’s service invocation process](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/#service-invocation)

Dapr’s service invocation process

Dapr’s service invocation process
  1. Service A makes an HTTP or gRPC call targeting Service B. The call goes to the local Dapr sidecar.

  2. Dapr discovers Service B’s location using the name resolution component which is running on the given hosting platform.

  3. Dapr forwards the message to Service B’s Dapr sidecar (always gRPC calls)

  4. Service B’s Dapr sidecar forwards the request to the specified endpoint (or method) on Service B. Service B then runs its business logic code.

  5. Service B sends a response to Service A. The response goes to Service B’s sidecar.

  6. Dapr forwards the response to Service A’s Dapr sidecar.

  7. Service A receives the response.

There are a few new words like a sidecar. Let’s understand them one by one.

Sidecar:-

Bike with sidecar

Bike with sidecar

In the above image, a sidecar is attached to the bike. Logically, the Dapr distributed application capabilities provider is attached to our Service. That’s why the name sidecar. The Dapr APIs are run and exposed on a separate process ( i.e. the Dapr sidecar) running alongside your application. The Dapr sidecar process is named daprd.

Name Resolution Component:- This component helps in finding the sidecar of the desired service. Dapr provides users with the ability to call other applications that have unique IDs. This functionality allows apps to interact with one another via named identifiers and puts the burden of service discovery on the Dapr runtime.

mTLS Encryption:- Mutual Transport Layer Security (mTLS) is a process that establishes an encrypted TLS connection in which both parties use digital certificates( X.509) to authenticate each other. I won’t go in deep on why we need this. If interested, you may have a look at this Wikipedia article.

In a distributed system, Dapr sidecar architecture helps locate other services, call other services securely, and handle retries in case of temporary service interruptions.

After a basic introductory theory, now let’s do a practical example.
The complete code can be found here.

I would create two microservices(Web APIs) with the names ServiceOne and ServiceTwo.

Basic Configuration:-

ASP.NET Core Web API

ASP.NET Core Web API

Two NuGet packages Dapr.Client and Dapr.AspNetCore is required.

Dapr NuGets for ASP.NET Core

Dapr NuGets for ASP.NET Core

Let’s now create the project ServiceOne with Dapr.

1. Folder Structure:-

(You may keep the folder structure the way you prefer.)

Folder Hierarchy

Folder Hierarchy

2. Startup.cs:-

Add Dapr integration. Appending the AddDapr extension method to the AddControllers() extension method registers the necessary services to integrate Dapr into the MVC pipeline.

ServiceOne Startup.cs

ServiceOne Startup.cs

3. IDaprClientHelper:-

In this interface, I declared a method definition with the name ResponseByDaprClient. This method’s implementation contains steps to invoke other microservices using sidecar.

IDaprClientHelper

IDaprClientHelper

4. DaprClientHelper:-

DaprClientHelper

DaprClientHelper

ResponseByDaprClient method has so many concepts. Let’s understand them.

4.1 httpMethod:- Every API has a unique HTTP request method like PATCH/POST/GET/PUT/DELETE. httpMethod contains this HTTP request method details.

4.2 appId:- Name Resolution Component uses this appId to find and perform service invocation for that particular service. AppId would be mentioned in docker-compose.yaml. I will discuss it later in this article.

4.3 endPoint:- Relative API controller path.

4.4 daprClient:- The Dapr client package allows interaction with other Dapr applications from a .NET application. The daprClient is an instance of DaprClientBuilder that would be used to call other dapr services.

4.5 daprRequest:- A HttpRequestMessage that can be used to perform service invocation for the application identified by appId and invokes the method specified by methodName(endPoint) with the HTTP method specified by httpMethod.

4.6 InvokeMethodAsync:- Perform service invocation using the request provided by request. If the response has a success status code the body will de-serialize using JSON to a value of type T. Other wise an exception will be thrown.

5. IServiceTwoHelper:-

IServiceTwoHelper

In this interface, I declared a method definition with the name GetMessage. This method’s implementation contains steps to call ServiceTwo’s API named GetMessage.

ServiceTwo GetMessage API Controller

ServiceTwo GetMessage API Controller

6. ServiceTwoHelper:-

ServiceTwoHelper

I have injected IDaprClientHelper in this class, so I can use ResponseByDaprClient method for ServiceOne to ServiceTwo communication. ResponseByDaprClient takes three parameters as input.

httpMethod- HttpMethod.Get in this case because ServiceTwo API is a Get method.

appId- “servicetwoapp”, this name will be mentioned in the docker-compose file.

endPoint- “/Home/GetMessage”, it is the address of ServiceTwo API.

7. HomeController:-

HomeController

Now the docker-compose.yaml file.

docker-compose.yaml

docker-compose.yaml

I explained a basic docker-compose file in the first part of this series. So here, I am going to focus on the sidecar part i.e. serviceoneapp-dapr and servicetwoapp-dapr.

  • image: daprd is just the name of the Dapr sidecar process. There are many published Docker images for each of the Dapr components available on Docker Hub. You may use daprd:latest for the latest release. You should always use fix and stable version for the production environment.

  • command: It contains a collection of instructions to run a Dapr sidecar.

  • app-id: ResponseByDaprClient uses this name. The name resolution component uses this name to find the required Dapr sidecar.

  • app-port: This port number will be the same as an exposed port of service’s Dockerfile.

ServiceOne Dockerfile Expose Port

ServiceOne Dockerfile Expose Port

  • ***components***: It is a configuration that defines which state store to use or which pub-sub to use or which binding to use etc.

  • network_mode: Using network_mode, I placed two containers serviceoneapp-dapr and serviceoneapp in the same namespace. It means both have the same IP and the same open TCP ports.

Hey, that’s a lot of explanation but does it even works?
Now Let’s test it.

>>>docker compose up

deploying....( wink)
Enter fullscreen mode Exit fullscreen mode

Any application can invoke a Dapr sidecar by using the native invoke API built into Dapr. The API can be called with either HTTP or gRPC. Use the following URL to call the HTTP API:

[http://localhost](http://localhost):<dapr-port>/v1.0/invoke/<application-id>/method/<method-name>
Enter fullscreen mode Exit fullscreen mode
  • the HTTP port that Dapr is listening on.

  • application ID of the service to call.

  • name of the method to invoke the remote service.

Call to ServiceOne sidecar which invokes ServiceTwo

Call to ServiceOne sidecar which invokes ServiceTwo

Call to ServiceTwo sidecar

Call to ServiceTwo sidecar

That’s how I accomplished service-to-service invocation using docker-compose, Dapr, and a web API.

Friendly Suggestion: Keep the code for every microservice in a separate repository.

Any comments or suggestions would be greatly appreciated.

💖 💪 🙅 🚩
unbalanced-tree
Ravi Vijay

Posted on August 30, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related