I'm new in rabbitmq.I'm using spring-amqp to implement the feature.
As we know spring provide #RabbitListener to register a listener to queue when the app initialization.
I want to design a function when I click some button, a new consumer will be created and listen to a specified queue.
Java base provide channel.basicConsume() method to consume a queue.
Is spring provide such function ?
I want to implement like :
producer keep sending messages to a fanout exchange.
when a consume wants to join, call function1 -> create queue and binding to exchange -> consume messages.
when a consume wants leave, call function2 -> disconnect
There are a few options.
Use one of the RabbitTemplate.receive() or convertAndReceive() methods to get messages one-at-a-time, you can set a receiveTimeout in case there are no messages.
RabbitTemplate.execute() with a callback that gets a channel that you can call basicConsume() on. This is a lower-level option and won't do any conversion for you.
Create a SimpleMessageListenerContainer (or DirectMessageListenerContainer) dynamically and start/stop it as needed.
...
In all cases, you can use a RabbitAdmin to create/bind the queue, for all except option 1, it would probably an auto-delete queue that will be removed when the consumer is cancelled. With option 1, you would have to use a non-auto-delete queue and remove it with the RabbitAdmin.
I would suggest that #3 is the most efficient using pure Spring AMQP.
You could also use Spring Integration with an inbound channel adapter and a publish-subscribe channel; that way you only need one queue (per application instance) and then subscribe a new MessageHandler to the channel for each user.
Related
Setting up a CMS consumer with a listener involves two separate calls: first, acquiring a consumer:
cms::MessageConsumer* cms::Session::createConsumer( const cms::Destination* );
and then, setting a listener on the consumer:
void cms::MessageConsumer::setMessageListener( cms::MessageListener* );
Could messages be lost if the implementation subscribes to the destination (and receives messages from the broker/router) before the listener is activated? Or are such messages queued internally and delivered to the listener upon activation?
Why isn't there an API call to create the consumer with a listener as a construction argument? (Is it because the JMS spec doesn't have it?)
(Addendum: this is probably a flaw in the API itself. A more logical order would be to instantiate a consumer from a session, and have a cms::Consumer::subscribe( cms::Destination*, cms::MessageListener* ) method in the API.)
I don't think the API is flawed necessarily. Obviously it could have been designed a different way, but I believe the solution to your alleged problem comes from the start method on the Connection object (inherited via Startable). The documentation for Connection states:
A CMS client typically creates a connection, one or more sessions, and a number of message producers and consumers. When a connection is created, it is in stopped mode. That means that no messages are being delivered.
It is typical to leave the connection in stopped mode until setup is complete (that is, until all message consumers have been created). At that point, the client calls the connection's start method, and messages begin arriving at the connection's consumers. This setup convention minimizes any client confusion that may result from asynchronous message delivery while the client is still in the process of setting itself up.
A connection can be started immediately, and the setup can be done afterwards. Clients that do this must be prepared to handle asynchronous message delivery while they are still in the process of setting up.
This is the same pattern that JMS follows.
In any case I don't think there's any risk of message loss regardless of when you invoke start(). If the consumer is using an auto-acknowledge mode then messages should only be automatically acknowledged once they are delivered synchronously via one of the receive methods or asynchronously through the listener's onMessage. To do otherwise would be a bug in my estimation. I've worked with JMS for the last 10 years on various implementations and I've never seen any kind of condition where messages were lost related to this.
If you want to add consumers after you've already invoked start() you could certainly call stop() first, but I don't see any problem with simply adding them on the fly.
I am integrating several .Net modules using pub/sub messaging using RabbitMQ and MassTransit. Most of the message subscription shall be durable. But some shall be transient. When a consumer dies the messages shall not be stored and already queued messages shall be discarded.
In each module I create 1 bus with 2 receive endpoints. One is configured as durable and non-auto-delete. The other one is configured as non-durable and auto-delete. Each gets its own set of consumers. This works as expected.
Now I am trying to implement request/response messages. Here comes the problem because now the sender has to decide to which exchange to route to. And that is wrong as I want receiver to decide whether to use durable or transient queue.
My questions:
Is there a better way how to support durable and transient subscription at the same time?
Why is MassTransit binding message exchange to an endpoint exchange that is bound to an endpoint queue? Why cannot the message exchange be directly bound to the endpoint queue?
Lets assume that all request consumers in one module are either durable or transient. Is it possible to declare one "module"-exchange which is then bound to either durable or transient queue? So the sender addresses the module exchange and module decides to which queue to bind. How to convince MassTransit to do so?
A module is using durable subscriptions that survive through restarts of module and also broker. After some time, admin (so in run-time of the system) decides to disconnect this module from the system. Can the module somehow unsubscribe everything and let MassTransit to remove the durable exchanges and queues?
Your question starts with request/response sent to an unknown endpoint, and ends with removing exchanges. These are different things, I suppose.
I cannot answer point-by-point, just will try to clear up things.
Request/response by definition requires you to know where you send stuff. As per MassTransit convention, the endpoint address is always an exchange/queue pair address. Therefore, you cannot let receiver decide who will handle this message, it will be delivered to the exchange/queue of the endpoint where you send it to.
About the "unsubscribe" - MassTransit deletes nothing. You have to clean up the binding that is not being used manually or by using the management API.
DefaultConsumer has a channel that it binds to and can be acquired with getChannel(). Can I use this channel in handleDelivery to publish something to another queue or should I create a new factory+connection+channel trifecta and use that to publish? I want to publish an event to another queue when consumer consumes an event from it's queue, just not sure if the consumer channel can be re-used for publishing and if it is safe to do.
The best practice is to use different channels
you should no use the same channel to consume and publish
This is the scenario - There are multiple app servers. Browser can connect via websocket to any app server.
The app servers (consumers) are all listening on a particular queue. As soon as a web socket connection is received, the particular app server binds the queue with a routing key {userId} to a direct exchange.
I want a message sent to the direct exchange with the routing key {userId} to be received by only the particular app server where the binding has occured.
Is a direct exchange the right exchange to use in this case? Or should some other type of exchange be used?
I'm using spring-amqp to create dynamic bindings when a websocket comes in
// create the RabbitMq queue and bind to it
String routingKey = MessageConstants.getRoutingKeyForUserRecommendationQueue(user);
Binding userRecommendationBinding = BindingBuilder.bind(userRecommendationsQueue).
to(directExchange).with(routingKey);
amqpAdmin.declareBinding(userRecommendationBinding);
Send message to a particular consumer in a queue
this is not possible. any consumer connected to a queue has a chance of consuming any given message in the queue
I want a message sent to the direct exchange with the routing key {userId} to be received by only the particular app server where the binding has occured.
you can do this by creating exclusive / autoDelete queues for your consumer, with a binding that directs all messages for that consumer to that queue.
Is a direct exchange the right exchange to use in this case?
either a direct exchange or a topic exchange is fine. direct exchange is slightly easier to understand, but topic exchange is more flexible
Actually you go right way.
And yes: Direct Exchange with an appropriate binding should save you.
See more info in the RabbitMQ Tutorial: http://www.rabbitmq.com/tutorials/tutorial-four-java.html
Also take a look into Spring AMQP Samples on the matter: https://github.com/spring-projects/spring-amqp-samples/tree/master/rabbitmq-tutorials
UPDATE
Unfortunately that is not what is happening. The messages seem to go randomly to any consumer, and not just the consumer that created the binding.
M-m-m. That's possible, because we route only my the key, but after that the message is placed to the queue, which may have several consumers on different machines.
In this case yes: the dynamic binding doesn't help.
You should consider to create an unique new queue (auto-deleted is fine) and bind and listen exactly from that. The SimpleMessageListenerContainer supports addQueues() at runtime to start a new consumer for a new queue.
I think that should work for you.
You still shouldn't do anything on the producer side: the same exhchange and routingKey logic.
In RabbitMQ api, when auto-ack is set to false on a channel, we can use channel.basicAck(...) to send acknowledgement back to the queue.
In Spring, I have a SimpleMessageListenerContainer where I set AcknowledgeMode.MANUAL. and handler (SomeMessageHandler that handles a String type) I set as the listener for the MessageListenerAdapter. I can't find anywhere (any component) where I send the acknowledgement back to the queue. Is there a component I need to autowire to my handler to take care of this? Or what is the the correct way to handle this acknowledgement, when acknowledgment mode is set to manual?
When using MANUAL acks, you can't use the MessageListenerAdapter, you have to implement ChannelAwareMessageListener. However, MANUAL ack is rarely needed with Spring AMQP, the container will take care of it for you on success (or reject on failure) when the delivery completes (or is handed off to another thread).