I'm using masstransit to consume messages from an azure service bus. It's going greate for nom but I need now to add filter rules to my subscription.
I found some posts on the subject, like this one:
Is there a way to define a Azure Service Bus rule/filter when setting up a consumer?
but without many success...
My subscription is created properly when configuring my consumers like this, but it has the $Default 1=1 filter.
cfg.SubscriptionEndpoint<MyMessage>(mySubscription, cfg =>
{
se.Consumer<MyConsumer>(x => x.UseConcurrentMessageLimit(1));
});
I would like to add a different filter, but when I do this, the creation of the subscription seems to fail silently
cfg.SubscriptionEndpoint<MyMessage>(mySubscription, cfg =>
{
cfg.Rule = new CreateRuleOptions
{
Name = "Receiver filter",
Filter = new SqlRuleFilter("receiver='all'")
};
se.Consumer<MyConsumer>(x => x.UseConcurrentMessageLimit(1));
});
I'm I missing something?
I found my mistake... Everything is fine except for one thing. The rule name does not support spaces.
cfg.Rule = new CreateRuleOptions
{
Name = "ReceiverFilter", // instead of "Receiver filter"
Filter = new SqlRuleFilter("receiver='all'")
};
Related
I started working on Microservices. So I have made two Restful APIs.
Organisations API ( GET, PUT, POST, DELETE )
Customers API ( GET, PUT, POST, DELETE )
These two are separate APIs and hosted on different ports on my local IIS.
Now I want to consume them in my main application.
So the requirement is to call them only by Network connection.
I found that I need to use Rpc, gRpc or Kafka.
so, I have decided to use Rpc by using RabbitMq and EasyNetQ.
By this, I have configured rabbiqMq in docker and it is running successfully.
What I am not understanding is that in my Organisations and Customers API there are multiple actions. GET, PUT, POST, DELETE
So, Where I need to define the queue name for those method, so I can consume it in my main app by calling with some name. and it will directly call that method.
e.g.
var factory = new ConnectionFactory() { HostName = "localhost" };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
var body = Encoding.UTF8.GetBytes(entity);
channel.BasicPublish(exchange: "organisations", routingKey: "organisations.add", basicProperties: null, body: body);
Where in the organisations api, I will define this organisations.add, organisations.update, organisations.search ?
Can I add them dynamically through some mediator ?
Or I need to add manually in the rabitmq ui .. to adding queue ?
I usually use IBus.Publish for publishing message it supports strong type and also it detects the Exchange and Queue name automatically.
Let's assume you have a Message class for adding organizations like this:
[Queue("Organisations.Add", ExchangeName = "Organisations.Add")]
public class Message
{
public string Text { get; set; }
public DateTime Date { get; set; }
}
And for publish a message to broker:
using (var bus = RabbitHutch.CreateBus("host=localhost"))
{
var message = new Message() { Text = "Hello World", Date = DateTime.Now };
bus.Publish<Message>(message);
}
So you can create multiple messages based on api and use them in Mediator.
Currently, I have to switch our messaging system to use AmazonSQS and due to pricing policy, we are obliged to put tags. But I do not found any method to add tags.
Below the method which won't work due to fact that this approach is expecting that the queues already exist and I can get URL of the queue:
public static EndpointConfiguration CreateEndpointConfiguration(BusConfig config)
{
var endpointConfiguration = new EndpointConfiguration(config.QueueName);
endpointConfiguration.LicensePath("license.xml");
endpointConfiguration.SendFailedMessagesTo($"{config.QueueName}.Errors");
endpointConfiguration.EnableInstallers();
endpointConfiguration.UseSerialization<NewtonsoftSerializer>();
endpointConfiguration.LimitMessageProcessingConcurrencyTo(10);
endpointConfiguration.Conventions()
.DefiningEventsAs(type => typeof(IMessage).IsAssignableFrom(type))
.DefiningCommandsAs(type => typeof(ICommand).IsAssignableFrom(type));
var transport = endpointConfiguration.UseTransport<SqsTransport>();
transport.ClientFactory(() =>
{
var amazonSQSConfig = new AmazonSQSConfig()
{
RegionEndpoint = RegionEndpoint.USWest2
};
var client = new AmazonSQSClient(amazonSQSConfig);
var addedTags = new Dictionary<string, string>();
addedTags.Add("Team", "Development");
addedTags.Add("Accounting ID", "number");
var tagQueueRequest = new TagQueueRequest()
{
Tags = addedTags
};
client.TagQueueAsync(tagQueueRequest);
return client;
});
transport.QueueNamePrefix("some-prefix");
endpointConfiguration.Recoverability()
.AddUnrecoverableException<CustomException>();
return endpointConfiguration;
}
Can you provide solution for adding automatically tags during configuring endpoints?
Thank you for any help
The NServiceBus integration with SQS doesn't seem to support configuration of tags at the moment. You'd have to manually create your queues upfront with the appropriate tags or manually add tags to existing queues.
You can raise feature requests for tag support on Particular Software's SQS transport repository here: https://github.com/Particular/NServiceBus.AmazonSQS
I am trying to configure the RequiresDuplicateDetection property on the ASB topics to true, but it doesn't appear that the setting on the main IServiceBusFactoryConfigurator is respected:
var busControl = Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
cfg.Host("ASB_ConnectionString");
cfg.SubscriptionEndpoint<ExtractionRequest>("Test", e =>
{
e.LockDuration = TimeSpan.FromMinutes(1);
e.MaxAutoRenewDuration = TimeSpan.FromMinutes(5);
e.AutoDeleteOnIdle = TimeSpan.FromHours(1);
});
cfg.RequiresDuplicateDetection = true;
});
Any topics that are created for this subscription on ASB don't seem to respect the setting. I found a (maybe hacky) way to actually work around by hooking into the TopicDescription object on the PublishTopology of my message type.
var smth = busControl.Topology.Publish<ExtractionRequest>() as ServiceBusMessagePublishTopology<ExtractionRequest>;
smth.TopicDescription.RequiresDuplicateDetection = true;
The topics that are created correctly after this workaround. If anyone can shed some light on this, that would be great.
You can configure the publish topology for the topic within the bus configurator:
cfg.Publish<ExtractionRequest>(x => x.RequiresDuplicateDetection = true);
You should configure the topology prior to configuring your subscription endpoint, order matters particularly in this case.
In your example, specifying cfg.RequiresDuplicateDetection = true; configures the bus receive endpoint only, not the subscription endpoint or any other configured receive endpoints.
I'm using Azure Service Bus as transport for MassTransit and I'm trying to send message(command) to queue:
var sendEndpoint = await busControl.GetSendEndpoint(sericeUri);
sendEndpoint.Send<ISimpleRequest>(new SimpleRequest(customerId));
Also I try to connect to this queue by two consumers:
var _busControl = Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
var host = cfg.Host("...", h =>
{
h.OperationTimeout = TimeSpan.FromMinutes(1);
});
cfg.ReceiveEndpoint(host, "queueName",
e => { e.Consumer<RequestConsumer>(); });
cfg.UseServiceBusMessageScheduler();
});
The same code with same queue name for second consumer.
After I send message only one consumer get the response. So how I can config this to work with two or more consumers?
If you want to get two copies of the message, one for each consumer, you should use two separate queues and Publish the message. In this case, MassTransit will send it to the topic, and each queue will receive a copy forwarded from the topic.
I'm trying to listen to the error queue to process failed messages but I can't seem to get MassTransit not to set bindings on message that i want it to listen to within the configuration. The configuration is below and is using v3 of MassTransit:
var hostAddress = new Uri("rabbitmq://localhost/");
var username = "guest";
var password = "guest";
_busControl = MassTransit.Bus.Factory.CreateUsingRabbitMq(configurator =>
{
var host = configurator.Host(hostAddress, h =>
{
h.Username(username);
h.Password(password);
});
configurator.ReceiveEndpoint(host, "myqueue_error",
endpointConfigurator =>
{
endpointConfigurator.Handler<SomeMessage>(context =>
{
return Console.Out.WriteLineAsync("Woop");
});
});
});
In the above example it will set bindings up for anything that publishes SomeMessage and direct them in to the myqueue_error which I only want messages going in to this queue which has been forward from the service that are failing. Is there anyway to consume messages from a queue but tell MassTransit not to get bindings up for them?
Update - Potential Solution
It seems that I don't need to setup a ReceiveEndpoint but I can just rename the controlbus to accept the message that I care about, This will then be able to process these messages without creating exchange bindings to the messages.
Below is the altered code, not sure if this is an ideal way but it works
var hostAddress = new Uri("rabbitmq://localhost/");
var username = "guest";
var password = "guest";
_busControl = MassTransit.Bus.Factory.CreateUsingRabbitMq(configurator =>
{
configurator.Host(hostAddress, h =>
{
h.Username(username);
h.Password(password);
});
// We need to make the queue look like the error queue
configurator.BusQueueName = $"{_queue}_error";
configurator.Durable = true;
configurator.AutoDelete = false;
configurator.SetQueueArgument("x-expires", null);
});
var connectHandle = _busControl.ConnectHandler<SomeMessage>(context => Console.Out.WriteLineAsync("Woop"));
_busHandle = _busControl.Start();
_busHandle.Ready.Wait();
// Wait
// Clean up
connectHandle.Disconnect();
_busHandle.Stop
From a lot of digging around I found a better solution which I totally missed from the documentation.
It seems that we can listen to messages by subscribing consumers to listen to Fault This works perfect for what I've been trying to achieve and we can also keep the error queues in tack.
http://docs.masstransit-project.com/en/mt3/usage/exceptions.html#handling-exceptions
So the final bit of configuration that i settle with is the following:
var hostAddress = new Uri("rabbitmq://localhost/");
var username = "guest";
var password = "guest";
_busControl = MassTransit.Bus.Factory.CreateUsingRabbitMq(configurator =>
{
var host = configurator.Host(hostAddress, h =>
{
h.Username(username);
h.Password(password);
});
configurator.ReceiveEndpoint(host, "error_listener",
endpointConfigurator =>
{
endpointConfigurator.Handler<Fault<SomeMessage>>(context =>
{
return Console.Out.WriteLineAsync("Woop");
});
});
});