Integrating Amazon Simple Queue Service (SQS) with .NET 6 | Exploring asynchronous message handling.

May 15, 2023 |
Views: 117 |

Reading Time:

While working on a microservice architecture system you will definitely come to a point when you need to communicate with other services. You can either call these service:

  1. Synchronously: Calling Rest API of other service and exchange data through JSON response.
  2. Asynchronously: Using a 3rd party message queue services (like Rabbit MQ, Apache Kafka, AWS Simple Queue Service) and handle data when a message request is received.

It is actually ideal to use the asynchronous path because there are many disadvantages for synchronous communication, such as:

  1. If your service URL is changed you need to update all the other services with the new service URL
  2. If your services make chain calls then the response time will grow and that will make your application very unresponsive.

Today I’m going to show you how you can implement asynchronous communication in your .NET 6 service applications. We are going to use Amazon Simple Queue Service, a fully managed message queuing for microservices, distributed systems, and serverless applications. First, we need a queue. You can create one by following this guideline: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/step-create-queue.html

I have create a queue called neuron-blast-queue. Next, let’s open Visual Studio 2022 and create a new Web API project:

Now, Let’s create a Services folder, create an interface IMessageHandler.cs and an implementation class MessageHander.cs. We also need a DTO class MessageResquest.cs which will hold our request information. Declare a method Task HandleMessage(MessageResquest request) in your IMessageHandler interface. Our code will look like this:

// MessageRequest.cs
namespace AwsSqsDemo.Services
{
public class MessageRequest
{
public RequestTypes RequestType { get; set; }
public string RequestData { get; set; } = default!;
}

public enum RequestTypes
{
UserCreation = 1
}
}

// IMessageHandler.cs
namespace AwsSqsDemo.Services
{
public interface IMessageHandler
{
Task HandleMessage(MessageRequest request);
}
}

// MessageHandler.cs
namespace AwsSqsDemo.Services
{
public class MessageHandler : IMessageHandler
{
public async Task HandleMessage(MessageRequest request)
{
throw new NotImplementedException();
}
}
}

Okay, now let’s add AWSSDK.SQS nuget package in our project:

Let’s create a class AwsSqsClient.cs which will handle all our SQS related tasks like sending or receiving messages:

using Amazon;
using Amazon.Runtime;
using Amazon.SQS;
using Amazon.SQS.Model;
using AwsSqsDemo.Services;
using System.Text.Json;

namespace AwsSqsDemo.Utils
{
public class AwsSqsClient
{
private readonly AmazonSQSClient _client;

public AwsSqsClient()
{
// These values as well as other configurations related values should be retrived from appsettings.json, I’ve hardcoded these for demo purpose
var awsCreds = new BasicAWSCredentials(“AccessKey”, “SecretKey”);
_client = new AmazonSQSClient(awsCreds, RegionEndpoint.APSoutheast1);
}

public async Task PublishAsync(MessageRequest messageBody)
{
try
{
var request = new SendMessageRequest()
{
QueueUrl = “neuron-blast-queue-url”,
MessageBody = JsonSerializer.Serialize(messageBody)
};

var response = await _client.SendMessageAsync(request);

if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
Console.WriteLine($”Message added to queue: {response.MessageId}”);
}
else
{
Console.WriteLine($”Message {response.MessageId} was not sent to queue. Please check”);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

public async Task ReceiveAsync(CancellationToken cancellationToken)
{
var responseObject = new MessageRequest() { RequestType = RequestTypes.Empty };

try
{
var request = new ReceiveMessageRequest()
{
QueueUrl = “neuron-blast-queue-url”,
};

var response = await _client.ReceiveMessageAsync(request, cancellationToken);

if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
var message = response.Messages.FirstOrDefault();
if (message != null)
{
if (!string.IsNullOrEmpty(message.Body)) responseObject = JsonSerializer.Deserialize(message.Body) ?? responseObject;
await _client.DeleteMessageAsync(“neuron-blast-queue-url”, message.ReceiptHandle, cancellationToken);
}
}
else
{
Console.WriteLine($”Message could not be received. Please check”);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

return responseObject;
}
}
}

Now we need a background job processor which will listen to SQS queue and handle our requests whenever there’s a message available. I’m going to create a class Worker.cs which will inherit a system class BackgroundService (IHostedService implementation). This our code:

using AwsSqsDemo.Services;
using AwsSqsDemo.Utils;

namespace AwsSqsDemo
{
public class Worker : BackgroundService
{
private readonly AwsSqsClient _client;
private readonly IServiceScopeFactory _scopeFactory;

public Worker(IServiceScopeFactory scopeFactory)
{
_client = new AwsSqsClient();
_scopeFactory = scopeFactory;
}

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var response = await _client.ReceiveAsync(cancellationToken);

try
{
using (var scope = _scopeFactory.CreateScope())
{
var messageHandler = scope.ServiceProvider.GetRequiredService();
await messageHandler.HandleMessage(response);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}

And this is the code for HandleMessage method:

namespace AwsSqsDemo.Services
{
public class MessageHandler : IMessageHandler
{
public async Task HandleMessage(MessageRequest request)
{
try
{
switch (request.RequestType)
{
case RequestTypes.UserCreation:
//Handle User Creation Logic Here
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}

Now, let’s send a message to the queue. For test purpose I’m sending a message from UserController.cs:

using AwsSqsDemo.Services;
using AwsSqsDemo.Utils;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace AwsSqsDemo.Controllers
{
[Route(“api/[controller]”)]
[ApiController]
public class UserController : ControllerBase
{
[HttpPost]
public async Task CreateUser()
{
try
{
var client = new AwsSqsClient();
await client.PublishAsync(new MessageRequest()
{
RequestType = RequestTypes.UserCreation,
RequestData = JsonSerializer.Serialize(new { Name = “Bishal Sarker” })
});
return Ok();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
}
}

Finally, inject all the services in Program.cs:

That’s all people! Let me know about thoughts in the comments. Thanks.
The fastest way to build a multi-tenant eCommerce platform.

The fastest way to build a multi-tenant eCommerce platform.

Building a multi-tenant eCommerce platform can be an incredibly daunting task. That can be especially true if you are new to this space and exploring new technologies. Many people are busy trying to grow their businesses, but they don’t have the time to manage multiple websites. A multi-tenant eCommerce platform can help you avoid this issue while growing your business at the same time.

read more
How to create clear user stories for your software idea.

How to create clear user stories for your software idea.

We have been in the custom software development industry long enough to see many clients who have wonderful ideas, but lack effective communication. Businesses come to us with vague descriptions of what they want in their software project and expect our developers to figure it all out. These situations are certainly not the clients’ fault. It can be difficult to accurately describe an idea when it only resides in your mind. However, to build a successful software project, the more data developers get, the better it is.

read more
Progressive Web Apps: the future of application development.

Progressive Web Apps: the future of application development.

When it comes to marketing an application, time to market and cost efficiency are two of the most important factors. Progressive Web Apps provide you with the opportunity to create user-friendly applications that are built using just HTML, CSS and JavaScript without the hassle of publishing them in an app store.

However, we do not want you to take our word for it. In this article, we are going to discuss what a competitor analysis exactly is and what questions it will help you answer, so you can make an informed decision for yourself. (Trust me though, you will not want to miss it.)

read more
The Crypto world: A simple explanation of cryptocurrencies and NFTs.

The Crypto world: A simple explanation of cryptocurrencies and NFTs.

From virtual currencies secured by cryptography to encoded digital assets representing real-world objects – the crypto world is moving fast and growing big. Some say it’s a bubble destined to get popped. Whereas some say it will change the world. What does the world of crypto hold? What is a cryptocurrency? What are NFTs? To keep up with the fast-paced world, we need answers. So let us jump in.

read more
SHARE ON SOCIAL MEDIA