ServiceStack.RabbitMq - how to set custom attributes on messages - rabbitmq

We use ServiceStack.RabbitMq and I could not find a way to put a custom attribute on the rabbit mq message. I want the publisher to set the attribute on the message and the worker to read it.
A variant is to move that attribute as part of request body but I have a lot of requests and in all honesty the request should not know at all about this kind of information - as that is metadata of the message.
Any idea how this can be achieved?

You can use the Message Filters in RabbitMqServer to add and introspect message properties, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};

You could either add the custom attribute to the object you are pushing down the queue or add that attribute to the rabbit message metadata header table. RabbitMQ messages have various metadata attributes that can be set when a message is published.
Check this

Related

problem with masstransit dynamic event publish ( json event )

we need publish multiple event as json string from DB. publish this json event by masstransit like this:
using var scope = _serviceScopeFactory.CreateScope();
var sendEndpointProvider = scope.ServiceProvider.GetService<ISendEndpointProvider>();
var endpoint = await sendEndpointProvider.GetSendEndpoint(new System.Uri("exchange:IntegrationEvents.DynamicEvent:DynamicEvent"))
var json = JsonConvert.SerializeObject(dynamicObject, Newtonsoft.Json.Formatting.None);// sample
var obj = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { });
await endpoint.Send(obj,i=>i.Serializer.ContentType.MediaType= "application/json");
and in config we use this config:
cfg.UseRawJsonSerializer();
when use this config, json event is successful published but we have strange problem : "all" event consumer is called by empty message data ! ... in Rabbitmq jsut published our "Dynamic Event", but in masstrasit all consumers called !!
Thank you for letting us know if we made a mistake
You don't need all of that JSON manipulation, just send the message object using the endpoint with the serializer configured for RawJson. I cover JSON interoperability in this video.
Also, MassTransit does not allow anonymous types to be published. You might be able to publish dynamic or Expando objects.
I used ExpandoObject like this and get this exception "Messages types must not be in the System namespace: System.Dynamic.ExpandoObject" :
dynamic dynamicObject = new ExpandoObject();
dynamicObject.Id = 1;
dynamicObject.Name = "NameForName";
await endpoint.Send(dynamicObject);
and using like this we get same result as "all consumers called":
var dynamicObject = new ExpandoObject() as IDictionary<string, object>;
dynamicObject.Add("Id", 1);
dynamicObject.Add("Name", "NameForName");
I watch your great video, you used from rabbitmq directly .. how "send the message object using the endpoint with the serializer configured for RawJson" in C# code.

Add metadata when configuring an endpoint to be delivered in the Consumer

So, when I'm configuring the endpoint and I set ep.Consumer<MyConsumer>(context) - am I able to add something to the Consumer that appears in the ConsumeContext on each invocation?
For example something like this:
MyMetaData metadata = new MyMetaData() { foo: "Bar" }
ep.Consumer<MyConsumer>(context, c => c.somehowincludemetadata(metadata));
And then in the Consumer:
public Task Consume(ConsumeContext<Message> context)
{
var metadata = (MyMetaData)context.heresyourmetadata();
}
Obviously that psudeo-code - but what I'm after is essentially a means by which I can add context (like a customer name, computer name) to the context for retrieve during processing.
It's completely on the consumer side, the publisher won't have any idea of the data that needs to be included.
Many thanks.
That metadata typically has to come from someplace, which can be injected as a dependency into the consumer. Another option, if it is based off data in the message, or headers, is to use middleware – such as a scoped filter that could add payload to the ConsumeContext. This option could either set properties on a shared context object scoped in the container, or as mentioned, add payload.
Yet another option is to somehow add an async method that is called before the consumer, such as shown below.
ep.ConfigureConsumer<MyConsumer>(context,
x => x.UseExecute(context => context.Consumer.Property = Value)));
Or, you can add payload as well:
ep.ConfigureConsumer<MyConsumer>(context,
x => x.UseExecute(context =>
context.GetOrAddPayload(() => new MyPayload())));

Is there a way to programmatically delete an ActiveMQ Job Schedule?

I'm trying to delete a scheduled job in ActiveMQ, and so far no luck.
Schedule is created among with the message using either NMS API or Amqpnetlite (except openwire lib as that one is not updated and can't be used on netstandard/netcore)
Sample code used to create the schedule with NMS, same thing done with AMQP lib:
var factory = new Apache.NMS.ActiveMQ.ConnectionFactory(brokerUri);
IConnection connection = factory.CreateConnection(user, password);
connection.Start();
ISession session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
IDestination dest = session.GetQueue(destination);
IMessageProducer producer = session.CreateProducer(dest);
producer.DeliveryMode = MsgDeliveryMode.NonPersistent;
var msg = session.CreateTextMessage("Sample text message");
msg.Properties.SetString("AMQ_SCHEDULED_CRON", "* * * * *");
producer.Send(msg);
connection.Close();
This part produces the following result in browser console, and that's what I am willing to delete:
I've read this other question and answer, also active mq system constants but no way the schedule gets deleted. Also tried to browse over the documentation but can't find anything useful so far
Does ActiveMQ even supports a programmative way of managing schedules? An AMQP solution would be great, but NMS is also appreciated.
You can manage scheduled jobs in ActiveMQ over STOMP, AMQP or simply from a JMS client. I've written about this before showing how to do it using the ActiveMQ Java client but the principle is the same. You can send messages with specific headers set that will operate on the scheduled messages.
To browse the collection of scheduled message you'd do something like the following:
Connection connection = createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the Browse Destination and the Reply To location
Destination requestBrowse = session.createTopic(ScheduledMessage.AMQ_SCHEDULER_MANAGEMENT_DESTINATION);
Destination browseDest = session.createTemporaryQueue();
// Create the "Browser"
MessageConsumer browser = session.createConsumer(browseDest);
connection.start();
// Send the browse request
MessageProducer producer = session.createProducer(requestBrowse);
Message request = session.createMessage();
request.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION,
ScheduledMessage.AMQ_SCHEDULER_ACTION_BROWSE);
request.setJMSReplyTo(browseDest);
producer.send(request);
Message scheduled = browser.receive(5000);
while (scheduled != null) {
// Do something clever...
}
The returned messages contain the information on the actual scheduled message jobs that have been previously added. Obtaining the job Id allows you to completely cancel the delivery of said message.
To remove a scheduled message send that was scheduled using the Java client, AMQP client or other procotol client you'd do the following:
Message remove = session.createMessage();
remove.setStringProperty(ScheduledMessage.AMQ_SCHEDULER_ACTION,
ScheduledMessage.AMQ_SCHEDULER_ACTION_REMOVE);
remove.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_ID,
scheduled.getStringProperty(ScheduledMessage.AMQ_SCHEDULED_ID));
producer.send(remove);
The full set of message property values that can be used when working with the scheduler are documented here, in AMQP just use the string literal of each as the Application Property value you set with the job Id to remote, or in the NMS client it'd just be a string key message property with the job Id that you want to remove.
There is one caveat though when doing this over AMQP and that would be that you'd need to ensure that the broker is using the JMS transformer ?transport.transformer=jms"see the AMQP documentation for ActiveMQ 5.
Accepted answer is working and correct. Turns out scheduledMessage.NMSMessageId was not holding the scheduler id.
Here's the clean C# source in case one is interested:
var factory = new Apache.NMS.ActiveMQ.ConnectionFactory(brokerUri);
IConnection connection = factory.CreateConnection(userName, password);
var session = connection.CreateSession(AcknowledgementMode.AutoAcknowledge);
var requestBrowse = session.GetTopic("ActiveMQ.Scheduler.Management");
var queue = session.GetQueue(queueName);
var consumer = session.CreateConsumer(queue);
connection.Start();
var producer = session.CreateProducer(requestBrowse);
var scheduledMessage = consumer.Receive(TimeSpan.FromSeconds(10));
if (scheduledMessage != null)
{
// do check with persistent storage, if schedule is canceled, remove it:
var remove = session.CreateMessage();
// get prop names from : http://activemq.apache.org/maven/apidocs/constant-values.htm
remove.Properties["AMQ_SCHEDULER_ACTION"] = "REMOVE";
remove.Properties["scheduledJobId"] = scheduledMessage.Properties.GetString("scheduledJobId");
producer.Send(remove);
}
producer.Close();
session.Close();
connection.Close();
Flow is like this: Get messages from some queue, in case some condition is met, drop the schedule entirely.

Message label in Rabbitmq

MSMQ messages have a Label property. It can contain an application-defined string describing the message. Does Rabbitmq have such a concept? Maybe it's called differently - haven't found anything similar yet.
I would use custom message headers. They are a lot more flexible than the MSMQ label. You can store string, number or boolean, or a list of those values.
Add custom headers to the IBasicProperties (C# example)
var properties = channel.CreateBasicProperties();
properties.Headers = new Dictionary<string, object>();
properties.Headers.Add("Label", "some text");
When you consume, extract them from the IBasicProperties.

Is there support for the RabbitMQ mandatory flag in MassTransit

I would like to get an exception when sending to a RabbitMQ exchange using MassTransit if the message is not delivered to any queues. It looks like the RabbitMQ mandatory flag could be used for something like this, but I have not been able to find any way to set this using MassTransit. Is this supported?
My code:
var personSendEndpoint = busControl.GetSendEndpoint(new Uri($"rabbitmq://localhost/{personQueueName}")).Result;
// An exception should occur on the following call if the message is not delivered to any queue
personSendEndpoint.Send(new PersonUpdated() { Name = "Dorian Gray", Key = Guid.NewGuid() });
You can set the Mandatory flag, but there isn't a cute extension methods to do it for you. Basically, you do as shown below:
await endpoint.Send(new PersonUpdate(...), context =>
{
(context as RabbitMqSendContext).Mandatory = true;
});
And that will set the flag telling RabbitMQ to throw an exception if no bindings for the exchange exist.