I have implemented ActiveMQ message broker in my application but whenever I'm connected to ActiveMQ server in my console logs I'm always seeing the below messages:
10:28:05.282 [ActiveMQ InactivityMonitor WriteCheckTimer] DEBUG o.a.a.t.AbstractInactivityMonitor - WriteChecker: 10000ms elapsed since last write check.
10:28:05.282 [ActiveMQ InactivityMonitor Worker] DEBUG o.a.a.t.AbstractInactivityMonitor - Running WriteCheck[tcp://10.211.127.203:61616]
Looks it keep on polling the listener queue it seems. I want my listener to be active so that whenever the message comes to the queue, the application can process it. At the same time, i do not want this message to pile up my log file.
My message configuration:
#Configuration
#EnableJms
#ImportResource("classpath*:beans.xml")
public class MessagingConfiguration {
#Autowired
MongoCredentialEncryptor encryptor;
#Value("${activemq.broker.url}")
private String BROKER_URL = "tcp://localhost:61616";
#Value("${activemq.request.queue}")
private String REQUEST_QUEUE = "test.request";
#Value("${activemq.response.queue}")
private String RESPONSE_QUEUE = "test.response";
#Value("${activemq.borker.username}")
private String BROKER_USERNAME = "admin";
#Value("${activemq.borker.password}")
private String BROKER_PASSWORD = "admin";
#Autowired
ListenerClass messageListener;
#Autowired
JmsExceptionListener jmsExceptionListener;
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(BROKER_URL);
connectionFactory.setUserName(BROKER_USERNAME);
//connectionFactory.setPassword(BROKER_PASSWORD);
connectionFactory.setPassword(encryptor.decrypt(BROKER_PASSWORD));
connectionFactory.setTrustAllPackages(true);
connectionFactory.setRedeliveryPolicy(redeliveryPolicy());
return connectionFactory;
}
#Bean
public RedeliveryPolicy redeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setBackOffMultiplier(3); // Wait 5 seconds first re-delivery, then 15, 45 seconds
redeliveryPolicy.setInitialRedeliveryDelay(5000);
redeliveryPolicy.setMaximumRedeliveries(3);
redeliveryPolicy.setUseExponentialBackOff(true);
return redeliveryPolicy;
}
#Bean
public ConnectionFactory cachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(connectionFactory());
connectionFactory.setExceptionListener(jmsExceptionListener);
connectionFactory.setSessionCacheSize(100);
connectionFactory.setCacheConsumers(false);
connectionFactory.setCacheProducers(false);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(connectionFactory());
template.setDefaultDestinationName(REQUEST_QUEUE);
template.setSessionAcknowledgeModeName("CLIENT_ACKNOWLEDGE");
template.setMessageConverter(converter());
return template;
}
#Bean
public DefaultMessageListenerContainer jmsListenerContainer() {
DefaultMessageListenerContainer factory = new DefaultMessageListenerContainer();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrency("1-1");
factory.setDestinationName(RESPONSE_QUEUE);
factory.setMessageListener(messageListener);
factory.setExceptionListener(jmsExceptionListener);
return factory;
}
#Bean
MessageConverter converter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Solution:
I found a solution to my problem by disabling the InactivityMonitor by following this link ActiveMQ InactivityMonitor. I tested it the connection is still alive and its not keep on polling the queue. But i would like to know is there any downfall by disabling InactivityMonitor? Is there any better solution for this problem.
activemq.broker.url=failover:tcp://localhost:61616?wireFormat.maxInactivityDuration=0
The fix is simple, change your logging settings to not write at debug level or filter that one logger to not be at debug level. The logs are keeping you updated on the fact that the client is successfully pinging the remote broker to ensure the connection is alive. If you disable the monitoring then you can miss connection drop events are things like half closed sockets etc.
In production you really don't want to be turning off connection checking features that cause your code to miss the fact that it is not going to be able to receive anything
Example configuration modification:
<logger name="org.apache.activemq.transport" level="WARN"/>
There is nothing harm if this log is getting printed, InactivityMonitor just checks whether the connection between broker and client is active or not.
If it finds that the connection is inactive for the given time, then InactivityMonitor closes the connection between client and broker.
We can set the timeout for InactivityMonitor by specifying wireFormat.maxInactivityDuration="<time in ms>" in the tcp URLs in transportConnectors section of activemq.xml file.
For detailed information visit this : InactivityMonitor
Related
I have written a Spring Application that is implementing RabbitMQ manually with the RabbitMQ Client API.
The way the Connection Factory and Connection are set are similar to the tutorial:
public class Recv {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException,
java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("122.34.1.1");
factory.setPort(5672);
factory.setUsername("user");
factory.setPassword("password")
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
...
}
}
The connection works correct. However, if I turn off my host server, my application seems to stuck in some sort of loop where it tries to auto recover by keep pinging the turned-off host.
Because of this, my console get filled with tons of stacktraces that say "UnknownHostException". The exact location according to stacktraces is the line:
Connection connection = factory.newConnection();
I have tried to put a try-catch block around this line, but that doesn't seem to work at all.
If the traditional try-catch block can't handle the exception coming from the connection, what is the proper way to catch the exception and stop the auto-recovery from creating this loop?
Thanks.
Try setting up your Rabbit like this (on both ends):
#Bean
public ConnectionFactory connectionFactory() {
final CachingConnectionFactory connectionFactory = new CachingConnectionFactory(server);
connectionFactory.setUsername("user");
connectionFactory.setPassword("pass");
return connectionFactory;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
final SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
return factory;
}
The port should be set by default. If not, it will show on startup and you can change it. The Queue should be declared and set by the consumer (host or whatever you call it).
And put a #EnableRabbit annotation on your configuration class if you haven't.
Consumer declares a Queue:
#RabbitListener(queues = "myQueue")
#Component
public class RabbitListener {
#Bean
public Queue getQueue() {
return new Queue("myQueue");
}
#RabbitHandler
public void getElementFromMyQueue(#Payload Object object) {
// handle object as you want
}
}
After that just #Autowire the RabbitTemplate on the sender and you should be good to go. The queue should remain idle but 'active', even when the host is down
I am using spring boot with RabbitMQ. Everything is working - processing messages works and after losing connection it automatically tries to reconnect. However,
I have only one problem:
When Rabbit server is swtiched off (no possibility to establish connection) and I try to launch spring-boot server it can't start. I can't check now (no access to machine) what exact content of exception is, however it was about problem with instatiation of beans. Can you help me ?
#Configuration
public class RabbitConfig{
private String queueName = "myQueue";
private String echangeName = "myExchange";
#Bean
public FanoutExchange exchange(RabbitAdmin rabbitAdmin) {
FanoutExchange exch = new
FanoutExchange(echangeName);
rabbitAdmin.declareExchange(exch);
return exch;
}
#Bean
public Queue queue(FanoutExchange exchange, RabbitAdmin rabbitAdmin) {
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 20);
args.put("x-dead-letter-exchange", "dlx_exchange_name");
Queue queue = new Queue(queueName, true, false, false, args);
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange));
return queue;
}
}
Edit
I must edit, because I was not aware of fact that is is important here.
In my case the last argument is not null, it is some Hashmap (it is important for me). I edited my code above.
Moreover, I don't understand your answer exactly. Could you be more precisely ?
In order make sure that I was sufficiently clear: I would like to take advantage of automatic reconnection (now it is working). Additionally, If during starting spring boot server rabbit broker is shutdown it should start and cyclically try to reconnect (at this moment application doesn't start).
Edit2
#Configuration
public class RabbitConfig{
private String queueName = "myQueue";
private String echangeName = "myExchange";
#Bean
public FanoutExchange exchange(RabbitAdmin rabbitAdmin) {
FanoutExchange exch = new
FanoutExchange(echangeName);
//rabbitAdmin.declareExchange(exch);
return exch;
}
#Bean
public Queue queue(FanoutExchange exchange, RabbitAdmin rabbitAdmin) {
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 20);
args.put("x-dead-letter-exchange", "dlx_exchange_name");
Queue queue = new Queue(queueName, true, false, false, args);
//rabbitAdmin.declareQueue(queue);
//rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange));
return queue;
}
// EDIT 3: now, we are made to create binding bean
#Autowired
Queue queue; // inject bean by name
#Autowired
Exchange exchange;
#Bean
public Binding binding() {
return BindingBuilder.bind(queue.to(exchange);
}
}
That's correct. You try to register Broker entities manually:
rabbitAdmin.declareExchange(exch);
...
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange));
You should rely here on the built-in auto-declaration mechanism in the Framework.
In other words: you're good to declare those beans (including Bindingm BTW), but you have not to call rabbitAdmin.declare at all. At least not from the bean definition phase.
Update
It was my mistake I forget the ssl debugging running, it is super fast now and working like magic
I have a Spring Boot application that connects to IBM MQ using spring JMS. I realized that the jmsTemplate is super slow compared to not using Spring at all.
I am sure I have something not configured correctly. Hope Someone can help.
I create a connection factory using IBM MQ 8 jar files.
#Bean
public ConnectionFactory connectionFactory() {
properties.getCipherSpec());
MQConnectionFactory factory = new MQConnectionFactory();
try {
factory.setHostName(properties.getHost());
factory.setPort(properties.getPort());
factory.setQueueManager(properties.getQueueManager());
factory.setChannel(properties.getChannel());
factory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
factory.setClientReconnectTimeout(CLIENT_RECONNECT_TIMEOUT);
factory.setClientReconnectOptions(WMQConstants.WMQ_CLIENT_RECONNECT);
if (properties.isEnableSsl()) {
factory.setSSLCipherSuite(properties.getCipherSpec());
factory.setSSLSocketFactory(socketFactory());
}
factory.setUseConnectionPooling(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
return factory;
}
Then I am creating a caching Connection factory and setting Target Connection Factory to the connection factory above.
#Bean(name = "cachingConnectionFactory")
public CachingConnectionFactory cachingConnectionFactory(){
CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setSessionCacheSize(50);
factory.setTargetConnectionFactory(connectionFactory());
factory.setReconnectOnException(true);
factory.afterPropertiesSet();
return factory;
}
and then in the Service class I am using the cache connection factory bean to create JmsTemplate per each thread and use the normal send receive operations.
#Autowired
private CachingConnectionFactory connectionFactory;
#PostConstruct
#DependsOn(value = "cachingConnectionFactory")
public void setJmsConnectionFactory(){
this.jmsQueueTemplate = new JmsTemplate(this.cachingConnectionFactory);
}
Any help will be appreciated ...
I'm having difficulty finding a Spring way to initial an exchange that's sending the incoming message to more then 1 queue - on my Spring-boot application:
I can't find a good way to define a seconds exchange-queue binding.
I'm using RabbitTemplate as the producer client.
The RabbitMQ 6 page tutorial doesn't really help with that since:
the only initial several temporary queues from the Consumer on-demand (while I need to the Producer to do the binding - to persistant queues)
The examples are for basic java usage - not using Spring capabilities.
I also failed to find how to implement it via The spring AMQP pages.
what I got so far, is trying to inject the basic java binding to the spring way of doing it - but it's not working....
#Bean
public ConnectionFactory connectionFactory() throws IOException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
Connection conn = connectionFactory.createConnection();
Channel channel = conn.createChannel(false);
channel.exchangeDeclare(SPRING_BOOT_EXCHANGE, "fanout");
channel.queueBind(queueName, SPRING_BOOT_EXCHANGE, ""); //first bind
channel.queueBind(queueName2, SPRING_BOOT_EXCHANGE, "");// second bind
return connectionFactory;
}
Any help would be appreciated
Edited
I think the problem arise with the fact that every time I restart my server it tries to redefine the exchange-query-binding - while they persist in the broker...
I managed to define them manually via the brokers UI console - so the Producer only aware of the exchange name, and the Consumer only aware to it's relevant queue.
Is there a way to define those element progrematically - but in such a way so it won't be redefined\overwritten if already exist from previous restarts?
We use an approach similar to the following to send data from one specific input channel to several input queues of other consumers:
#Bean
public IntegrationFlow integrationFlow(final RabbitTemplate rabbitTemplate, final AmqpHeaderMapper amqpHeaderMapper) {
IntegrationFlows
.from("some-input-channel")
.handle(Amqp.outboundAdapter(rabbitTemplate)
.headerMapper(headerMapper))
.get()
}
#Bean
public AmqpHeaderMapper amqpHeaderMapper() {
final DefaultAmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper();
headerMapper.setRequestHeaderNames("*");
return headerMapper;
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
return new CachingConnectionFactory();
}
#Bean
public RabbitAdmin rabbitAdmin(final ConnectionFactory rabbitConnectionFactory) {
final RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
rabbitAdmin.afterPropertiesSet();
return rabbitAdmin;
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory rabbitConnectionFactory, final RabbitAdmin rabbitAdmin) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
final FanoutExchange fanoutExchange = new FanoutExchange(MY_FANOUT.getFanoutName());
fanoutExchange.setAdminsThatShouldDeclare(rabbitAdmin);
for (final String queueName : MY_FANOUT.getQueueNames) {
final Queue queue = new Queue(queueName, true);
queue.setAdminsThatShouldDeclare(rabbitAdmin);
final Binding binding = BindingBuilder.bind(queue).to(fanoutExchange);
binding.setAdminsThatShouldDeclare(rabbitAdmin);
}
rabbitTemplate.setExchange(fanoutExchange);
}
and for completeness here's the enum for the fanout declaration:
public enum MyFanout {
MY_FANOUT(Lists.newArrayList("queue1", "queue2"), "my-fanout"),
private final List<String> queueNames;
private final String fanoutName;
MyFanout(final List<String> queueNames, final String fanoutName) {
this.queueNames = requireNonNull(queueNames, "queue must not be null!");
this.fanoutName = requireNonNull(fanoutName, "exchange must not be null!");
}
public List<String> getQueueNames() {
return this.queueNames;
}
public String getFanoutName() {
return this.fanoutName;
}
}
Hope it helps!
Thanks!
That was the answer I was looking for.
also - for the sake of completeness - I found a way to it 'the java way' inside Spring Bean:
#Bean
public ConnectionFactory connectionFactory() throws IOException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
Connection conn = connectionFactory.createConnection();
Channel channel = conn.createChannel(false);
// declare exchnage
AMQP.Exchange.DeclareOk resEx = channel.exchangeDeclare(AmqpTemp.SPRING_BOOT_EXCHANGE_test, ExchangeTypes.FANOUT, true, false, false, null);
// declares queues
AMQP.Queue.DeclareOk resQ = channel.queueDeclare(AmqpTemp.Q2, true, false, false, null);
resQ = channel.queueDeclare(AmqpTemp.Q3, true, false, false, null);
// declare binding
AMQP.Queue.BindOk resB = channel.queueBind(AmqpTemp.Q2, AmqpTemp.SPRING_BOOT_EXCHANGE_test, "");
resB = channel.queueBind(AmqpTemp.Q3, AmqpTemp.SPRING_BOOT_EXCHANGE_test, "");
// channel.queueBind(queueName2, SPRING_BOOT_EXCHANGE, "");
return connectionFactory;
}
The problems I was having before, were do to the fact that I created some queues in my initial play with the code - and when I tried to reuse the same queue names it caused exception since they were initially defined differently - so - lesson learnt: rename the queues from the names you used when you 'played' with the code.
I have a spring application that has to consume messages from some JMS queues. The number of queues has to be configurable, and because of this we have to manually create the consumers by reading a config file. So I can have x queues of type1 and y queues of type2, and all the connection details are specified in this config file.
I would say it is a rather complicated code, and I need to point out the following facts: I manually create the spring DefaultMessageListenerContainer and call start and stop on it, the transaction manager is distributed between the JMS and JDBC resources. Also, the application runs on WebLogic and the JMS queues are in WebLogic too.
The flow is that the app reads messages from the queues, tries to put the messages into the database, but if the database is down, the transaction (shared between both JMS and JDBC) is rolled back, so the messages is put back into the queue - this is the failover mechanism when database is down.
The issue that I am experiencing is that when I stop the application while it performs the failover mechanism, there are some JMS consumer threads that are not stopped. This way I get to leak threads and overload the system.
So my question is how can I make sure that when the application stops, it stops all the consumer threads? Calling stop on the message listener container doesn't seem to do the job.
Below are some code snippets:
config:
[
{
"factoryInitial": "weblogic.jndi.WLInitialContextFactory",
"providerUrl": "t3://localhost:7001",
"securityPrincipal": "user",
"securityCredentials": "password",
"connectionFactory": "jms/QCF",
"channels": {
"type1": "jms/queue1"
}
}
]
java:
public class JmsConfig {
private Map<String, List<DefaultMessageListenerContainer>> channels = new HashMap<>();
private Map<String, MessageListener> messageConsumers;
private PlatformTransactionManager transactionManager;
public JmsConfig(Map<String, MessageListener> messageConsumers, PlatformTransactionManager transactionManager) throws Exception {
this.messageConsumers = messageConsumers;
this.transactionManager = transactionManager;
List<JmsServerConfiguration> serverConfigurationList = readJsonFile();
for (JmsServerConfiguration jmsServerConfiguration : serverConfigurationList) {
Properties environment = createEnvironment(jmsServerConfiguration);
JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(environment);
ConnectionFactory connectionFactory = createConnectionFactory(jndiTemplate, jmsServerConfiguration);
populateMessageListenerContainers(jmsServerConfiguration, jndiTemplate, connectionFactory);
}
}
#PreDestroy
public void stopListenerContainers() {
for (Map.Entry<String, List<DefaultMessageListenerContainer>> channel : channels.entrySet()) {
for (DefaultMessageListenerContainer listenerContainer : channel.getValue()) {
listenerContainer.stop();
}
}
}
private void populateMessageListenerContainers(
JmsServerConfiguration jmsServerConfiguration,
JndiTemplate jndiTemplate, ConnectionFactory connectionFactory) throws Exception {
Set<Map.Entry<String, String>> channelsEntry = jmsServerConfiguration.getChannels().entrySet();
for (Map.Entry<String, String> channel : channelsEntry) {
Destination destination = createDestination(jndiTemplate, channel.getValue());
DefaultMessageListenerContainer listenerContainer =
createListenerContainer(connectionFactory, destination, messageConsumers.get(channel.getKey()));
if (!channels.containsKey(channel.getKey())) {
channels.put(channel.getKey(),
new ArrayList<DefaultMessageListenerContainer>());
}
channels.get(channel.getKey()).add(listenerContainer);
}
}
private Properties createEnvironment(JmsServerConfiguration jmsServerConfiguration) {
Properties properties = new Properties();
properties.setProperty("java.naming.factory.initial", jmsServerConfiguration.getFactoryInitial());
properties.setProperty("java.naming.provider.url", jmsServerConfiguration.getProviderUrl());
properties.setProperty("java.naming.security.principal", jmsServerConfiguration.getSecurityPrincipal());
properties.setProperty("java.naming.security.credentials", jmsServerConfiguration.getSecurityCredentials());
return properties;
}
private ConnectionFactory createConnectionFactory(JndiTemplate jndiTemplate,
JmsServerConfiguration jmsServerConfiguration) throws Exception {
JndiObjectFactoryBean connectionFactory = new JndiObjectFactoryBean();
connectionFactory.setJndiTemplate(jndiTemplate);
connectionFactory.setJndiName(jmsServerConfiguration.getConnectionFactory());
connectionFactory.setExpectedType(ConnectionFactory.class);
connectionFactory.afterPropertiesSet();
return (ConnectionFactory) connectionFactory.getObject();
}
private Destination createDestination(JndiTemplate jndiTemplate, String jndiName) throws Exception {
JndiObjectFactoryBean destinationFactory = new JndiObjectFactoryBean();
destinationFactory.setJndiTemplate(jndiTemplate);
destinationFactory.setJndiName(jndiName);
destinationFactory.setExpectedType(Destination.class);
destinationFactory.afterPropertiesSet();
return (Destination) destinationFactory.getObject();
}
private DefaultMessageListenerContainer createListenerContainer(
ConnectionFactory connectionFactory, Destination destination,
MessageListener messageListener) {
DefaultMessageListenerContainer listenerContainer = new DefaultMessageListenerContainer();
listenerContainer.setConcurrentConsumers(3);
listenerContainer.setConnectionFactory(connectionFactory);
listenerContainer.setDestination(destination);
listenerContainer.setMessageListener(messageListener);
listenerContainer.setTransactionManager(transactionManager);
listenerContainer.setSessionTransacted(true);
listenerContainer.afterPropertiesSet();
listenerContainer.start();
return listenerContainer;
}
}
So the issue was solved by calling listenerContainer.shutdown(); instead of stop().