Spring AMQP: Queue with machine name - java

I'm working with Spring AMQP to create queues in RabbitMQ. I'd like to have a queue whose name includes the name of the machine that the app is running on. So the queue name might be "fooQueue.host1" or "fooQueue.host2" depending on where you run the app.
I've figured out a way to do this (detailed below) but it seems a little complicated. Is there an easier/better/Spring-ier way to accomplish this?
My Solution
First make a bean to fetch the machine name:
public class MachineNamePropertyBean {
public String GetMachineName() throws UnknownHostException {
InetAddress localMachine = InetAddress.getLocalHost();
return localMachine.getHostName();
}
}
Then register the bean in your Spring config:
<bean id="machineNameBean" class="com.example.myapp.MachineNamePropertyBean" />
then use it in your Spring AMQP config like this:
<rabbit:queue id="fooQueue"
name="fooQueue.#{ machineNameBean.GetMachineName() }"
durable="false"
auto-delete="false"
exclusive="false" />

There is no other solution unless using SpEL:
<bean id="machineName" class="java.lang.String">
<constructor-arg value="#{T(java.net.InetAddress).localHost.hostName}"/>
</bean>
<rabbit:queue id="fooQueue"
name="fooQueue.#{ machineName }"
durable="false"
auto-delete="false"
exclusive="false" />
The same as you are doing, but without new class and via SpEL features.

Related

Spring RabbitMQ SimpleRabbitListenerContainerFactory usage

From the docs, I want to use consume from queues by dynamically changing the consumers without restarting the application.
I do see that Spring RabbitMQ latest version supports the same, but no clue/example/explanation to change the same. I couldn't see proper source code for the same or how to pass params like maxConcurrentConsumers
I am using XML based configuration of Spring RabbitMQ along with Spring integration
<bean id="rabbitListenerContainerFactory"
class="org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="concurrentConsumers" value="3"/>
<property name="maxConcurrentConsumers" value="10"/>
<property name="acknowledgeMode" value="AUTO" />
</bean>
<int-amqp:inbound-channel-adapter channel="lowInboundChannel" queue-names="lowLoadQueue" advice-chain="retryInterceptor" acknowledge-mode="AUTO" listener-container="rabbitListenerContainerFactory" />
<int-amqp:inbound-channel-adapter channel="highInboundChannel" queue-names="highLoadQueue" advice-chain="retryInterceptor" acknowledge-mode="AUTO" listener-container="rabbitListenerContainerFactory" />
Can anyone guide me how to dynamically configure the consumers?
First of all you shouldn't share the same rabbitListenerContainerFactory for different <int-amqp:inbound-channel-adapter>s, because they do this:
protected void onInit() {
this.messageListenerContainer.setMessageListener(new ChannelAwareMessageListener() {
So, only last adapter wins.
From other side there is even no reason to have several adapters. You can specify queue-names="highLoadQueue,lowLoadQueue" for a single adapter.
Although in case of listener-container you must specify queues on the SimpleRabbitListenerContainerFactory.
If you want to change some rabbitListenerContainerFactory options at runtime, you can just inject it to some service and invoke its setters.
Let me know if I have missed anything.

Spring 4 + Websockets: how to close the session?

I am using Spring 4 + Websockets + Stomp JS library.
I could not find any way to setup websocket ping/pong mechanism (heartbeat).
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans" ...">
<websocket:message-broker>
<websocket:stomp-endpoint path="/cors/auth/clientEndpoint">
<websocket:handshake-handler ref="myHandshakeHandler" />
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/queue, /topic" />
<websocket:client-inbound-channel>
<websocket:interceptors>
<bean class="com.mycompany.myproject.utils.messaging.MyInboundChannelInterception"></bean>
</websocket:interceptors>
</websocket:client-inbound-channel>
</websocket:message-broker>
<bean id="myHandshakeHandler" class="com.mycompany.myproject.utils.security.MyHandshakeHandler" />
<bean class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean">
<property name="maxSessionIdleTimeout" value="120000" />
</bean>
As result, I am implementing my own mechanism of ping/pong messages.
One of the tasks here - to implement server side closure of the websocket in case if no ping message during more than 10s from client.
And no way to do this using Spring Websockets!
Maybe somebody can tell me how to access Session object of the user or to close those Session via Spring Websockets?
Seems Spring is very limited here.
I'm surprised spring doc doesn't mention how to config server ping...It seems that spring expects us to read code instead of read doc..
after some time searching on net and reading source code, I realize it's already supported, but not documented at a noticeable place like spring websocket doc.
I'm using spring 4.3.3, and here is how to config server ping without using sockJS:
#Configuration
#EnableWebSocketMessageBroker
public class StompOverWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
ThreadPoolTaskScheduler pingScheduler = new ThreadPoolTaskScheduler();
pingScheduler.initialize();
registry.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[]{20000, 0}).setTaskScheduler(pingScheduler);
}
....
}
and should make sure that you correctly set web socket session timeout, it should be greater than ping interval, like this:
<bean id="servletServerContainerFactoryBean" class="org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean">
<property name="maxSessionIdleTimeout" value="30000"/>
</bean>
In this case, configuring SockJS in your app could go a long way:
<websocket:stomp-endpoint path="/cors/auth/clientEndpoint">
<websocket:handshake-handler ref="myHandshakeHandler" />
<websocket:sockjs/>
</websocket:stomp-endpoint>
This will give you:
better HTTP clients support
heartbeat management
If you want to actually close a session from STOMP endpoints, then I suggest you to vote/follow the SPR-12288 JIRA issue.
To access websocket session you can use the following approach:
https://stackoverflow.com/a/32270216/2982835

Redis : Can I init jedis instance as a static final field?

I need to use Redis as data source in Java, so I decide to use the code:
public class RedisService {
private static final Jedis jedis = new Jedis("host",6400);;
public static Device getDevice(String key) {
// Do something use redis.
return null;
}
}
I thought the server will automatically init Jedis(Redis API for Java), it this a good way to use Jedis ?
Have a look at how we are using Jedis:
Create a singleton org.springframework.data.redis.connection.jedis.JedisConnectionFactory instance by passing host and port info
Create singleton org.springframework.data.redis.core.RedisTemplate instance by passing the connection factory to it
Use the redisTemplate created above in your service, the benefit of using Redistemplate is that you can use it perform operation across all data structures provided by redis( list, set, hashes)
Just for your reference, here's the spring code that does the same, you can use if your are using spring else you can create the same using java code
<!-- Create Factory -->
<bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
<property name="hostName" value="localhost" />
<property name="port" value="6370" />
<property name="timeout" value="5000" />
</bean>
<!-- Create Redis Template -->
<bean id="redisRemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name ="connectionFactory" ref="jedisFactory" />
</bean>
<!-- Your Service class -->
<bean id="serviceClass" class="RedisService" >
<property name ="redisTemplate" ref="redisRemplate" />
</bean>
public class RedisService
{
private final RedisTemplate redisTemplate = /* get from store or inject using spring */;
public static Device getDevice(String key) {
// Do something use Redis.
return null;
}
}
As Santosh Joshi tried to explain: it is best to use a JedisFactory. Your Jedis which is Singleton can "die" due to network, overload etc... and you will have to restart your application to get a new connection to Redis.
To counter that, you can define a Jedis Pool and, if you don't want to use Spring (on which the solution from Santosh is based on), you can use the JedisPool class which is provided with Jedis. Then, you can define it as a singleton (as static final or via Spring for instance) and get Jedis instances from it.
As it is a pool you can get more than 1 connection to Redis at a time (you can configure that), and it supports dealing with broken connections: it creates fresh new Jedis when one is dead.

Spring RabbitMQ - is a queue configuration with no exchanges possible

Here is an existing spring rabbit config from a project that I inherited -
<rabbit:connection-factory id="rabbitConnectionFactory"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
virtual-host="${rabbitmq.virtualHost}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"/>
<rabbit:template id="ampqTemplate" connection-factory="rabbitConnectionFactory" routing-key="" />
<rabbit:admin connection-factory="rabbitConnectionFactory" />
<rabbit:queue name="${rabbitmq.queueName}" />
I dont have experience using Rabbit and with my limited reference,
I understand an exchange is an important piece in the setup since it relays messages to the queue internally.
However, the above configuration does not contain any exchange information.
My Questions are :
Is an exchange absolutely important for even a simple queue configuration.?
Is there any implication of not defining an exchange
Is there anyother configuration obviously missing from the above configuration?
If you don't define an exchange the default exchange will be used. It is a direct exchange which will use the name of the queue as its routing key.
It doesn't look like there is anything missing from your configuration. Mine is:
<rabbit:connection-factory id="connectionFactory" host="${rabbit.host}" username="${rabbit.username}" password="${rabbit.password}" virtual-host="${rabbit.vhost}"/>
<rabbit:template id="rabbitTemplate" connection-factory="connectionFactory" message-converter="jsonMessageConverter" routing-key="event-queue"/>
You may be able to take the rabbit:queue definition out and use the queue name as the routing key for the rabbit:template.

How to receive what was sent by convertAndSend?

I'm reading Spring Framework reference, chapter about JMS integration. There are some examples for sending text messages and asynchronously receiving them (by listeners). And there is also an example for JmsTemplate function convertAndSend which converts given object to a message. The reference says:
By using the converter, you and your application code can focus on the business object that is being sent or received via JMS and not be concerned with the details of how it is represented as a JMS message.
But there is no example for receiving such messages. They mention function receiveAndConvert but, unfortunately, it receives synchronously.
So how am I to receive it asynchronously? Must I be aware that when I convertAndSend a Map, the resulting message will be a MapMessage, and just check in my listener for this type of message and handle it? But they promised I'm not to be concerned with the details of how it is represented as a JMS message.
So is there a better way?
I know it's been a while since this was asked, but I had the same problem, solved it and wanted to give an explicit code example here.
Here's my MessageListener. This implements the onMessage(Message) method to intercept messages asynchronously.
package com.package.amqp;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.support.converter.JsonMessageConverter;
import com.package.model.User;
public class TestListener implements MessageListener {
public void onMessage(Message message) {
JsonMessageConverter jmc = new JsonMessageConverter();
User u = (User)jmc.fromMessage(message);
System.out.println("received: " + u.getFirstName());
}
}
The messages are then converted using the standard JsonMessageConvertor in my case as this is the messageConvertor I plugged into my rabbitTemplate bean.
<bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory">
<constructor-arg value="10.10.1.2"/>
<property name="username" value="guest"/>
<property name="password" value="guest"/>
</bean>
<bean class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="queueName" value="queue.helloWorld"/>
<property name="messageListener" ref="someListener"/>
</bean>
<bean id="someListener" class="com.package.amqp.TestListener"></bean>
<bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<property name="connectionFactory" ref="rabbitConnectionFactory"/>
<property name="messageConverter">
<bean class="org.springframework.amqp.support.converter.JsonMessageConverter"/>
</property>
</bean>
Hope this helps someone!
Owen
While JmsTemplate provides basic synchronous receive methods, asynchronous reception is a whole lot more complicated, and is beyond the scope of JmsTemplate.
Asynchronous reception of JMS messages is done in Spring using Message Listener Containers, which asynchronously take messages from the JMS destination and pass them to your application. You can plug a MessageConverter in to your message listener container via a MessageListenerAdapter (plug the converter into the adapter, plug your application's listener into the adapter, then plug the adapter into the listener container).

Categories

Resources