I am currently implementing a Java messaging system with Apache Camel and ActiveMQ. My goal is to dynamically set the priority of a message based on a few attributes the message has.
I already configured my ActiveMQ as explained here. Then I created the following method that sends a TextMessage:
public void send(BaseMessage baseMessage, int jmsPriority) throws JsonProcessingException {
Map<String, Object> messageHeaders = new HashMap<>();
messageHeaders.put(MESSAGING_HEADER_JMS_PRIORITY, jmsPriority);
messageHeaders.put(MESSAGING_HEADER_TYPE, baseMessage.getClass().getSimpleName());
String payload = objectMapper.writeValueAsString(baseMessage);
producerTemplate.sendBodyAndHeaders(payload, messageHeaders);
}
Sending the message perfectly works, and the dynamic type of BaseMessage is properly set to the header of each message. The priority is set as well, but is ignored. The order for the outcoming messages is still FIFO, as queues usually do.
Until now I did not achieve to set the priority of the message dynamically. I do not want to use Apache Camel's Resequencer since I would have to create several new queues only for "sorting". From my point of view ActiveMQ must be able to prioritize and reorder the messages itself.
Any tip is appreciated. Ask me for further details if required.
By default, ActiveMQ disables message priority. This is normal. When doing distributed messaging-- sending messages across servers, prioritization does not practically work out, since the broker can only scan so many messages in the queue for messages of a higher priority before it stats to slow down all traffic for that queue.
Prioritized messages can work well when embedding a broker and using it for task dispatch-- where queue depth generally doesn't exceed the low-thousands.
Updated:
Reminder-- the QOS settings in JMS must be set on the MessageProducer object, and not the message per JMS-spec.
Enable Prioritized Messages
Related
I'm using simple test project with Spring's JmsTemplate that sends synchronous messages with:
jmsTemplate.sendAndReceive(...)
Code snippet of JmsTemplate to do this:
Message requestMessage = messageCreator.createMessage(session);
responseQueue = session.createTemporaryQueue();
producer = session.createProducer(destination);
consumer = session.createConsumer(responseQueue);
requestMessage.setJMSReplyTo(responseQueue);
if (logger.isDebugEnabled()) {
logger.debug("Sending created message: " + requestMessage);
}
doSend(producer, requestMessage);
return receiveFromConsumer(consumer, getReceiveTimeout());
All work fine but when I'm going to Jolokia console I can see all my temp queues at address level:
In standard ActiveMQ console temporary queues are not shown (deleted?).
Because of my application use many synchronous message, list can grow up rapidly.
I try to use
<temporary-queue-namespace>temp</temporary-queue-namespace>
with
<address-setting match="temp.#">
<enable-metrics>false</enable-metrics>
</address-setting>
But my temp-queue are not under temp addresses...
Does it possible to don't show temp queue in console? (because when JmsTemplate has received response or time-out, consumer is closed and temp queue is marked as deleted).
If not, how can I regroup them into one addresses folder?
or something else useful to achieve this.
My application work with about 30-40 queues, and possibly 1000 or more temp queues by day. ActiveMQ "Classic" doesn't show temp queue in web console so its easy to administer durable queue. We plan to migrate to Artemis, and during my simple test case I see that temp queue are by default shown in the web console next to all other queues, and if I have 1000 or more temp queues I need to scroll down a very long time to show the queues that I want to see. After each refresh the scroll is reinitialized. So i want to find a solution to regroup all temp queue in one folder like namespace or other solution.
There are two main ways to deal with a large number of queues and problems with refreshing the JMX "tree" view.
Use the "Queues" tab to view the queues you're interested in rather than the JMX "tree" view. You can even filter out temporary queues, e.g.:
Disable refresh of the JMX "tree" view via the "Preferences" available by clicking on the user icon in the top right of the web console, e.g.:
It's worth noting that the enable-metrics only deals with metrics as they are related to metrics plugins. Setting this to false does not disable their MBeans.
In the future the JMX "tree" like likely be removed from the web console due, in part, to the issues you're observing.
Is there a way we can push messages to RabbitMQ and have an expiry time for it and once it expires, it should provide a notification.
Or
Is there a way we can deliver the messages in RabbitMQ after a certain amount of time. For example, I want to push a message in the queue and wants it to get delivered after 10 seconds..and simultaneously next messages.
Regarding the first part of your question, the routing of messages that have expired due to a per-message TTL is a feature of the RabbitMQ dead letter exchange (DLX).
Regarding a delay, this is not something supported by RabbitMQ out of the box, nor in my opinion should it be a feature of a message broker. I can't imagine a legitimate use case where you would deliberately want to introduce a delay into a message queue. In fact, it is a design goal of any message broker to minimize delay with enqueued messages. If you find a delay to be appropriate, then it is also likely that a message queue is not the appropriate means of conveyance.
The RabbitMQ Delayed Message Plugin adds a new exchange type to RabbitMQ where messages routed by that exchange can be delayed if the users chooses to do so.
You can use it in a way like described below.
// ... elided code ...
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "direct");
channel.exchangeDeclare("my-exchange", "x-delayed-message", true, false, args);
// ... more code ...
I haven't been able to figure this one out from Google alone. I am connecting to a non-durable EMS topic, which publishes updates to a set of data. If I skip a few updates, it doesn't matter, as the following update will overwrite it anyway.
The number of messages being published on the EMS topic is quite high, and occasionally for whatever reason the consumer lags behind. Is there a way, on the client connection side, to determine a 'time to live' for messages? I know there is on other brokers, but specifically on Tibco I have been unable to figure out whether it's possible or not, only that this parameter can definitely be set on the server side for all clients (this is not an option for me).
I am creating my connection factory and then creating an Apache Camel jms endpoint with the following code:
TibjmsConnectionFactory connectionFactory = new TibjmsConnectionFactory();
connectionFactory.setServerUrl(properties.getProperty(endpoints.getServerUrl()));
connectionFactory.setUserName(properties.getProperty(endpoints.getUsername()));
connectionFactory.setUserPassword(properties.getProperty(endpoints.getPassword()));
JmsComponent emsComponent = JmsComponent.jmsComponent(connectionFactory);
emsComponent.setAsyncConsumer(true);
emsComponent.setConcurrentConsumers(Integer.parseInt(properties.getProperty("jms.concurrent.consumers")));
emsComponent.setDeliveryPersistent(false);
emsComponent.setClientId("MyClient." + ManagementFactory.getRuntimeMXBean().getName() + "." + emsConnectionNumber.getAndIncrement());
return emsComponent;
I am using tibjms-6.0.1, tibjmsufo-6.0.1, and various other tib***-6.0.1.
The JMSExpiration property can be set per message or, more globally, at the destination level (in which case the JMSExpiration of all messages received in this destination is overridden). It cannot be set per consumer.
One option would be to create a bridge from the topic to a custom queue that only your consumer application will listen to, and set the "expiration" property of this queue to 0 (unlimited). All messages published on the topic will then be copied to this queue and won't ever expire, whatever their JMSExpiration value.
RabbitMQ's Channel#basicConsume method gives us the following arguments:
channel.basicConsume(queueName, autoAck, consumerTag, noLocal,
exclusive, arguments, callback);
Giving us the ability to tell RabbitMQ exactly which queue we want to consume from.
But Channel#basicPublish has no such equivalency:
channel.basicPublish(exchangeName, routingKey, mandatory, immediateFlag,
basicProperties, messageAsBytes);
Why can't I specify the queue to publish to here?!? How do I get a Channel publishing to, say, a queue named logging? Thanks in advance!
To expand on #Tien Nguyen's answer, there is a "cheat" in RabbitMQ that effectively lets you publish directly to a queue. Each queue is automatically bound to the AMQP default exchange, with the queue's name as the routing key. The default exchange is also known as the "nameless exchange" - ie its name is the empty string. So if you publish to the exchange named "" with routing key equal to your queue's name, the message will go to just that queue. It is going through an exchange as #John said, it's just not one that you need to declare or bind yourself.
I don't have the Java client handy to try this code, but it should work.
channel.basicPublish("", myQueueName, false, false, null, myMessageAsBytes);
That said, this is mostly contrary to the spirit of how RabbitMQ works. For normal application flow you should declare and bind exchanges. But for exceptional cases the "cheat" can be useful. For example, I believe this is how the Rabbit Admin Console allows you to manually publish messages to a queue without all the ceremony of creating and binding exchanges.
Basically queues can be binded to an exchange based on routingKeys.
Assume that you have 3 different publishers.
Publisher1 sending message to exchange with routingKey "events"
Publisher2 sending message to exchange with routingKey "tasks"
Publisher3 sending message to exchange with routingKey "jobs"
You can have a consumer that consumes only messages with specific routhingKey.
For example in order to have a consumer for "events" messages you declare like this
channel.queueBind(queueName, exchangeName, "events");
If you want to consume all the messages coming to the exchange you give the routing as '#'
So in short what i can say is,
1. Messages will be published to an exchange.
2. Queues will be bound to exchange based on routingKeys.
3. RabbitMQ will forward messages with matching routing keys to the corresponding queues.
Please see the tutorial - http://www.rabbitmq.com/tutorials/tutorial-three-java.html
The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesn't even know if a message will be delivered to any queue at all. Instead, the producer can only send messages to an exchange
please try this:
channel.basicPublish("", yourQueueName, null,
message.getBytes((Charset.forName("UTF-8"))));
It worked for my project.
Is it possible to send message to particular receiver using JMS Queue(HornetQ)?
Among so many receivers, I want certain message to be received by receiver which
are running on Linux OS.
Every suggestion is appriciated.
Thanks.
You can set a message property using Message.setObjectProperty(String, Object) and then have your consumers select the messages they are interested in using Session.createConsumer(Destination, String)
Sender example:
Message message = session.createMessage();
message.setObjectProperty("OS", "LINUX");
producer.send(message);
Receiver example:
MessageConsumer consumer = session.createConsumer(destination, "OS = 'LINUX'");
//Use consumer to receive messages.
The receiver in the example will ignore (they will go to some other receiver) all messages that do not match the selector. In this case all message where the 'OS' property is not 'LINUX' will be ignored by this consumer.
You can set properties of JMS message: http://download.oracle.com/javaee/1.4/api/javax/jms/TextMessage.html and filter messages at client side.
For example,
message.setStringProperty("TARGET_OS", "LINUX") - at sender
http://www.mkyong.com/java/how-to-detect-os-in-java-systemgetpropertyosname/ - detect OS at receivers and filter messages with correct TARGET_OS property
You can use JMS selectors on the consumer side to look for messages that fit specific criteria.
Not sure if I am missing something, you could keep things simple by having multiple queues - specific to each platform, then the linux based consumers can listen to the linux specific queue alone. Now your challenge probably will be to route the messages to the appropriate queue from the producer side, that should be fairly easy if the routing is based on some attribute of the message?