MassTransit not receiving Azure ServiceBus Topic Messages - c#

I'm monitoring the topic and subs and messages are getting in, however my masstransit consumer is not receiving anything.
Here's how it's been setup:
var bus = Bus.Factory.CreateUsingAzureServiceBus(
cfg =>
{
var azSbHost = cfg.Host(new Uri(CloudConfigurationManager.GetSetting("ServiceBus.Url"))
, host =>
{
host.TokenProvider = TokenProvider
.CreateSharedAccessSignatureTokenProvider
(CloudConfigurationManager.GetSetting("ServiceBus.SharedAccessKeyName"),
CloudConfigurationManager.GetSetting("ServiceBus.AccessKey"),
TokenScope.Namespace);
});
cfg.ReceiveEndpoint(azSbHost,
e =>
{
e.Consumer<PingConsumer>();
});
//azSbHost.
});
The Ping Consumer:
public class PingConsumer : IConsumer<Ping>
{
public async Task Consume(ConsumeContext<Ping> pingContext)
{
pingContext.Respond(new Pong
{
Message = "Pong: " + pingContext.Message.Message
});
}
}
And the sender:
var pong = await _bus.CreatePublishRequestClient<Ping, Pong>(TimeSpan.FromSeconds(10),null ).Request(
new Ping {Message = "Ping: " + message});
In Azure, I'm seeing my message count climbing up and not going down. So messages are getting to the queue, but consumer is not consuming the message.

I was missing a VERY important key call to make it all work on both client and server side.
Bus.Start

Related

MQTTnet disconnects from Azure IoT Hub on message publish

I am trying to subscribe MQTT messages in Azure IoT Hub. I am able to publish message using MQTTNet library but when I create subscriber to receive message from IoT hub then connection gets disconnected once message is published to the IoT hub.
I have tried to publish message from separate application and VS code extension, in both cases subscriber gets disconnected on message published.
I am using following code for subscriber
Console.WriteLine("Starting Subscriber.....");
//create subscriber client
var mqttFactory = new MqttFactory();
var mqttClient = mqttFactory.CreateMqttClient();
var mqttClientOptions = new MqttClientOptionsBuilder()
.WithClientId("<Device-Id>")
.WithTcpServer("<IoTHub>.azure-devices.net", 8883)
.WithCredentials("<IoTHub>.azure-devices.net/<Device-Id>/api-version=2018-06-30", "SharedAccessSignature")
.WithTls(new MqttClientOptionsBuilderTlsParameters() { UseTls = true })
.WithCleanSession()
.Build();
mqttClient.ConnectedAsync += async (MqttClientConnectedEventArgs arg) =>
{
Console.WriteLine("Connected");
};
mqttClient.DisconnectedAsync += async (MqttClientDisconnectedEventArgs arg) =>
{
Console.WriteLine("Disconnected");
};
mqttClient.ApplicationMessageReceivedAsync += async (MqttApplicationMessageReceivedEventArgs arg) =>
{
Console.WriteLine("Message received");
};
var result = mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None).GetAwaiter().GetResult();
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
.WithTopicFilter(
f =>
{
f.WithTopic("devices/<Device-Id>/messages/events/");
})
.Build();
var r = mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None).GetAwaiter().GetResult();
Console.WriteLine("MQTT client subscribed to topic.");
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
When I run this code and publish message then I get following output
Instead of receiver event, mqtt disconnect event fires. I am using 4.1.4.563 version of MQTTnet library. Any help would be gratefully appreciated, thanks!
You are subscribing to a custom topic. See here similar question.
Device can subscribe to devices/{device_id}/messages/devicebound/#

Communicating Between Blazor app and Microservice api using azure service bus

I am creating a microservice where one app is sending selected filters to other app using azure service bus queue.
I am able to send and receive the message however unable to use received message in my SQL Query.
The API is getting hit by one application (frontend).
.../api/User
Our controller
public class UserController : ControllerBase
{
public IEnumerable<dynamic> Get()
{
return userRepository.GetAll();
}
}
GetAll method
public IEnumerable<dynamic> GetAll()
{
ReceiveMsg().GetAwaiter().GetResult(); // We have called receiveMsg from here
startdate = content1[0];
enddate = content1[1];
using (IDbConnection dbConnection = connection)
{
var result = connection.Query("select * from [User] where DateofBirth between '" + startdate + "' and'" + enddate + "'");
return result;
}
}
Receive Message method`
public static async Task ReceiveMsg()
{
//
string sbConnectionString = <connection string for Service Bus namespace>;
string sbQueueName = <Queue name>;
try
{
queueClient = new QueueClient(sbConnectionString, sbQueueName);
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
queueClient.RegisterMessageHandler(ReceiveMessagesAsync, messageHandlerOptions);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.ReadKey();
await queueClient.CloseAsync();
}
}
public static async Task ReceiveMessagesAsync(Message message, CancellationToken token)
{
Debug.WriteLine($"Received message: {Encoding.UTF8.GetString(message.Body)}");
var receivedmsg = Encoding.UTF8.GetString(message.Body);
ServiceBusMessage DeserializeMsg = JsonConvert.DeserializeObject<ServiceBusMessage>(receivedmsg);
content1 = DeserializeMsg.Content;
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine(exceptionReceivedEventArgs.Exception);
return Task.CompletedTask;
}`
You're mixing two paradigms here - a message pump and receive messages on demand. With a message pump, when using .ReceiveMessageHandler() you're requesting the Service Bus SDK to run a continuous loop, aka "message pump", to receive messages as they arrive. That doesn't align with an explicit message retrieval when making a request to the Web API (via controller). You need to redesign your application and retrieve the message(s) upon demand rather than running a message pump.

Microservice does not receive RabbitMq message

When I add a new user to my IdentiyServerService the following MassTransit code is called:
var newUserCreated = new UserCreated
{
UserId = userId.ToString(),
Name = user.Name
};
await _bus.Publish(newUserCreated);
My destination is that my ProfileService receive this event by RabbitMq.
My RabbitMq configuration in my Startup.cs (IdentiyServerService)
private static void ConfigureBus(ContainerBuilder builder)
{
builder.Register(context =>
{
return Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost"), h =>
{
h.Username("guest");
h.Password("guest");
});
});
}).As<IBus, IBusControl, IPublishEndpoint>().SingleInstance();
}
I start the bus like this
//Startup.cs IdentityServerService
var container = containerBuilder.Build();
var busControl = container.Resolve<IBusControl>();
busControl.Start();
The configuration in my ProfileService look quite almost the same. The difference is, that I add an consumer in my Startup.cs (ProfileService)
config.ReceiveEndpoint(host, "user_queue", ep =>
{
ep.Consumer<UserCreatedConsumer>(); // The consumer is registered explicitly this time.
});
I add an IConsumer as well
public class UserCreatedConsumer : IConsumer<UserCreated>
{
public Task Consume(ConsumeContext<UserCreated> context)
{
var user = context.Message;
Debug.WriteLine("My debug string here");
return TaskUtil.Completed;
}
}
When I create a new user the messages receive the RabbitMq (publish rates increase). But then nothing goes on. The total number of messages in the Queued messages does not change.
I have two connections (I would expect IdentityServerService and ProfileService) and I have different queues (I was only expecting one: user_queue)
When I implement the IConsumer inside my IdentityServerService I receive the message.
I have no error log, warnings or something else.
Anyway... 1) Why does ProfileService not receive the message? 2) And why do I have so much queues?
If you need some more information... please tell
Edit
When I send a message in the rabbitMq-management my ProfileService receive a message, but now I get the following error
MassTransit.Messages Error: 0 : R-FAULT
rabbitmq://localhost/user_queue_new Value cannot be null.
Parameter name: source,
System.Runtime.Serialization.SerializationException: An exception occurred while deserializing the message envelope ---> System.ArgumentNullException: Value cannot be null.
Parameter name: source

How to retrieve messages from RabbitMQ DeadLetter queue using MassTransit?

I'm trying to notify the user when a message is not received after some time, using MassTransit and RabbitMQ.
From what I read, the timeout is set using the TimeToLive property when the message is published. When that specified time runs out, the message should be automatically added to a Dead Letter queue, named with a "_skipped" at the end.
How do I retrieve messages from Dead Letter queues? In my attempt below, the message is added to the both queues right away, and it never times out.
I think I could do this using sagas, but it seems like a over complicated solution for such a simple problem, so I would like to avoid using it if at all possible.
static void Main(string[] args)
{
var bus = CreateBus("rabbitmq://localhost/", "guest", "guest", true);
var msg = new TestMessage("First Message");
LogMessageSent(msg);
bus.Publish(msg, c => c.TimeToLive = TimeSpan.FromSeconds(15));
Console.ReadKey();
bus.Stop();
bus = CreateBus("rabbitmq://localhost/", "guest", "guest", false);
msg = new TestMessage("SecondMessage");
LogMessageSent(msg);
bus.Publish(msg, c => c.TimeToLive = TimeSpan.FromSeconds(15));
Console.ReadKey();
bus.Stop();
}
private static IBusControl CreateBus(string rabbitUrl, string username, string password, bool enableEndpoint)
{
var bus = Bus.Factory.CreateUsingRabbitMq(c =>
{
var host = c.Host(new Uri(rabbitUrl), h =>
{
h.Username(username);
h.Password(password);
});
if (enableEndpoint)
{
c.ReceiveEndpoint(host, "TestQueue", x =>
{
x.Handler<TestMessage>(e => LogMessageReceived(e.Message, "TestQueue"));
});
}
c.ReceiveEndpoint(host, "TestQueue_skipped", x =>
{
x.Handler<TestMessage>(e => LogMessageReceived(e.Message, "TestQueue_skipped"));
});
});
bus.Start();
return bus;
}
private static void LogMessageSent(TestMessage msg)
{
Console.WriteLine(string.Format("{0} - Message \"{1}\" sent.", DateTime.Now.ToString("HH:mm:ss"), msg.Content));
}
private static Task LogMessageReceived(TestMessage msg, string queueName)
{
Console.WriteLine(string.Format("{0} - Message \"{1}\" received on queue \"{2}\".", DateTime.Now.ToString("HH:mm:ss"), msg.Content, queueName));
return Task.CompletedTask;
}
public class TestMessage
{
public string Content { get; }
public TestMessage(string content)
{
Content = content;
}
}
Because you are calling Publish, the message is sent to every subscriber. Since each receive endpoint is adding the consumer, that creates a subscription (and subsequent exchange binding in RabbitMQ) for that message type. You can disable this by specifying BindMessageExchanges = false on that skipped receive endpoint. You will need to manually remove the exchange binding on the broker.
As to your TimeToLive question, that isn't how it works. TimeToLive is passed to the broker, and if the message expires, it is moved to a broker-specified dead-letter queue, if so configured. It is not moved to the skipped queue which has a different meaning in MassTransit. In MassTransit, the skipped queue is for messages that are delivered to a receive endpoint but there wasn't a consumer configured on that endpoint to consume the message.
For RabbitMQ, you can configure the dead-letter queue in MassTransit by using:
endpoint.BindDeadLetterQueue("dead-letter-queue-name");
This will configure the broker so that messages which reach their TTL are moved to the specified exchange/queue. Then your consumer on that receive endpoint will be able to consume them (again, be sure to set BindMessageExchanges = false on the dead-letter receive endpoint.
For example:
c.ReceiveEndpoint(host, "TestQueue_expired", x =>
{
x.BindMessageExchanges = false;
x.Handler<TestMessage>(e => LogMessageReceived(e.Message, "TestQueue_expired"));
});
And then your original receive endpoint:
c.ReceiveEndpoint(host, "TestQueue", x =>
{
x.BindDeadLetterQueue("TestQueue_expired");
x.Handler<TestMessage>(e => LogMessageReceived(e.Message, "TestQueue"));
});

Wait for Mass Transit saga to finish

I'm trying to create a saga that returns some result to the caller, just like the Request/Response pattern. I'm able to start the saga if I call the Send method, but not by submitting a Request.
So, the saga logic runs fine, but it doesn't return anything to the client.
Or submitting a Request gets processed by it's consumer and returns a response to the client, but never starts the saga.
UPDATE: The answer to masstransit deferred respond in sagas doesn't seem to apply to my question for two reasons:
1) I wasn't able to start the saga by calling the Request method;
2) If I call the Send method to send the request and, later on, send the response, the caller thread does not wait for the response to get back before continuing to the next line of code;
[END OF UPDATE]
Please find the complete code here. And below are the more relevant fragments:
Here is the saga class:
public class MySaga : MassTransitStateMachine<MySagaState>
{
public static Uri address = new Uri($"loopback://localhost/req_resp_saga");
public Event<IStartSagaCommand> StartSaga { get; private set; }
public Request<MySagaState, MyRequest, MyResponse> SomeRequest { get; private set; }
public MySaga()
{
InstanceState(s => s.CurrentState);
Event(() => StartSaga,
cc =>
cc.CorrelateBy(state => state.Data, context => context.Message.Data)
.SelectId(context => Guid.NewGuid()));
Request(() => SomeRequest, x => x.NullableCorrelationId, cfg =>
{
cfg.ServiceAddress = address;
cfg.SchedulingServiceAddress = address;
cfg.Timeout = TimeSpan.FromSeconds(30);
});
Initially(
When(StartSaga)
.Then(context =>
{
context.Instance.Data = context.Data.Data;
})
.ThenAsync(
context => Console.Out.WriteLineAsync($"Saga started: " +
$" {context.Data.Data} received"))
.Request(SomeRequest, context => new MyRequest() { CorrelationId = context.Instance.CorrelationId, RequestMessage = "Please do this" })
.TransitionTo(SomeRequest.Pending)
.ThenAsync(context => Console.Out.WriteLineAsync($"Transition completed: " +
$" {(context.Instance.CurrentState == SomeRequest.Pending ? "pending" : "done")} received"))
//.Then(context =>
//{
// var endpoint = context.GetSendEndpoint(address).GetAwaiter().GetResult();
// endpoint.Send(new MyResponse() { CorrelationId = context.Instance.CorrelationId, ResponseMessage = "Your wish is my command" });
//})
);
During(SomeRequest.Pending,
When(SomeRequest.Completed)
.ThenAsync(
context => Console.Out.WriteLineAsync($"Saga ended: " +
$" {context.Data.ResponseMessage} received"))
.Finalize()
);
}
}
This starts the saga but doesn't wait for it to finish and respond:
var address = new Uri($"loopback://localhost/req_resp_saga");
var endPoint = bus.GetSendEndpoint(address)
.Result;
endPoint.Send<IStartSagaCommand>(new { Data = "Hello World!!" });
And this waits for a response but doesn't involve the saga at all:
var address = new Uri($"loopback://localhost/req_resp_saga");
var requestClient = new MessageRequestClient<MyRequest, MyResponse>(bus, address, TimeSpan.FromSeconds(30));
var response = requestClient.Request(new MyRequest() { CorrelationId = Guid.NewGuid(), RequestMessage = "Please do this" })
.GetAwaiter()
.GetResult();
How can I have the caller start the saga and wait for it to finish and do something with its reponse?
I was able to find the solution myself.
The problem was that I was confused about how to trigger the saga with a Request call. I thought I had to declare a
Request<in TInstance, TRequest, TResponse>
(Automatonimous)
That was not working for me.
And the Event I used to start the saga had it's own interface
Event<IStartSaga>
which was not the same I was using when calling the Request method
var requestClient = new MessageRequestClient<MyRequest, MyResponse>(bus, address, TimeSpan.FromSeconds(30));
var response = requestClient.Request(new MyRequest() { CorrelationId = Guid.NewGuid(), RequestMessage = "Please do this" })
.GetAwaiter()
.GetResult();
So the fix was to change the declaration of the event to
Event<MyRequest>
Now the saga starts whenever I call Request with a MyResquest message. And the caller waits for the response from the saga.
I made some other changes to clean up the code a little and pushed it to github too.

Categories