RPC rabbitMQ sends only one message and receive one client - c#

I used the tutorial from rabbitMQ on RCP in C#
server program.cs
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
namespace RabbitMQServerTest
{
internal class Program
{
static void Main(string[] args)
{
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
{
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue:"RPC_TEST",true,false,false,null);
channel.BasicQos(0,1,false);
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(queue: "RPC_TEST",
autoAck: true, consumer: consumer);
Console.WriteLine(" [x] Awaiting RPC requests");
consumer.Received += (model, ea) =>
{
string response = null;
var body = ea.Body.ToArray();
var props = ea.BasicProperties;
var replyProps = channel.CreateBasicProperties();
replyProps.CorrelationId = props.CorrelationId;
try
{
var message = Encoding.UTF8.GetString(body);
int n = int.Parse(message);
Console.WriteLine($" [.] fib({message}) from {replyProps.CorrelationId }");
response = fib(n).ToString();
}
catch (Exception e)
{
Console.WriteLine(" [.] " + e.Message);
response = "";
}
finally
{
var responseBytes = Encoding.UTF8.GetBytes(response);
channel.BasicPublish(exchange: "", routingKey: props.ReplyTo,
basicProperties: replyProps, body: responseBytes);
channel.BasicAck(deliveryTag: ea.DeliveryTag,
multiple: false);
}
};
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
}
}
private static int fib(int n)
{
if (n == 0 || n == 1)
{
return n;
}
return fib(n - 1) + fib(n - 2);
}
}
}
client program.cs
internal class Program
{
private static RpcClient m_rpcClient;
static void Main(string[] args)
{
m_rpcClient = new RpcClient();
callFib();
m_rpcClient.Close();
}
static void callFib()
{
Console.WriteLine("Write number:");
var num = Console.ReadLine();
if (num != "q")
{
var response = m_rpcClient.Call("30").Result;
Console.WriteLine(" [.] Got '{0}'", response);
callFib();
}
}
}
client RpcClient.cs
public class RpcClient :IDisposable
{
private readonly IConnection connection;
private readonly IModel channel;
private readonly string replyQueueName;
private readonly EventingBasicConsumer consumer;
private ConcurrentDictionary<string, TaskCompletionSource<string>> m_ActiveQueue = new ConcurrentDictionary<string, TaskCompletionSource<string>>();
private readonly IBasicProperties props;
public RpcClient()
{
var factory = new ConnectionFactory() { HostName = "localhost" };
connection = factory.CreateConnection();
channel = connection.CreateModel();
replyQueueName = channel.QueueDeclare().QueueName;
consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var response = Encoding.UTF8.GetString(body);
if (m_ActiveQueue.TryRemove(ea.BasicProperties.CorrelationId, out
var taskCompletionSource))
{
taskCompletionSource.SetResult(response);
}
};
channel.BasicConsume(
consumer: consumer,
queue: replyQueueName,
autoAck: true);
}
public Task<string> Call(string message)
{
var props = channel.CreateBasicProperties();
var correlationId = Guid.NewGuid().ToString();
props.CorrelationId = correlationId;
props.ReplyTo = replyQueueName;
var taskCompletionSource = new TaskCompletionSource<string>();
var messageBytes = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(
exchange: "",
routingKey: "RPC_TEST",
basicProperties: props,
body: messageBytes);
m_ActiveQueue.TryAdd(correlationId, taskCompletionSource);
return taskCompletionSource.Task;
}
public void Close()
{
try
{
channel?.Close();
connection?.Close();
}
catch (Exception ex)
{
}
}
public void Dispose()
{
Close();
}
}
It's working only with single message from single client
if I'm trying to run few clients , only the first that sent the message get response
the seconds message sends the server doesn't even get to the consumer.Received event in server program.cs

Related

Getting error Argument 2: cannot convert from 'System.Threading.Tasks.ParallelLoopState' to 'System.Threading.CancellationToken' how to solve?

Following on:
How to download files using HttpClient with a ProgressBar?
The project is WinForms .NET 6
How to solve the erro :
Severity Code Description Project File Line Suppression State
Error CS1503 Argument 2: cannot convert from 'System.Threading.Tasks.ParallelLoopState' to 'System.Threading.CancellationToken' WinFormsApp1 D:\Csharp\WinFormsApp1\ResourceDownloader.cs 63 Active
On line number 63
var dataBytes = await client.Value.GetByteArrayAsync(site, token);
The full class code :
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace WinFormsApp1
{
public class ResourceDownloader
{
private static Lazy<HttpClient> client = new(() => {
HttpClientHandler handler = CreateHandler(autoRedirect: true);
var client = new HttpClient(handler, true) { Timeout = TimeSpan.FromSeconds(60) };
client.DefaultRequestHeaders.Add("User-Agent", #"Mozilla/5.0 (Windows NT 10; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0");
client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
client.DefaultRequestHeaders.ConnectionClose = true;
return client;
}, true);
private static HttpClientHandler CreateHandler(bool autoRedirect)
{
return new HttpClientHandler()
{
AllowAutoRedirect = autoRedirect,
CookieContainer = new CookieContainer(),
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};
}
public record Website(string Url, byte[]? Data, bool Completed = true, Exception? Ex = null);
public record ProgressReport(Website Site, int PercentageComplete);
private static object syncObj = new object();
private static ConcurrentBag<Website> processed = default!;
private static int progressCount = 0;
private static int totalCount = 0;
public static bool IsBusy { get; internal set; } = false;
public static async Task<List<Website>> Download(IProgress<ProgressReport> progress, IList<string> sites, CancellationTokenSource cts)
{
IsBusy = true;
processed = new ConcurrentBag<Website>();
progressCount = 0;
totalCount = sites.Count;
try
{
ParallelOptions options = new()
{
MaxDegreeOfParallelism = 8,
CancellationToken = cts.Token
};
await Parallel.ForEach(sites, options, async (site, token) => {
try
{
var dataBytes = await client.Value.GetByteArrayAsync(site, token);
ReportProgress(progress, dataBytes, site, null);
}
catch (Exception ex)
{
ReportProgress(progress, null, site, ex);
}
});
}
// To Debug / Log
catch (TaskCanceledException) { Debug.Print("The operation was canceled"); }
finally { IsBusy = false; }
return processed.ToList();
}
private static void ReportProgress(IProgress<ProgressReport> progress, byte[]? data, string site, Exception? ex)
{
lock (syncObj)
{
progressCount += 1;
var percentage = progressCount * 100 / totalCount;
Website website = new(site, data, ex is null, ex);
processed.Add(website);
progress.Report(new ProgressReport(website, percentage));
}
}
}
}
Parallel.ForEach is not Task-aware and does not handle asynchronous workloads (correctly, at least), and overloads accepting handler with 2 parameters (like ForEach<TSource>(IEnumerable<TSource>, Action<TSource,ParallelLoopState>)) use ParallelLoopState as second parameter of the handler.
You need to use Parallel.ForEachAsync (for example this overload, which accepts Func<TSource, CancellationToken, ValueTask>) to correctly handle async workloads.

.NETFramework 4.6.2 client to .NET Core 3.1 server GRPC client calls failure

I've created GRPC service host under .NET core 3.1 (using Grpc.AspNetCore v2.30 from https://github.com/grpc/grpc-dotnet) and GRPC client under .NET framework 4.6.2 (using Grpc v2.30 from https://github.com/grpc/grpc ). These frameworks for the hosts are a constraint.
I'm running many calls to stress test the service from one client - one time calling Update the other calling UpdateStream. For both I'm facing a strange problem in the client side. It sometimes generates an error - can happen immediately when I start the execution or in the middle of it, and it never recovers - I must stop the client host and restart it again to make it work. it only happens when using different machines - on local host calls there are no issues.
any thoughts/ideas about this issue?
This is the exception that I'm getting for the Update/UpdateStream client calls:
Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"#1595
930477.263000000","description":"Error received from peer ipv4:[ip]:23456","file":"T:\src\github\grpc\workspace_csharp_ext_windows_x86\src\core
\lib\surface\call.cc","file_line":1055,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")
This is the client/server code:
Server:
class Program
{
const int _port = 23456;
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
Console.WriteLine("started - press any key to quit...");
Console.ReadKey();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.ConfigureEndpointDefaults(o =>
{
o.Protocols = HttpProtocols.Http2;
});
options.ListenAnyIP(_port);
});
webBuilder.UseStartup<Startup>();
});
}
public class ProxyService : StreamingApi.Protos.StreamingApi.StreamingApiBase
{
private long _handledRequests = 0;
private long _timeDiff = 0;
public override Task<UpdateResponse> Update(UpdateRequest request, ServerCallContext context)
{
Interlocked.Add(ref _timeDiff, (DateTime.Now - TimeSpan.FromTicks(Convert.ToInt64(request.Items["time"]))).Millisecond);
Interlocked.Increment(ref _handledRequests);
return Task.FromResult(new UpdateResponse());
}
public override async Task<UpdateResponse> UpdateStream(IAsyncStreamReader<UpdateRequest> requestStream, ServerCallContext serverCallContext)
{
try
{
while (await requestStream.MoveNext(serverCallContext.CancellationToken))
{
var updateReq = requestStream.Current;
Interlocked.Add(ref _timeDiff, (DateTime.Now - TimeSpan.FromTicks(Convert.ToInt64(updateReq.Items["time"]))).Millisecond);
Interlocked.Increment(ref _handledRequests);
}
}
catch(OperationCanceledException ex)
{
// log
}
return new UpdateResponse();
}
}
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<ProxyService>();
});
}
}
Client:
partial class Program
{
static void Main(string[] args)
{
Console.WriteLine();
Arguments arguments = new Arguments(args);
if (arguments.Initialized)
{
IProxyClient grpcClient = GetGrpcClient(arguments.Host, arguments.Port, ChannelCredentials.Insecure);
string limitationMsg = arguments.UseLimiter ?
$"limitation of max {arguments.MaxRequestsPerTimeUnit} requests per {arguments.TimeUnitSecs} seconds":
"no limitation";
Console.WriteLine($"\nExecuting {arguments.TotalRequests} requests with StreamMode={arguments.IsStreamMode} using {arguments.Threads} threads with {limitationMsg} ...");
var result = Run(grpcClient, arguments.Threads, arguments.TotalRequests, arguments.MaxRequestsPerTimeUnit, TimeSpan.FromSeconds(arguments.TimeUnitSecs), arguments.UseLimiter, arguments.IsStreamMode).Result;
Console.WriteLine($"Time Taken = {result.Item1}, Total Request Calls = {result.Item2}, Total Errors: {result.Item3}\n");
grpcClient.Disconnect().Wait();
Thread.Sleep(1000);
}
}
private static IProxyClient GetGrpcClient(string host, int port, ChannelCredentials channelCredentials)
{
var channel = new Channel(host, port, channelCredentials);
StreamingApi.Protos.StreamingApi.StreamingApiClient channelClient = new StreamingApi.Protos.StreamingApi.StreamingApiClient(channel);
return new ProxyClient(channel, channelClient);
}
private static async Task<(TimeSpan, int, int)> Run(IProxyClient grpcClient,
int threads,
int requests,
int maxRequestsPerTimeUnit,
TimeSpan timeUnit,
bool useLimiter,
bool isStreamMode)
{
int totalRequestCalls = 0;
int totalErrors = 0;
List<Task> tasks = new List<Task>();
int requestsPerThread = requests / threads;
TimeLimiter timeLimiter = useLimiter ? TimeLimiter.GetFromMaxCountByInterval(maxRequestsPerTimeUnit, timeUnit) : null;
UpdateRequest request = GetMeasuredRequest();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < threads; i++)
{
tasks.Add(Task.Run(async () =>
{
for (int requestIndex = 0; requestIndex < requestsPerThread; requestIndex++)
{
request.Items["time"] = DateTime.Now.Ticks.ToString();
if (useLimiter)
{
await timeLimiter;
}
try
{
if (isStreamMode)
{
await grpcClient.SendUpdateStream(request);
}
else
{
_ = await grpcClient.SendUpdate(request);
}
Interlocked.Increment(ref totalRequestCalls);
}
catch (Exception ex)
{
Interlocked.Increment(ref totalErrors);
Console.WriteLine(ex.Message);
Thread.Sleep(500);
}
}
}));
}
await Task.WhenAll(tasks);
sw.Stop();
return (sw.Elapsed, totalRequestCalls, totalErrors);
}
private static UpdateRequest GetMeasuredRequest()
{
UpdateRequest request = new UpdateRequest { ItemName = "pattern", SubcriptionId = "subscriptionId", IsSnapshot = false};
request.Items["key1"] = "value1";
request.Items["key2"] = "value2";
request.Items["key3"] = "value3";
request.Items["key4"] = "value4";
request.Items["key5"] = "value5";
return request;
}
}
public class ProxyClient : IProxyClient
{
private Channel _channel;
private StreamingApi.Protos.StreamingApi.StreamingApiClient _client;
private AsyncClientStreamingCall<UpdateRequest, UpdateResponse> _updateRequestStreamWriter;
public ProxyClient(Channel channel, StreamingApi.Protos.StreamingApi.StreamingApiClient client)
{
_client = client;
_updateRequestStreamWriter = client.UpdateStream();
}
public async Task Disconnect()
{
await _channel.ShutdownAsync();
}
public async Task<UpdateResponse> SendUpdate(UpdateRequest request)
{
return await _client.UpdateAsync(request);
}
public async Task SendUpdateStream(UpdateRequest request)
{
await _updateRequestStreamWriter.RequestStream.WriteAsync(request);
}
}

C# How a winform capture server sent event

How to subscribe a desktop client to server sent event. so whenever something will be pushed from server side my winform application should be able to capture and show that message. just a looking for a example code. i got some relevant links.
Here i am highlighting a sample code where a javascript side and ASP.Net MVC talking to each other by server sent event.
<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />
<script>
var es = new EventSource('/home/message');
es.onmessage = function (e) {
console.log(e.data);
};
es.onerror = function () {
console.log(arguments);
};
$(function () {
$('#ping').on('click', function () {
$.post('/home/ping', {
UserID: $('#userid').val() || 0
});
});
});
</script>
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace EventSourceTest2.Controllers {
public class PingData {
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
public void Ping(int userID) {
pings.Enqueue(new PingData { UserID = userID });
}
public void Message() {
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
}
Response.Flush();
Thread.Sleep(1000);
} while (true);
}
}
}
i want to communicate between a web api and winform by server sent event. so please show me how a winform can communicate subscribe to server sent event ?
if any small example would be added....will be great help. thanks
I wrote a basic implementation using the TcpClient.
Variables:
Host: 127.0.0.1
Port: 5000
Method: GET
Url: /home/message
Listener
private static async Task ConnectEventStreamAsync(CancellationToken token)
{
var client = new TcpClient();
try
{
await client.ConnectAsync("127.0.0.1", 5000);
if (!client.Connected)
throw new Exception("Unable to connect the host");
var encoding = Encoding.UTF8;
var stream = client.GetStream();
var connectBytes = encoding.GetBytes(
"GET /home/message HTTP/1.1\r\n" +
"Host: 127.0.0.1\r\n" +
"Content-Length: 0\r\n\r\n"
);
await stream.WriteAsync(connectBytes, 0, connectBytes.Length, token);
var buffer = new byte[4096];
while (!token.IsCancellationRequested)
{
var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (readCount > 0)
{
var message = encoding.GetString(buffer, 0, readCount);
using (var stringReader = new StringReader(message))
{
string line;
while ((line = await stringReader.ReadLineAsync()) != null)
{
//--try to read the next chunk
if (!int.TryParse(line, NumberStyles.HexNumber, null, out var bytes))
continue;
var data = await stringReader.ReadLineAsync();
Console.WriteLine($">>New Event ({bytes} bytes)\r\n{data}");
}
}
}
await Task.Delay(500, token);
}
}
finally
{
client.Dispose();
}
}
EDIT: Here is other solution using the HttpClient. I added more code to parse the text-eventstream messages. You can read more here to make improvements.
private static async Task ConnectEventStreamAsync(CancellationToken token)
{
var client = new HttpClient
{
Timeout = Timeout.InfiniteTimeSpan
};
try
{
var response = await client.GetAsync(
"http://127.0.0.1:5000/home/message",
HttpCompletionOption.ResponseHeadersRead,
token
);
if (!response.IsSuccessStatusCode)
throw new Exception("Unable to connect the stream");
var isTextEventStream = response.Content.Headers.ContentType.MediaType == "text/event-stream";
if (!isTextEventStream)
throw new InvalidOperationException("Invalid resource content type");
var stream = await response.Content.ReadAsStreamAsync();
var buffer = new byte[4096];
while (!token.IsCancellationRequested)
{
var readCount = await stream.ReadAsync(buffer, 0, buffer.Length, token);
if (readCount > 0)
{
var data = Encoding.UTF8.GetString(buffer, 0, readCount);
await ParseDataAsync(data);
}
await Task.Delay(500, token);
}
}
finally
{
client.Dispose();
}
async Task ParseDataAsync(string data)
{
using (var stringReader = new StringReader(data))
{
string line;
while ((line = await stringReader.ReadLineAsync()) != null)
{
if (line.StartsWith("event:"))
{
var eventText = line.Substring("event:".Length);
Console.WriteLine($">>Event ({eventText.Length} bytes)\r\n{eventText}");
continue;
}
if (line.StartsWith("data:"))
{
var dataText = line.Substring("data:".Length);
Console.WriteLine($">>Data ({dataText.Length} bytes)\r\n{dataText}");
continue;
}
if (line.StartsWith("id:"))
{
var eventId = line.Substring("id:".Length);
Console.WriteLine($">>Event ID ({eventId.Length} bytes)\r\n{eventId}");
continue;
}
if (line.StartsWith("retry:"))
{
var retryValue = line.Substring("retry:".Length);
Console.WriteLine($">>Retry ({retryValue.Length} bytes)\r\n{retryValue}");
continue;
}
if (line.StartsWith(":"))
{
Console.WriteLine($">>Comment ({line.Length - 1} bytes)\r\n{line.Substring(1)}");
}
}
}
}
}
The server part using Asp.Net Core (very similar to the one displayed by the user who asks).
...
private static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
[HttpGet]
public void Ping(int userID)
{
pings.Enqueue(new PingData { UserID = userID });
}
[HttpGet]
public void Message()
{
Response.ContentType = "text/event-stream";
Response.WriteAsync($":Hello {Request.Host}\n");
const int intervalMs = 1000;
do
{
if (pings.TryDequeue(out var nextPing))
{
Response.WriteAsync($"event:Ping\n");
Response.WriteAsync($"retry:{intervalMs}\n");
Response.WriteAsync($"id:{DateTime.Now.Ticks}\n");
Response.WriteAsync($"data:{JsonConvert.SerializeObject(nextPing)}\n\n");
}
Thread.Sleep(intervalMs);
} while (Response.Body.CanWrite);
}
public class PingData
{
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
...

SemaphoreSlim to protect the connection pool from exhaustion

I have a microservice (Web API) within an eventdriven architecture receiving messages from RabbitMQ and it is supposed to save them into a PostgreSQL DB using ADO.NET.
Unfortunately, my connection pool (currently set to 50) gets exhausted quite fast, giving me this error message:
The connection pool has been exhausted, either raise MaxPoolSize
My RabbitMQ Consumer is set up like this (Singleton):
public class Listener : RabbitMqConnection
{
public AsyncEventingBasicConsumer _asyncConsumer;
private static readonly SemaphoreSlim AsyncLock = new SemaphoreSlim(1, 1);
public Listener()
{
_asyncConsumer = new AsyncEventingBasicConsumer(_channel);
_asyncConsumer.Received += ConsumerReceived;
}
public async Task ConsumerReceived(object sender, BasicDeliverEventArgs message)
{
await AsyncLock.WaitAsync();
try
{
//Performing logic and saving into database
//....
using (var ctx = ContextFactory.GetContext<PostgreSqlDatabaseContext>(_connectionString))
{
//Creating query with StringBuilder...
await ctx.Database.ExecuteSqlCommandAsync(query.ToString(), parameters);
}
_channel.BasicAck(message.DeliveryTag, false);
}
catch (DecoderFallbackException decoderFallbackException)
{
_logger.LogError($"...");
_channel.BasicNack(message.DeliveryTag, false, false);
}
finally {
AsyncLock.Release();
}
}
}
ContextFactory
internal class ContextFactory
{
public static T GetContext<T>(string sqlConnection) where T : DbContext
{
var optionsBuilder = new DbContextOptionsBuilder<PostgreSqlDatabaseContext>();
optionsBuilder.UseNpgsql(sqlConnection);
return new PostgreSqlDatabaseContext(optionsBuilder.Options) as T;
}
}
RabbitMqConnection:
public abstract class RabbitMQConnection
{
public IModel _channel;
public IBasicProperties _properties;
public AsyncEventingBasicConsumer _asyncConsumer;
public ConnectionFactory _factory;
public ConnectConfiguration _connectConfiguration;
bool isConnected = false;
public void Connect(ConnectConfiguration connectConfiguration)
{
if (!isConnected)
{
_connectConfiguration = connectConfiguration;
CreateFactory(_connectConfiguration);
SetupConfiguration(_connectConfiguration.Exchange);
}
}
private void CreateFactory(ConnectConfiguration config)
{
_factory = new ConnectionFactory
{
AutomaticRecoveryEnabled = true,
DispatchConsumersAsync = true,
UseBackgroundThreadsForIO = true,
RequestedHeartbeat = 15,
HostName = config.Server,
UserName = config.UserName,
Password = config.Password
};
if (!string.IsNullOrWhiteSpace(config.Vhost))
_factory.VirtualHost = config.Vhost;
}
private void SetupConfiguration(string exchange)
{
var connection = _factory.CreateConnection();
_channel = connection.CreateModel();
_properties = _channel.CreateBasicProperties();
_properties.Persistent = true;
_channel.BasicQos(0, 10, false);
_channel.ExchangeDeclare(exchange, "topic", true);
isConnected = true;
}
}
I can´t not understand why I keep getting this error. Isn´t the SemaphoreSlim with WaitAsync() and Release() suppose to prevent the ConsumerReceived method from running the logic?

Received doesn't get triggered

I have shared library as Bus, and I am trying to recieve messages from rabbitmq but ConsumerOnReceived never get triggered.
namespace Bus
{
public class MessageListener
{
private static IConnection _connection;
private static IModel _channel;
public void Start(string hostName, int port, string queueName)
{
var factory = new ConnectionFactory() { HostName = hostName, Port = port };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += ConsumerOnReceived;
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer);
}
}
public static void Stop()
{
_channel.Close(200, "Goodbye");
_connection.Close();
}
public virtual void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea)
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
}
}
public static class MessageSender
{
public static void Send(string hostName, int port, string queueName, string message)
{
var factory = new ConnectionFactory() { HostName = hostName, Port = port };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false, arguments: null);
var body = Encoding.UTF8.GetBytes(message.ToString());
channel.BasicPublish(exchange: "", routingKey: queueName, basicProperties: null, body: body);
}
}
}
}
Core
namespace Core
{
class Program
{
static void Main(string[] args)
{
new MessageListener().Start("localhost", 5672, "MakePayment");
Console.WriteLine("Core Service");
string line = Console.ReadLine();
}
}
}
namespace Core
{
public class MessageListener : Bus.MessageListener
{
public override void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea)
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
}
}
}
The problem is here
channel.BasicConsume(queue: queueName, noAck: true, consumer: consumer);
However, BasicConsume is not a blocking method therefore when you call Start you create a connection and a channel but then it gets disposed immediately.
The following is NOT a solution but you can confirm by doing the following:
channel.BasicConsume(queue: queueName, noAck: true, consumer: consumer);
Console.ReadKey();//←Added Line
Your program will work this way.
This is my proposed solution. Please take notice that _channel.BasicConsume(queue: queueName, noAck: true, consumer: consumer); will start on another thread so you do not need to use while(...)
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace Bus {
public abstract class BaseMessageListener {
private static IModel _channel;
private static IConnection _connection;
public abstract void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea);
public void Start(string hostName, int port, string queueName) {
var factory = new ConnectionFactory() { HostName = hostName, Port = port };
_connection = factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.QueueDeclare(queue: queueName, durable: false, exclusive: false, autoDelete: false);
var consumer = new EventingBasicConsumer(_channel);
consumer.Received += ConsumerOnReceived;
_channel.BasicConsume(queue: queueName, noAck: true, consumer: consumer);//This will start another thread!
}
public void Stop() {
_channel.Close(200, "Goodbye");
_connection.Close();
}
}
}
namespace StackOverfFLow.RabbitMQSolution {
using Bus;
public class MessageListener : BaseMessageListener {
public override void ConsumerOnReceived(object sender, BasicDeliverEventArgs ea) {
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(message);
}
}
internal class Program {
private static void Main(string[] args) {
var listener = new MessageListener();
listener.Start("localhost", 5672, "MakePayment");
Console.WriteLine("Core Service Started!");
Console.ReadKey();
listener.Stop();
}
}
}

Categories

Resources