How to consume RabbitMQ in WCF? - c#

I have a scenerio,where a executable is the producer and WCF service is the consumer.
WCF service WorkFlow is as follows:
1)Service invokes the executable (producer), this executable is another process which produces the messages into RabbitMQ Queue.
2)Service has to consume the messages from the RabbitMQ Queue
3)Returns the data to client.
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace ConnectionServices
{
public class Connection : IConnection
{
public string ConnectSite(string provider, string server, string siteName)
{
InvokeProducer(provider, server, siteName);
string activeInstance = RunRabbitMQ();
return activeInstance;
}
public void InvokeProducer(string provider, string server, string siteName)
{
string siteManagerExePath = #"C:\Users\mbmercha\Documents\Visual Studio 2015\Projects\Producer\Producer\bin\Debug\Producer.exe";
try
{
ProcessStartInfo startInfo = new ProcessStartInfo();
Process siteManagerProcess = new Process();
startInfo.FileName = siteManagerExePath;
startInfo.Arguments = string.Format("{0} {1} {2} {3}", "-b ", provider, server, siteName);
siteManagerProcess.StartInfo = startInfo;
siteManagerProcess.Start();
siteManagerProcess.WaitForExit();
}
catch (Exception e)
{
}
}
public string RunRabbitMQ()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
string activeInstance = null;
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare("DurableQueue", true, false, false, null);
channel.ExchangeDeclare("DurableExchange", ExchangeType.Topic, true);
channel.QueueBind("DurableQueue", "DurableExchange", "durable");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
activeInstance = message;
};
channel.BasicConsume(queue: "DurableQueue",
autoAck: false,
consumer: consumer);
}
return activeInstance;
}
}
}
So far service is able to invoke executable and messages are produced in the queue.
But service fails from step 2, it is returning null instead of actual message.
Can anybody suggest me what I am missing here?
Thanks in Advance.

You're never setting activeInstance to anything except null.
You appear to be using the asynchronous API, which means that you're retrieving the message from RabbitMQ long after the RunRabbitMQ method call has completed... or you would be if you didn't immediately dispose of all the consumer machinery when returning.
If you want to retrieve messages synchronously - in this case, within a synchronous method call - you'll need to wait for a message to become available. For this, you'd want to use the 'pull API', which is channel.BasicGet(...).

Related

only one consumer receives messages from the queue on RabbitMQ

I created a small demo to show the RabbitMQ basics. Unfortunatly it doesn't work as expected an has two issues. I am using .NET Core 3.1 and RabbitMQ.Client 6.2.2
I created the Employee class which receives messages from the task queue. The first employee is working nice but if I start more employees they don't work (don't receive messages). And I can't figure out why that would be.
And if I have a lot of messages in the queue (before starting the second employee) I see that all messages in the tasks queue get ACKed when the second starts and then after a short time they become UNACKed again. Somehow weird.
But mainly: why do the other employees not work?
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;
namespace DemoTasks.Employee
{
class Employee
{
static void Main(string[] args)
{
string clientName = "Employee-" + Guid.NewGuid().ToString();
Console.Title = clientName;
Console.WriteLine("Moin moin");
IConnectionFactory connectionFactory = new ConnectionFactory
{
HostName = "localhost",
Port = 5672,
VirtualHost = "/",
UserName = "user",
Password = "password",
ClientProvidedName = clientName
};
using (IConnection connection = connectionFactory.CreateConnection(clientName))
{
using (IModel model = connection.CreateModel())
{
model.ExchangeDeclare("jobs", "fanout", false, false, null);
model.QueueDeclare("tasks", true, false, false);
model.QueueBind("tasks", "jobs", "", null);
EventingBasicConsumer consumer = new EventingBasicConsumer(model);
consumer.Received += OnWorkReceived;
model.BasicConsume("tasks", false, clientName + ":OnWorkReceived", consumer);
Console.ReadLine();
model.Close();
}
connection.Close();
}
Console.WriteLine("Wochenende ... woooh !!!");
}
private static void OnWorkReceived(object sender, BasicDeliverEventArgs e)
{
EventingBasicConsumer consumer = (EventingBasicConsumer)sender;
IModel model = consumer.Model;
string task = Encoding.UTF8.GetString(e.Body.ToArray());
Console.Write("working on: " + task + " ... ");
Thread.Sleep(5000);
Console.WriteLine("done!");
model.BasicAck(e.DeliveryTag, false);
}
}
}
I think your problem is about setting PrefetchCount on your channel. It's about how many messages that one consumer can get from rabbit and cache them on itself to process them.
If don't set it, one consumer can consume all messages on queue and no time to get messages by other consumers, so you can set it by using channel.basicQos(1) or basicqos(0,1,false). By this setting every consumer can get one message after send ack to rabbit then can get another one.
When set prefetch count to lower number, can affect on performance because your consumer must ask rabbit more to get messages .
For detail information see this: https://www.rabbitmq.com/consumer-prefetch.html

RabbitMQ Consumer/Reciever not working in C# Windows Form

I am currently working with the rabbitMQ server, As when i try working in c# console application, the publish exchange working and successfully save in the server and the message will lively appear in the console but when i apply my source code in the C# windows form, it will not get all the message sent by the publisher. I put the method in the constructor event but no happen at all it will not receive any message.
Please see source code below
using Publisher;
using RabbitMQ.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Timers;
using RabbitMQ.Client.Events;
using System.Diagnostics;
namespace Consumer
{
public partial class Consumer : Form
{
private EventingBasicConsumer consumer;
ConnectionFactory factory;
public Consumer()
{
factory = new ConnectionFactory() { HostName = Definition.HOSTNAME };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: Definition.EXCHANGE, type: ExchangeType.Fanout);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: Definition.EXCHANGE,
routingKey: "");
Debug.WriteLine(" [*] Waiting for Exchange ARexchange.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (sender, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Debug.WriteLine(" [x] {0}", message);
};
channel.BasicConsume(
queueName,
autoAck: false,
consumer: consumer);
}
InitializeComponent();
}
private void Consumer_Load(object sender, EventArgs e)
{
}
private void setExchange()
{
lblExchange.Text = Definition.EXCHANGE;
}
}
}
Please read this: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
When those using statements exit, the channel and connection will be closed. Change your code to save those as instance variables in the Consumer class. Then, when your form exits, you can clean up those two instances.
If you provide your code in a repository that can be cloned, compiled and run people could assist you by submitting pull requests to improve your code.
NOTE: the RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.

Connection closes after few seconds when connecting to ActiveMQ set up on AWS

I am connecting to Apache Active MQ which is hosted on AWS to integrate my app to a custom service. I need to keep this running always, not one time like it's right now. The code below works, but only for one message, I need to maintain the connection active all the time listening in order to receive all the messages.
Here is the code.
using Apache.NMS;
using Apache.NMS.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ApacheMQAsync
{
class Program
{
protected static ITextMessage message = null;
public static void Main(string[] args)
{
Uri connecturi = new Uri("URL:61617");
Console.WriteLine("About to connect to " + connecturi);
// NOTE: ensure the nmsprovider-activemq.config file exists in the executable folder.
IConnectionFactory factory = new Apache.NMS.ActiveMQ.ConnectionFactory(connecturi);
IConnection connection = factory.CreateConnection("username", "password");
ISession session = connection.CreateSession();
IDestination destination = SessionUtil.GetDestination(session, "queue://FOO.BAR");
Console.WriteLine("Using destination: " + destination);
// Create a consumer and producer
IMessageConsumer consumer = session.CreateConsumer(destination);
consumer.Listener += new MessageListener(OnMessage);
connection.Start();
// Wait for the message
if (message == null)
{
Console.WriteLine("No message received!");
}
else
{
Console.WriteLine("Received message with ID: " + message.NMSMessageId);
Console.WriteLine("Received message with text: " + message.Text);
}
}
protected static void OnMessage(IMessage receivedMsg)
{
message = receivedMsg as ITextMessage;
message.Acknowledge();
}
}
}
On the console it displays following
No message received!
and after few seconds the console exist?
There's no real magic there, you need to do something to keep your application running such as pausing on console input or looping on a sleep or other wait type call and then checking something to see if your application should continue. The JMS client isn't guaranteed to keep your application open and running and you should never rely on it to.

Registering RabbitMQ Consumer with .NET Core?

I'm trying to handle request authorization in a microservice based architecture using a message queue (RabbitMQ).
I've got a receiver and sender configured fine as a console application in .NET Core per these instructions. However, when using this in a real world example, my application receiving project isn't collecting messages as a consumer.
I'm assuming I have to register the consumer in the Startup.cs, but I can't seem to get this working.
My consumer/responder code:
public class RabbitMqHandler
{
private readonly IJWTFactory _jwtFactory;
public RabbitMqHandler(IJWTFactory jWTFactory)
{
_jwtFactory = jWTFactory;
}
public void Register()
{
var mqFactory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = mqFactory.CreateConnection())
{
Console.WriteLine("Listening on Rabbit MQ");
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "Authorize", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var jwtToken = Encoding.UTF8.GetString(body);
Console.WriteLine("Rceived Message");
var validatedToken = _jwtFactory.ValidateTokenSignature(jwtToken);
SendResponse(validatedToken);
};
channel.BasicConsume(queue: "Authorize", autoAck: true, consumer: consumer);
}
}
}
public void Deregister()
{
}
Startup.cs to register the
.AddSingleton()
Edit: I've added some additional listening code, this is definitely running on startup, but RabbitMQ is not showing the app as a consumer or a channel:
public static class ApplicationBuilderExtentions
{
public static RabbitMqHandler Listener { get; set; }
public static IApplicationBuilder UseRabbitListener(this IApplicationBuilder app)
{
Listener = app.ApplicationServices.GetService<RabbitMqHandler>();
var life = app.ApplicationServices.GetService<IApplicationLifetime>();
life.ApplicationStarted.Register(OnStarted);
//press Ctrl+C to reproduce if your app runs in Kestrel as a console app
life.ApplicationStopping.Register(OnStopping);
return app;
}
private static void OnStarted()
{
Listener.Register();
}
private static void OnStopping()
{
Listener.Deregister();
}
}
To summarise:
How do I correctly configure a consumer in .NET Core to consume messages?
Is this just the wrong to expect a Message Queue to manage request/response style communication?
Should I just be using an API call to authenticate and authorize users?
Answer as provided by #Evk (in the comments):
"using is designed to dispose things, it calls Dispose when you reach the end of the using block. BasicConsume is not a blocking call, so it starts consumption and returns immediately.
Right after that the end of the using blocks is reached for both channel and connection, disposing them (and disposing them is the same as closing)."
I'd like to add the following:
You can quickly try if removing the using brings the desired result very easily. Change the following code line:
using (var connection = mqFactory.CreateConnection())
to:
var connection = mqFactory.CreateConnection();
This will instantly do the trick. But be aware it also removes proper disposal - so you need to add that - here is an article from Microsoft describing how IDisposable is to be implemented correctly.

RabbitMQ C# Practical Example

I am new to RabbitMQ and am having trouble finding a VS2017 C# example that does more that prints to the Console. I can send and receive messages no problem, but I would like to take the contents of the message and actually use it. Below is the code I have:
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.IO;
namespace MyCode
{
class Program
{
public static void Main()
{
var factory = new ConnectionFactory() { HostName = "xxx.xx.x.x", UserName = "MyTest", Password = "MyTest", Port = 5672 };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "MyTest", durable: false, exclusive: false, autoDelete: false, arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
DoSomething(message);
};
channel.BasicConsume(queue: "MyTest", autoAck: true, consumer: consumer);
}
}
}
static void DoSomething(string message)
{
File.AppendAllText(#"C:\RMQ.txt", message);
}
}
}
The problem is, I can't ever seem to get anything out of the Consumer.Received step. Any help would be much appreciated!
EDIT::
I was able to make it work by using this code instead:
var factory = new ConnectionFactory() { HostName = "xxx.xx.x.x", UserName = "MyTest", Password = "MyTest", Port = 5672 };
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "MyTest", durable: false, exclusive: false, autoDelete: false, arguments: null);
BasicGetResult consumer = channel.BasicGet("MyTest", true);
if (consumer != null)
{
string message = Encoding.UTF8.GetString(consumer.Body);
DoSomething(message);
}
}
}
Anybody see any issues with this?
The problem with your first piece of code is that your program finishes execution before it can handle any messages. EventingBasedConsumer is asynchronous and won't actually prevent you program from exiting. You need to implement a wait of some sort to be able to actually handle messages. Try adding Thread.Sleep(10000); just after Channel.BasicConsume and check if there are any messages being processed.
For fire and forget applications, that tries to setup the read, try not to use the "using" statement for your connection and channel. They will go out of scope (set for garbage collection) once the the subroutine finish the execution.
Obviously, you need to have the console application up and running to receive the message. So, I did put a Console.Readline() in the calling app to prevent the thread from exiting.

Categories