AWS SQS With LocalStack

In this post I’m going to show how to get Amazon’s SQS queue working locally using LocalStack for Docker.

Installing LocalStack for Docker

The first step is to download and install Docker. If you don’t have Docker installed on your Windows PC, you’ll need to search for the docker download. Look for “docker engine community for windows” to find the right one. Once you install it on your machine, you’ll need to start it up and create a login. Docker will ask if you want to run Windows containers instead of Linux containers. Answer “yes”. You should see the whale symbol in your desktop icons.

Now that Docker is running on your PC, you need to startup PowerShell as an Administrator. Then you can paste the following command into PowerShell to get LocalStack SQS running (this is all one line):

docker run -it -p 4567-4578:4567-4578 -p 8080:8080 atlassianlabs/localstack

The first time you run this command, Docker will download the LocalStack software from the repository. Your window will look something like this:

Docker downloading LocalStack from the repository

This will take a few minutes to download and startup. When LocalStack has started your window should look something like this:

LocalStack in Docker is ready

I have created two programs to demonstrate how to transmit data through the queue and a program to receive data from the queue. The transmit program includes code to create a queue. Every time you startup LocalStack you’ll need to re-create the SQS queue. When you set up your SQS queue in AWS, you’ll script it and then it will be permanent.

Here’s the code for transmitting data through the queue:

using System;
using Amazon.Runtime;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace TestAwsSQS
{
    class Program
    {
        private static string SecretKey = "ignore";
        private static string AccessKey = "ignore";
        private static string ServiceUrl = "http://localhost:4576";
        private static string QueueName = "myQueue";
        private static string QueueUrl = "http://localhost:4576/0123456789/myQueue";
        
        static void Main(string[] args)
        {
            CreateQueue();
            Console.WriteLine(SendMessage("this is my message"));
        }

        private static void CreateQueue()
        {
            var awsCreds = new BasicAWSCredentials(AccessKey, SecretKey);
            var config = new AmazonSQSConfig
            {
                ServiceURL = ServiceUrl,
                //RegionEndpoint = RegionEndpoint.USEast1
            };

            var amazonSqsClient = new AmazonSQSClient(awsCreds, config);
            var createQueueRequest = new CreateQueueRequest();

            try
            {
                amazonSqsClient.GetQueueUrl(QueueName);
            }
            catch
            {
                try
                {
                    // create channel
                    createQueueRequest.QueueName = QueueName;
                    var createQueueResponse = amazonSqsClient.CreateQueue(createQueueRequest);
                    Console.WriteLine("QueueUrl : " + createQueueResponse.QueueUrl);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
        }

        private static string SendMessage(string message)
        {
            var awsCreds = new BasicAWSCredentials(AccessKey, SecretKey);
            var config = new AmazonSQSConfig
            {
                ServiceURL = ServiceUrl,
                //RegionEndpoint = RegionEndpoint.USEast1
            };

            var amazonSqsClient = new AmazonSQSClient(awsCreds, config);

            var sendMessageRequest = new SendMessageRequest
            {
                QueueUrl = QueueUrl,
                MessageBody = message
            };

            var sendMessageResponse = amazonSqsClient.SendMessage(sendMessageRequest);
            return sendMessageResponse.SequenceNumber;
        }
    }
}

Make sure you install the AWS SDK NuGet package for SQS:

Install-Package AWSSDK.SQS

When you run the console program above, a queue called myQueue will be created. LocalStack ignores the credentials. When you move your code into AWS, you’ll need to generate a SecretKey and an AccessKey. You’ll also need to replace “0123456789” in the QueueUrl variable with your Amazon account number. The URL will be something like:

sqs.us-east-1.amazonaws.com

The sample above is for the east coast region 1. Go here to find the exact URL you need to access the queue in your location.

If you look at the C# code above, you’ll see that I broke out the queue create code and I call it every time. You only need to call this once for your test system, but it works OK if you call it every time. In your production code, you’ll want to leave out the code for creating the queue.

One other detail that you need to be aware of: The region setting in the C# code cannot be set when using LocalStack (I show it commented out in the code above). This causes the code to crash. It does, however, need to be set for an AWS cloud system and it must match the URL you use for your queue url.

The SendMessage method in the C# code sends only a string of characters. If you want to send complex data, you can serialize a class and send that. There is a maximum packet size of 256k. If you want to know other limits of SQS you can visit this page.

The SendMessage method also returns a string value that represents the sequence number in the queue. LocalStack cannot do FIFO queues (First In First Out). Therefore, this sample code uses a standard queue and not a FIFO queue. The standard queue has no ordering and always returns null for this parameter. If you use a FIFO queue in AWS, you can get the sequence number from this method.

Here is the code you can use to receive a message. Copy this into another console application and run it after you have created the queue form the above code. This code has an endless loop that will keep monitoring the queue. Every time you run the code above, another message will be captured by the code below and displayed in the console.

using System;
using Amazon.Runtime;
using Amazon.SQS;
using Amazon.SQS.Model;

namespace TestAwsSQSReceive
{
    class Program
    {
        private static string SecretKey = "ignore";
        private static string AccessKey = "ignore";
        private static string ServiceUrl = "http://localhost:4576";
        private static string QueueUrl = "http://localhost:4576/0123456789/myQueue";
        
        static void Main(string[] args)
        {
            ReadMessage();
        }

        private static void ReadMessage()
        {
            // create channel
            var awsCredentials = new BasicAWSCredentials(AccessKey, SecretKey);
            var config = new AmazonSQSConfig
            {
                ServiceURL = ServiceUrl,
                //RegionEndpoint = RegionEndpoint.USEast1
            };

            var amazonSqsClient = new AmazonSQSClient(awsCredentials, config);

            var receiveMessageRequest = new ReceiveMessageRequest();
            receiveMessageRequest.QueueUrl = QueueUrl;

            while (true)
            {
                receiveMessageRequest.WaitTimeSeconds = 15;
                var receiveMessageResponse = amazonSqsClient.ReceiveMessage(receiveMessageRequest);

                if (receiveMessageResponse.Messages.Count == 0)
                {
                    continue;
                }
                
                foreach (var message in receiveMessageResponse.Messages)
                {
                    Console.WriteLine(message.Body);

                    var deleteReq = new DeleteMessageRequest
                    {
                        QueueUrl = QueueUrl,
                        ReceiptHandle = message.ReceiptHandle
                    };
                    var deleteResp = amazonSqsClient.DeleteMessage(deleteReq);
                }
            }
        }
    }
}

Again, you’ll have to include the NuGet package for AWS SDK SQS:

Install-Package AWSSDK.SQS

One other thing to note: SQS runs on port 4576. That is indicated in the startup status in PowerShell when LocalStack is started:

In the ReadMessage method above, the message is deleted from the queue right after it is read and written to the console. If you don’t delete the message, then it will remain in the queue.

Summary

I hope this code helps you get your AWS SQS queue up and running fast. I had to obtain pieces of this information from different sources to form a complete working program. I also discovered through trial and error some of pitfalls that I described in this article. If you find something missing or this article gets out of date (AWS may move their links around), send me an email and give me a heads-up. Or post a message on this article. I’ll fix it.

Leave a Reply