Monday, 25 October 2021

Unable to ping host.docker.internal

 This is the problem people out there facing a lot where their docker containers are unable to connect to docker host.

In general, host IP will be changing specially in dev machine and this create trouble for docker to resolve the host DNS and establish the connection. Hence Docker recommend a special DNS name host.docker.internal to use which resolves to the internal IP address used by the host.

In windows, we need to add below entry to the C:\Windows\System32\drivers\etc\hosts file as:
192.XXX.XX.XX host.docker.internal

All good but there is a problem where docker containers are unable to ping host.docker.internal and I too face the problem every time I restart the system. There is a huge discussion here and the channel is closed and I don’t see a working solution.

After struggling a lot, finally I figured out a working solution for me which I’m using it since last one month from the date I’m writing this post. Solution is, run the below command once when you restart your system, basically it is a cleanup command to clean unused resources like unused network, cache, unused & dangling containers. Read here more about this.

docker system prune

My Environment:
OS: Windows 10 Enterprise
Docker Desktop Version: 4.1.1

Hope this helps you. Thank you for reading.

Notification Queue : RabbitMQ in .NET Core

 Based on my previous article about RabbitMQ in .Net Core, one of my friend asked me a question that How He can build the Notification feature (bell icon, which we generally see with many applications and with medium too) using RabbitMQ which to publish the user specific notification message.

I said, well it is very easy and all you have to do is: publish the message to a specific queue with a routing queue and exchange type “direct”: publish message to the queues whose binding key exactly matches the routing key of the message.

Note: In my previous article I didn’t mention the exchange type and in result it was using default exchange type as “fan out”: publish messages to all the queues bound to it.

First lets start the RabbitMQ server running in docker container. If you don’t have already, it will pull by default and command it:

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.9-management

Now we will create the Publisher/Sender and here is the complete code:

using RabbitMQ.Client;
using System;
using System.Text;
namespace RabbitMQPublisher
{
class Sender
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
Console.WriteLine("Enter command 'exit' to stop sending meesages");
string message = string.Empty;
string receiver = string.Empty;
do
{
SendMessage(connection, message, receiver);
Console.Write("Enter your message:");
message = Console.ReadLine();
Console.Write("Enter the reciever:");
receiver = Console.ReadLine();

} while (!message.Equals("exit", StringComparison.OrdinalIgnoreCase));
}
}
private static void SendMessage(IConnection connection, string message, string receiver)
{
if (string.IsNullOrEmpty(message))
return;
using (var channel = connection.CreateModel())
{
//Create the exchange type direct which will broadcast messages to a specific queue driven by routingKey.
channel.ExchangeDeclare(exchange: "direct_msg", type: "direct");
channel.QueueDeclare(queue: "Notification",durable: false, exclusive: false, autoDelete: false, arguments: null);var body = Encoding.UTF8.GetBytes(message);var properties = channel.CreateBasicProperties();
properties.Persistent = true;
channel.BasicPublish(exchange: "direct_msg",routingKey: receiver,
basicProperties: properties,
body: body);

Console.WriteLine($" Message Sent to '{receiver}'");
}
}
}
}

In above code, receiver name is an input and based on that code creates a routingKey as “{receiver_name}” input and bind it to the queue “Notification” with exchange type as “direct”. Rest all is same as per my previous article.

Next, we will create Receivers/Subscribers. Here is the complete code:

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

using System;
using System.Text;
namespace RabbitMQSubscriber
{
class Receiver
{
public static void Main()
{
var queueName = "Notification";
Console.Write("Enter UserName:");
string userName = Console.ReadLine();
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "direct_msg", type: "direct");
channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);
channel.QueueBind(queue: queueName, exchange: "direct_msg", routingKey: userName);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine($"Message Received: {message}");
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
//making autoAck: false as we are doint it manually above
channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
}
}

In above Receiver’s code, routing key is set to the user input {user_name} and bind it to the queue named “Notification” and exchange type is “direct”. That’s all, now if you run the both the application as One instance of Sender and multiple instances of Receiver then the output would be as below:

Thank You for reading. Happy Happy RabbitMQueuing.

Monday, 18 October 2021

RabbitMQ in .NET Core

 RabbitMQ is a most popular and widely used open source message broker software. It supports multiple protocols AMQP, STOMP, MQTT, HTTP and WebSockets. Read more about these protocols here.

RabbitMQ is lightweight and can be easily deployed on premises and in the clouds. RabbitMQ is available on Microsoft azure as “RabbitMQ Server” as well as “RabbitMQ as a Service”. It also supports container based hosting environment and I love it and this is what I’m gonna explain here that How easy it is to make it running.

Before we start, you would need Docker Desktop application running on your system. Once you have the docker desktop application, switch to Linux containers and follow the below steps to write your first simple RabbitMQ message brokering application.

Step 1: Pull and host the docker image. Open the powershell command prompt and run the command:

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.9-management

Above command will run host the docker rabbitmq image under localhost:5672 port which uses amqp protocol and the rabbitmq management portal under localhost:15672 which uses http protocol.

Test the RabbitMQ server by browsing rabbitmq management portal: http://localhost:15672/#/

Note: the default userid and password of the portal would be “guest” and “guest”.

Step 2: We are all set with RabbitMQ server hence Lets begin with simple sender and receiver application. Sender will publish a message to a queue and receiver will read it from the same queue.

For this, Lets create two simple .Net 5 console application and name it RabbitMQSender and RabbitMQReceiver.

Step 3: Install RabbitMQ.Client nuget package from nuget store.

Step 3: Now Use the below code to create the sender under RabbitMQSender project which will publish messages to a queue named “hello”.

using RabbitMQ.Client;
using System;
using System.Text;
namespace RabbitMQSender
{
class Sender
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
Console.WriteLine("Enter command 'exit' to stop sending meesages");
string message = string.Empty;
do
{
SendMessage(connection, message);
Console.Write("Enter your next message:");
message = Console.ReadLine();
} while (!message.Equals("exit", StringComparison.OrdinalIgnoreCase));
}
}
private static void SendMessage(IConnection connection, string message)
{
if (string.IsNullOrEmpty(message))
return;
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var body = Encoding.UTF8.GetBytes(message);var properties = channel.CreateBasicProperties();
properties.Persistent = true;
channel.BasicPublish(exchange: "",
routingKey: "hello",
basicProperties: properties,
body: body);
Console.WriteLine(" [x] Sent {0}", message);
}
}
}
}

Above code is simple where we are:
i. creating a connection factory pointing to localhost:5672 (since 5672 is default reserved port for RabbitMQ hence we don’t need to mention in the code as hostname).
ii. establish the connection (var connection = factory.CreateConnection()).
iii. create the channel, session and model (var channel = connection.CreateModel()).
iv. Declare a queue where message will be published (channel.QueueDeclare).
v. publish the message (channel.BasicPublish).

To make sure the message will be delivered to the receiver whenever he comes i’m making it persistent. Properties will also allows you to define the contentType, ContentEncoding, DeliveryMethod, Expiraiton etc.

var properties = channel.CreateBasicProperties();properties.Persistent = true;

Step 4: Use the below code to create the receiver under RabbitMQReceiver project.

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

using System;
using System.Text;
namespace RabbitMQReceiver
{
class Receiver
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "hello",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] Received {0}", message);
channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "hello",
autoAck: false,//making it false as we are doing it manually above
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
}
}

Here too we are following the same process till creating the channel, session and model then declaring the queue. This will be having the same queue which you named for the sender’s queue as this has to listen the message for the same queue.

Once above is done we create the consumer and add the consumer’s Received event and that is where we read the published messages from sender and send back a acknowledgment to the sender. Here instead of RabbitMQ default message acknowledgment feature, I’m doing it manually after I read the message.

channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);

Now you can run both the program and test it. you can run multiple Receivers at a time and RabbitMQ will deliver the message to the active receivers one at a time based on default Round Robin method.

Note: if you would have noticed, I have used exchange=”” publishing the message in sender and it used RabbitMQ’s default Direct exchange protocol which publish the method to one queue at a time. if we need to publish the message for multiple queues then we need to user other exchange protocol as “Topic” or “Fanout”.

Thanks for reading. Happy RabbitMQueuing.