Spring RabbitMQ - is a queue configuration with no exchanges possible - java

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.

Related

dynamically determining polling frequency in mule

I have been struggling to find a work around to be able to dynamically read the polling frequency in mule flow. Currently I am reading that from a file using spring's Propertyplaceholder at the start up and value remains the same even if the fie is changed(as we all know)..
Since poll tag needs to be the first component in the flow, There is nothing much i could do to read the "live" file update.
Is there any way I could set the polling frequency dynamically read from a file(without requiring restart)?
For Reference:
<spring:beans>
<context:property-placeholder location="file:///C:/Users/test/config.properties" />
</spring:beans>
<flow name="querying-database-pollingFlow1" doc:name="querying-database-pollingFlow1">
<poll doc:name="Poll3e3">
<fixed-frequency-scheduler frequency="${pollinginterval}"/>
<db:select config-ref="MySQL_Configuration1" doc:name="Perform a query in MySQL">
<db:dynamic-query><![CDATA[select empId,empName from employer where status='active';]]></db:dynamic-query>
</db:select>
</poll>
....</flow>
There is absolutely no issue with <fixed-frequency-scheduler frequency="${pollinginterval}"/> as you can dynamically read polling frequency from a properties file ...
The only thing I am concern here is :- <context:property-placeholder location="file:///C:/Users/test/config.properties" />
Since you are reading from a properties file outside your classpath, better try with the following :-
<context:property-placeholder
location="file:C:/Users/test/config.properties" />
One more thing .. if you are using Spring beans for properties file use the following way :-
<spring:beans>
<spring:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<spring:property name="locations">
<spring:list>
<spring:value>file:C:/Users/test/config.properties</spring:value>
</spring:list>
</spring:property>
</spring:bean>
</spring:beans>
The clean way using FixedFrequencyScheduler is not there. You could potentially go to the registry, fetch your flow by name, then get the MessageSource and cast it to FixedFrequencyScheduler set the new interval and stop-start, however if you take a look to the code you'll see there is no setter for it and reflexion it's just too dirty.
My first choice would probably be to leverage a quartz endpoint and then leverage the quartz abilities to expose the configuration throught jmx/rmi.
I would definitely advise against using hot deploy to solve this problem especially if you need to change the frequency often. There is a risk that this will lead to problems with permgen running out of memory.
Instead you could use a flow with a quartz endpoint that fires at a relatively low frequency. Then add a filter that only lets through the message at the required frequency.
The filter can either watch a properties file for changes or expose attributes over JMX to allow you to change the frequency. Something like this.
<spring:beans>
<spring:bean id="frequencyFilter" class="FrequencyFilter" />
</spring:beans>
<flow name="trigger-polling-every-second" doc:name="trigger-polling-every-second">
<quartz:inbound-endpoint repeatInterval="1000" doc:name="Quartz" responseTimeout="10000" jobName="poll-trigger">
<quartz:event-generator-job>
<quartz:payload>Scheduled Trigger</quartz:payload>
</quartz:event-generator-job>
</quartz:inbound-endpoint>
<filter ref="frequencyFilter" />
<vm:outbound-endpoint path="query-database" />
</flow>
<flow name="query-database">
<vm:inbound-endpoint path="query-database" />
<db:select config-ref="databaseConfig" doc:name="Perform a query in database">
<db:dynamic-query><![CDATA[select empId,empName from employer where status='active']]></db:dynamic-query>
</db:select>
<logger level="ERROR" message="#[payload]"/>
</flow>

Spring AMQP: Queue with machine name

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.

Hazelcast Map TTL not working with spring cache abstraction

I am trying to get Hazelcast 3.0.2 working with Spring abstraction however it seems the TTL functionality is not working.
I have configured my spring context in the following way
<cache:annotation-driven cache-manager="cacheManager" mode="proxy" proxy-target-class="true" />
<bean id="cacheManager" class="com.hazelcast.spring.cache.HazelcastCacheManager">
<constructor-arg ref="hzInstance" />
</bean>
<hz:hazelcast id="hzInstance">
<hz:config>
<hz:group name="instance" password="password" />
<hz:properties>
<hz:property name="hazelcast.merge.first.run.delay.seconds">5</hz:property>
<hz:property name="hazelcast.merge.next.run.delay.seconds">5</hz:property>
<hz:property name="hazelcast.logging.type">slf4j</hz:property>
<hz:property name="hazelcast.jmx">true</hz:property>
<hz:property name="hazelcast.jmx.detailed">true</hz:property>
</hz:properties>
<hz:network port="8995" port-auto-increment="true">
<hz:join>
<hz:tcp-ip enabled="true">
<hz:interface>10.0.5.5</hz:interface>
<hz:interface>10.0.5.7</hz:interface>
</hz:tcp-ip>
</hz:join>
</hz:network>
<hz:map name="somecache"
backup-count="1"
max-size="0"
eviction-percentage="30"
read-backup-data="false"
time-to-live-seconds="120"
eviction-policy="NONE"
merge-policy="hz.ADD_NEW_ENTRY" />
</hz:config>
</hz:hazelcast>
I then made a simple test class having the following method
#Cacheable("somecache")
public boolean insertDataIntoCache(String data) {
logger.info("Inserting data = '{}' into cache",data);
return true;
}
I also made some method to print some information from every map Hazelcast finds and also the entires inside. Inserting the data and caching seems to work fine however the entry never expires even though I set a TTL of 120 seconds.
When I write the data from the cache it shows me that there is one map called "somecache" and that map has a TTL of 120 seconds but when I loop through the entries, it finds all the ones I inserted with a expirationTime of 0. I am not what is supposed to be the behaviour of hazelcast (maybe a map ttl takes precedence over an entry ttl) but in any case it will just not expire.
Is anybody aware of any issues with 3.0.2 and spring cache? I should also mention that I have other applications in the same application server running an older version of Hazelcast however they have their own separate config and my test application seems to be keeping to itself and not conflicting with anything.
Any input is appreciated.
EDIT 1:
It seems to work if I downgrade to using HZ 2.6.3 so it looks like there is a bug somewhere in hazelcast 3 regarding TTL
I just stumbled on the same thing and it seems that it has been fixed about a month ago: https://github.com/hazelcast/hazelcast/commit/602ce5835a7cc5e495b8e75aa3d4192db34d8b1a#diff-d20dd943d2216ab106807892ead44871
Basically TTL was overridden when you use Hazelcast Spring integration.

Spring JMS - Draining Topic on a Timer

I'm fairly new to Spring JMS, and I've found lots of documentation and examples at the Spring site and elsewhere, but my use case doesn't seem to be described anywhere, or at least in a way I can understand. I hope you might be able to help.
I would like to create a publisher of a topic and several durable subscribers to that topic. I'm working on the first subscriber now, and it is intended to run hourly (on a timer) and drain the topic of messages and process them all at once (i.e. to send an email summarizing all messages).
I do not know how to configure this setup in Spring, although I feel like this should be easy. Advice would be tremendously helpful.
My plan, such as it is, was to have the timer invoke the "processBatch" method, which would call receiveAndConvert() in a loop until it timed out, building up its list of messages.
This doesn't seem to work, though, because the consumer isn't really subscribed to the topic. Certainly not before it's run, and potentially not afterward.
How can I configure this using Spring and/or direct ActiveMQ?
I'm not sure where I ended up with my XML is a useful place for this discussion to start, but I'll provide it in case it is helpful:
<beans>
<!-- some unrelated beans -->
<!-- my Active MQ connection factory -->
<bean id="mqConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://broker"/>
</bean>
</property>
</bean>
<!-- my topic -->
<amq:topic id="completionsTopic" physicalName="completions.topic"/>
<!-- my subscriber -->
<bean id="emailer" class="com.j128.Emailer">
<property name="jmsTemplate">
<bean class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="mqConnectionFactory"/>
<property name="defaultDestination" ref="completionsTopic"/>
<property name="receiveTimeout" value="2000"/>
</bean>
</property>
</bean>
<!-- my scheduler and periodic call to the topic drainer -->
<task:scheduler id="taskScheduler" pool-size="10"/>
<task:scheduled-tasks>
<!-- send emails hourly -->
<task:scheduled ref="emailer" method="processBatch" cron="0 * * * *"/>
</task:scheduled-tasks>
</beans>
But I'm certain I fundamentally have the wrong strategy and that there's a simple way to configure this.
Thank you for your assistance.
Have a look at how-does-a-queue-compare-to-a-topic: "Only subscribers who had an active subscription at the time the broker receives the message will get a copy of the message."
And for a durable topic how-do-durable-queues-and-topics-work: "Durable topics however are different as they must logically persist an instance of each suitable message for every durable consumer - since each durable consumer gets their own copy of the message"
So with a non-durable topic, your plan wont work as the hourly job won't get any messages as it isn't running when the messages are published. If you set up a durable topic then it might work but it depends what you expect to happen when you say your subscriber will "drain the topic of messages". All it can do it read the messages published to it since it last run, it can't affect the messages going to other subscribers.
For a discussion around durable subscribers (I haven't used them on ActiveMQ) see this

What's the right way to ensure jms consumers are closed using spring integration?

I'm using spring integration to invoke a service on the other end of an active mq. My config looks like:
<bean id="jmsConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg>
<bean class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="${risk.approval.queue.broker}"
p:userName="${risk.approval.queue.username}"
p:password="${risk.approval.queue.password}"
/>
</constructor-arg>
<property name="reconnectOnException" value="true"/>
<property name="sessionCacheSize" value="100"/>
</bean>
<!-- create and close a connection to prepopulate the pool -->
<bean factory-bean="jmsConnectionFactory" factory-method="createConnection" class="javax.jms.Connection"
init-method="close" />
<integration:channel id="riskApprovalRequestChannel"/>
<integration:channel id="riskApprovalResponseChannel"/>
<jms:outbound-gateway id="riskApprovalServiceGateway"
request-destination-name="${risk.approval.queue.request}"
reply-destination-name="${risk.approval.queue.response}"
request-channel="riskApprovalRequestChannel"
reply-channel="riskApprovalResponseChannel"
connection-factory="jmsConnectionFactory"
receive-timeout="5000"/>
<integration:gateway id="riskApprovalService" service-interface="com.my.super.ServiceInterface"
default-request-channel="riskApprovalRequestChannel"
default-reply-channel="riskApprovalResponseChannel"/>
What I've noticed is that with this config the consumers created to grab the matching request from active mq never close. Every request increments the consumer count.
I can stop this from happening by adding
<property name="cacheConsumers" value="false" />
To the CachingConnectionFactory.
However according to the java docs for CachingConnectionFactory :
Note that durable subscribers will only be cached until logical
closing of the Session handle.
Which suggests that the session is never being closed.
Is this a bad thing? Is there a better way to stop the consumers from piling up?
Cheers,
Peter
First, you don't need the init-method on your factory-bean - it does nothing - the session factory only has one connection and calling close() on it is a no-op. (CCF is a subclass of SingleConnectionFactory).
Second; caching consumers is the default; sessions are never closed, unless the number of sessions exceeds the sessionCacheSize (which you have set to 100).
When close() is called on a cached session, it is cached for reuse; that's what the caching connection factory is for - avoiding the overhead of session creation for every request.
If you don't want the performance benefit of caching sessions, producers and consumers, use the SingleConnectionFactory instead. See the JavaDoc for CachingConnectionFactory.
Does the following work when using cachingConnectionFactory?
In your spring config file add in the connection factory config details something like this: cacheConsumers="false"
Default Behaviour is true which was causing a connection leak in the Queue.

Categories

Resources