Spring integration tcp gateway can be setup as follows:
<!-- Server side -->
<int-ip:tcp-connection-factory id="crLfServer"
type="server"
port="${availableServerSocket}"/>
<int-ip:tcp-inbound-gateway id="gatewayCrLf"
connection-factory="crLfServer"
request-channel="serverBytes2StringChannel"
error-channel="errorChannel"
reply-timeout="10000" />
<int:channel id="toSA" />
<int:service-activator input-channel="toSA"
ref="echoService"
method="test"/>
<bean id="echoService"
class="org.springframework.integration.samples.tcpclientserver.EchoService" />
<int:object-to-string-transformer id="serverBytes2String"
input-channel="serverBytes2StringChannel"
output-channel="toSA"/>
<int:transformer id="errorHandler"
input-channel="errorChannel"
expression="Error processing payload"/>
Notice the reply-timeout which is set as 10 seconds.
Does it mean that the TCP server will call the service and can wait for a maximum of 10 seconds? If the service does not reply within 10 seconds, Does the TCP server will send the message to errorChannel which in turn sends the client error message "Error processing payload"?
When I tested the TCP Server with a service that takes 20 seconds, client is taking 20 seconds to get the response. I am not seeing error message.
Can you please help in understanding the reply-timeout in TCP inbound-gateway?
Thanks
UPDATE:
Thanks for Artem to help out with this issue.
Best way to solve this problem is with the following config:
<beans>
<int-ip:tcp-connection-factory id="crLfServer" type="server" port="${availableServerSocket}"/>
<int-ip:tcp-inbound-gateway id="gatewayCrLf" connection-factory="crLfServer" request-channel="requestChannel" error-channel="errorChannel" reply-timeout="5000" />
<int:service-activator input-channel="requestChannel" ref="gateway" requires-reply="true"/>
<int:gateway id="gateway" default-request-channel="timeoutChannel" default-reply-timeout="5000" />
<int:object-to-string-transformer id="serverBytes2String" input-channel="timeoutChannel" output-channel="serviceChannel"/>
<int:channel id="timeoutChannel">
<int:dispatcher task-executor="executor"/>
</int:channel>
<bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
</bean>
<int:service-activator input-channel="serviceChannel" ref="echoService" method="test"/>
<bean id="echoService" class="org.springframework.integration.samples.tcpclientserver.EchoService" />
<int:transformer id="errorHandler" input-channel="errorChannel" expression="payload.failedMessage.payload + ' errorHandleMsg: may be timeout error'"/>
</beans>
Thanks
Well, actually we should on that attribute a description like we have in other similar places, e.g. HTTP Inbound Gateway:
<xsd:attribute name="reply-timeout" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Used to set the receiveTimeout on the underlying MessagingTemplate instance
(org.springframework.integration.core.MessagingTemplate) for receiving messages
from the reply channel. If not specified this property will default to "1000"
(1 second).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
That timeout means how much to wait for reply from downstream flow. But! That is possible if you flow is shifted to another thread somewhere. Otherwise everything is performed in the caller's Thread and therefore the wait time isn't deterministic.
Anyway we return null there after timeout without reply. And it is reflected in the TcpInboundGateway:
Message<?> reply = this.sendAndReceiveMessage(message);
if (reply == null) {
if (logger.isDebugEnabled()) {
logger.debug("null reply received for " + message + " nothing to send");
}
return false;
}
We can reconsider a logic in the TcpInboundGateway for :
if (reply == null && this.errorOnTimeout) {
if (object instanceof Message) {
error = new MessageTimeoutException((Message<?>) object, "No reply received within timeout");
}
else {
error = new MessageTimeoutException("No reply received within timeout");
}
}
But seems for me it really would be better on to rely on the timeout from the client.
UPDATE
I think we can overcome the limitation and meet you requirements with the midflow <gateway>:
<gateway id="gateway" default-request-channel="timeoutChannel" default-reply-timeout="10000"/>
<channel id="timeoutChannel">
<dispatcher task-executor="executor"/>
</channel>
<service-activator input-channel="requestChannel"
ref="gateway"
requires-reply="true"/>
So, the <service-activator> calls <gateway> and waits for reply from there. Requiring the last one, of course, to end up with the ReplyRequiredException, which you can convert into desired MessageTimeoutException in your error flow on the error-channel="errorChannel".
The timeoutChannel is an executor one, making our default-reply-timeout="10000" very useful because we shift a message on the gateway into separate thread immediately and move right from there into reply waiting process wrapped with that timeout on the CountDonwLatch.
Hope that is clear.
Related
I am trying to build a spring integration application, which has the following configuration (the culprit seems to be the channel xsltSpecific) :
<beans:beans>
<channel id="channel1"></channel>
<channel id="channel2"></channel>
<channel id="xsltSpecific"></channel>
<channel id="xsltSpecificDelayed"></channel>
<channel id="xsltCommon"></channel>
<channel id="irdSpecificUnmarshallerChannel"></channel>
<channel id="irdSpecificInputChannel"></channel>
<file:outbound-channel-adapter
directory="${dml.ird.directory}" channel="channel1"
auto-create-directory="true" filename-generator="timestampedFileNameGenerator">
</file:outbound-channel-adapter>
<recipient-list-router input-channel="fileChannel">
<recipient channel="channel1" selector-expression="${dml.data.logs.enable}" />
<recipient channel="channel2" />
</recipient-list-router>
<recipient-list-router input-channel="channel2">
<recipient channel="xsltSpecificDelayed"></recipient>
<recipient channel="xsltCommon"></recipient>
</recipient-list-router>
<delayer id="specificDelayer" input-channel="xsltSpecificDelayed" default-delay="5000" output-channel="xsltSpecific"/>
<jms:message-driven-channel-adapter
id="jmsInboundAdapterIrd" destination="jmsInputQueue" channel="fileChannel"
acknowledge="transacted" transaction-manager="transactionManager"
error-channel="errorChannel" client-id="${ibm.jms.connection.factory.client.id}"
subscription-durable="true" durable-subscription-name="${ibm.jms.subscription.id1}" />
<si-xml:xslt-transformer input-channel="xsltCommon" output-channel="jmsInputChannel"
xsl-resource="classpath:summit-hub-to-cpm-mapping.xsl" result-transformer="resultTransformer" >
</si-xml:xslt-transformer>
<si-xml:xslt-transformer input-channel="xsltSpecific" output-channel="irdSpecificUnmarshallerChannel"
xsl-resource="classpath:summit-hub-specific.xsl" result-transformer="resultTransformer" >
</si-xml:xslt-transformer>
<si-xml:unmarshalling-transformer id="irdUnmarshaller"
unmarshaller="irdUnmarshallerDelegate" input-channel="irdSpecificUnmarshallerChannel"
output-channel="saveSpecificTradeChannel" />
<beans:bean id="irdUnmarshallerDelegate"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<beans:property name="schema"
value="summit-hub-specific.xsd" />
<beans:property name="contextPath"
value="com.h.i.c.d.i.mapping" />
</beans:bean>
<beans:bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer" />
<service-activator ref="specificTradeService" input-channel="saveSpecificTradeChannel"
requires-reply="false" method="save"/>
<file:inbound-channel-adapter directory="${dml.retry.directoryForIrd}"
channel="fileChannelAfterRetry" auto-create-directory="true"
prevent-duplicates="false" filename-regex=".*\.(msg|xml)" queue-size="50" >
<poller fixed-delay="${dml.retry.delay}" max-messages-per-poll="50">
<transactional transaction-manager="transactionManager" />
</poller>
</file:inbound-channel-adapter>
<channel id="fileChannel"/>
<channel id="fileChannelAfterRetry"/>
<file:file-to-string-transformer
input-channel="fileChannelAfterRetry" output-channel="fileChannel"
delete-files="true" />
<beans:import resource="classpath:cpm-dml-common-main.xml" />
</beans:beans>
But I am having the following exception :
org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'org.springframework.context.support.GenericApplicationContext#6950e31.xsltSpecific'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
What does this exception mean ?
Also, I am not able to spot the problem, can you help me fix this issue ?
UPDATE
Sorry, I didn't give the whole context earlier, because I didn't think it was relevant.
The exception arises during a test derived from AbstractTransactionalJUnit4SpringContextTests, which closed the application context at the end of the test, before the message had a chance to get to the end.
I've added a Thread.sleep(10000) at the end of the test, and the exception doesn't happen anymore.
The xsltSpecific is just a default DirectChannel with a UnicastingDispatcher to deliver messages to channel's subscribers.
According your configuration you send a message to this channel from the:
<delayer id="specificDelayer" input-channel="xsltSpecificDelayed" default-delay="5000" output-channel="xsltSpecific"/>
And also it looks like you really have a subscriber to this channel:
<si-xml:xslt-transformer input-channel="xsltSpecific" output-channel="irdSpecificUnmarshallerChannel"
xsl-resource="classpath:summit-hub-specific.xsl" result-transformer="resultTransformer" >
</si-xml:xslt-transformer>
What is really not clear when this defined subscriber is lost. It doesn't look like you have an auto-startup="false" on this endpoint, but on the other hand maybe you really stop it at runtime...
Would you mind to share more stack trace on the matter? I want to see who is an original caller for that lost message.
Configuration details :
<int:publish-subscribe-channel id="toKafka"/>
<int:publish-subscribe-channel id="sendMessageToKafkaChannel"/>
<int:service-activator input-channel="toKafka" output-channel="sendMessageToKafkaChannel" order="1" ref="conditionalProducerService" method="producerCircuitBreaker">
<int:request-handler-advice-chain>
<ref bean="circuitBreakerAdvice" />
</int:request-handler-advice-chain>
</int:service-activator>
<int-kafka:outbound-channel-adapter id="kafkaOutboundChannelAdapter" kafka-producer-context-ref="producerContext"
auto-startup="true" channel="toKafka" message-key="kafka_messageKey"/>
<bean id="circuitBreakerAdvice" class="org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice">
<property name="threshold" value="2"/>
<property name="halfOpenAfter" value="15000" />
</bean>
public Message<?> producerCircuitBreaker(Message<?> payload) {
throw new RuntimeException("foo Pro");
}
for(int i=0;i<4;i++){
toKafka.send(MessageBuilder
.withPayload(messageVO.getMessageContentVO())
.setHeader(KafkaHeaders.TOPIC, topic)
.setHeader(KafkaHeaders.PARTITION_ID,Integer.parseInt(messageVO.getPartition())).
build());
APPLOGGER.info("sending message");
}
Expecting to get the process to fail 2 times with exception and then "circuit breaker open" exception but it is simply stopping after throwing the below exception in the console.
Also how can we configure error-channel here.
https://gist.github.com/anonymous/67aae50e548c78470cd0
updated config:
<int:service-activator input-channel="toKafka" ref="gw">
<int:request-handler-advice-chain> <ref bean="circuitBreakerAdvice"/>
</int:request-handler-advice-chain>
</int:service-activator>
<int:channel id="failedChannel1" />
<int:gateway id="gw" default-request-channel="toKafka" default-reply-timeout="0" error-channel="failedChannel1" />
<int:chain input-channel="failedChannel1">
<int:transformer expression="'failed:'+payload.failedMessage.payload+ ' with a' +payload.cause.message" />
<int-stream:stderr-channel-adapter append-newline="true"/>
</int:chain>
getting below exception.
failed:TestVo[data=sample message]] with Cannot process message.
https://gist.github.com/anonymous/921be7691c41d125dc84
however it is working with same message otherwise.(message content changed intentionally)
Also tried putting invalid value for the producer context: eg. broker- list/value-class-type as invalid class type than expected as below.
getting below error but expecting to get CB to come into picture and message should flow to the error channel.
in case of value-class-type : CB not invoked however message flowing to the error channel but there are many message are coming for 1 message published.
failed:TestVo [data={tes message}}] with No converter found capable of converting from type xx.xxx.vo.TestVo to type java.lang.String
these is occuring in the console many times.
in case of broker-list : it is simply throwing exception in the console.
https://gist.github.com/anonymous/6ece517fb5e82ac73492
Expected : CB to get invoked and message flow to the error channel in all cases.
<int-kafka:outbound-channel-adapter id="kafkaOutboundChannelAdapter" kafka-producer-context-ref="producerContext"
auto-startup="true" channel="toKafka" message-key="kafka_messageKey"/>
<int-kafka:producer-context id="producerContext" producer-properties="producerProperties">
<int-kafka:producer-configurations>
<int-kafka:producer-configuration
broker-list="1.2.3:9092" topic="headers['topic']" key-class-type="java.lang.String"
value-class-type="java.lang.String"
value-encoder="kafkaEncoder" key-encoder="kafkaKeyEncoder"
compression-type="none" />
</int-kafka:producer-configurations>
</int-kafka:producer-context>
With that code, you need try {...} around the send().
The first two attempts will catch your RuntimeException; the next will catch the circuit breaker exception.
Use a Messaging Gateway with an error channel instead of sending to the channel directly.
EDIT
This code...
<int:service-activator input-channel="toKafka" ref="gw">
<int:request-handler-advice-chain> <ref bean="circuitBreakerAdvice"/>
</int:request-handler-advice-chain>
</int:service-activator>
<int:gateway id="gw" default-request-channel="toKafka" default-reply-timeout="0" error-channel="failedChannel1" />
When you send a message to toKafka, the gateway will be invoked which will send the message to toKafka in a loop.
It will cause a stack overflow.
I would like to apply an Interceptor on the reply-channel of an http:inbound-gateway to save some event related data to a table. The flow continues in a chain which then goes to a header-value-router. As an example let's take a service-activator at the end of this flow, where the output-channel is not specified. In this case, the replyChannel header holds a TemporaryReplyChannel object (anonymous reply channel) instead of the gateway's reply-channel. This way the Interceptor is never called.
Is there a way to "force" the usage of the specified reply-channel? The Spring document states that
by defining a default-reply-channel you can point to a channel of your choosing, which in this case would be a publish-subscribe-channel. The Gateway would create a bridge from it to the temporary, anonymous reply channel that is stored in the header.
I've tried using a publish-subscribe-channel as reply-channel, but it didn't make any difference. Maybe I misunderstood the article...
Inside my chain I've also experimented with a header-enricher. I wanted to overwrite the value of the replyChannel with the id of the channel I want to intercept (submit.reply.channel). While debugging I was able to see "submit.reply.channel" in the header, but then I got an exception java.lang.NoClassDefFoundError: org/springframework/transaction/interceptor/NoRollbackRuleAttribute and stopped trying ;-)
Code snippets
<int-http:inbound-gateway id="submitHttpGateway"
request-channel="submit.request.channel" reply-channel="submit.reply.channel" path="/submit" supported-methods="GET">
<int-http:header name="requestAttributes" expression="#requestAttributes" />
<int-http:header name="requestParametersMap" expression="#requestParams" />
</int-http:inbound-gateway>
<int:channel id="submit.request.channel" />
<int:publish-subscribe-channel id="submit.reply.channel">
<int:interceptors>
<int:ref bean="replyChannelInterceptor" />
</int:interceptors>
</int:publish-subscribe-channel>
Thanks in advance for your help!
The only "easy" way is to explicitly send the reply via the output-channel on the last endpoint.
In fact, all that happens when you send to a declared channel is the reply channel is simply bridged to the replyChannel header.
You could do it by saving off the replyChannel header in another header, set the replyChannel header to some other channel (which you can intercept); then restore the replyChannel header to the saved-off channel before the reply is returned to the gateway.
EDIT:
Sample config...
<int:channel id="in" />
<int:header-enricher input-channel="in" output-channel="next">
<int:header name="origReplyChannel" expression="headers['replyChannel']"/>
<int:reply-channel ref="myReplies" overwrite="true" />
</int:header-enricher>
<int:router input-channel="next" expression="payload.equals('foo')">
<int:mapping value="true" channel="channel1" />
<int:mapping value="false" channel="channel2" />
</int:router>
<int:transformer input-channel="channel1" expression="payload.toUpperCase()" />
<int:transformer input-channel="channel2" expression="payload + payload" />
<int:channel id="myReplies" />
<!-- restore the reply channel -->
<int:header-enricher input-channel="myReplies" output-channel="tapped">
<int:reply-channel expression="headers['origReplyChannel']" overwrite="true" />
</int:header-enricher>
<int:channel id="tapped">
<int:interceptors>
<int:wire-tap channel="loggingChannel" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="loggingChannel" log-full-message="true" logger-name="tapInbound"
level="INFO" />
<!-- route reply -->
<int:bridge id="bridgeToNowhere" input-channel="tapped" />
Test:
MessageChannel channel = context.getBean("in", MessageChannel.class);
MessagingTemplate template = new MessagingTemplate(channel);
String reply = template.convertSendAndReceive("foo", String.class);
System.out.println(reply);
reply = template.convertSendAndReceive("bar", String.class);
System.out.println(reply); }
Result:
09:36:30.224 INFO [main][tapInbound] GenericMessage [payload=FOO, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, id=326a610f-80c6-5b74-0158-e3644b732aab, origReplyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#fba92d3, timestamp=1442496990223}]
FOO
09:36:30.227 INFO [main][tapInbound] GenericMessage [payload=barbar, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, id=d161917c-ca73-a5a9-d0f1-d7a4346a459e, origReplyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#662b4c69, timestamp=1442496990227}]
barbar
Can I send distinct messages (distinct serialize/deserialize) to the same tcp server (same host and port) and differentiate the tcp-inbound-gateway by some value of header or payload with a router???
(...)
I want to add a router for select the correct tcp-inbound-gateway depends on some field in header or payload (for example named typedata). In what order would enter the request (or message) between router, tcp-inbound-gateway, tcp-connection-factory, serialize/deserialize? Will I have problems with the serialization/deserialization for chose the tcp-inbound-gateway? What is the correct way to do this?
Thanks in advance.
EDIT
In server:
<!-- Common -->
<int:channel id="channelConnectionFactoryRequest" />
<int:channel id="channelConnectionFactoryResponse" />
<router method="getDestinationChannel"
input-channel="channelSConnectionFactoryRequest">
<beans:bean class="org.mbracero.integration.CustomRouter" />
</router>
<beans:bean id="customSerializerDeserializer"
class="org.mbracero.integration.serialization.CustomSerializerDeserializer" />
<int-ip:tcp-connection-factory id="customConnectionFactory"
type="server" port="${payment.port}" single-use="true"
serializer="customSerializerDeserializer"
deserializer="customSerializerDeserializer" />
<int-ip:tcp-inbound-gateway id="customInboundGateway"
connection-factory="customConnectionFactory"
request-channel="channelCustomConnectionFactoryRequest"
reply-channel="channelCustomConnectionFactoryResponse"
error-channel="errorChannel" />
<!-- Custom -->
<beans:bean id="operations"
class="org.mbracero.integration.applepay.impl.OperationsImpl" />
<!-- Operation One -->
<int:channel id="operationOneRequest" />
<int:service-activator input-channel="operationOneRequest"
output-channel="operationOneResponse" ref="operations" method="getOperationOne" />
<!-- Operation Two -->
<int:channel id="operationTwoRequest" />
<int:service-activator input-channel="operationTwoRequest"
output-channel="operationTwoResponse" ref="operations" method="getOperationTwo" />
OperationsImpl:
ResultOne getOperationOne(RequestOne request);
ResultTwo getOperationOne(RequestTwo request);
ResultOne & ResultTwo implements ResultBase. And in serialize of customSerializerDeserializer I have:
#Override
public void serialize(ResultBase arg0, OutputStream arg1) throws IOException {
byte[] xxx = XXX.getBytes();
arg1.write(xxx);
byte[] yyy = yyy.getBytes();
arg1.write(senderName);
// **Each custom object have a method for serialize their own data**
arg0.transformDataToByte(arg1);
arg1.flush();
}
In client:
<gateway id="tmGateway"
service-interface="org.mbracero.integration.CustomGateway" />
<beans:bean id="operationOneSerializerDeserializer"
class="org.mbracero.integration.serialization.OperationOneSerializerDeserializer" />
<int-ip:tcp-connection-factory id="operationOneFactory"
type="client" host="127.0.0.1" port="7878" single-use="true"
serializer="operationOneSerializerDeserializer" deserializer="operationOneSerializerDeserializer" />
<int-ip:tcp-outbound-gateway id="operationOneOutGateway"
request-channel="operationOneChannel" connection-factory="operationOneFactory"
request-timeout="5000" reply-timeout="5000" remote-timeout="5000" />
<beans:bean id="operationTwoSerializerDeserializer"
class="org.mbracero.integration.operationTwoRequestSerializerDeserializer"/>
<int-ip:tcp-connection-factory id="operationTwoFactory"
type="client" host="127.0.0.1" port="7878" single-use="true"
serializer="operationTwoSerializerDeserializer"
deserializer="operationTwoSerializerDeserializer" />
<int-ip:tcp-outbound-gateway id="operationTwoOutGateway"
request-channel="operationTwoChannel" connection-factory="operationTwoFactory"
request-timeout="50000" reply-timeout="50000" remote-timeout="50000" />
CustomGateway:
#Gateway(requestChannel="operationOneChannel")
OperationOneResponse sendOperationOne(OperationOneRequest request);
#Gateway(requestChannel="operationTwoChannel")
OperationTwoResponse sendOperationTwo(OperationTwo request);
You cannot have 2 server connection factories listening on the same port. TCP doesn't allow it - the network stack wouldn't know which server socket to route the request to.
There's no problem on the client side but, with a single socket, the server would have to understand how to deserialize both data types.
It's probably easier to combine the serializers/deserializers into one on both sides (add another header to the message so the deserializer knows what type of payload to decode).
This is my current spring amqp configuration
<rabbit:connection-factory id="rabbitConnectionFactory"
port="${rabbitmq.port}" host="${rabbitmq.host}" username="${rabbitmq.username}" password="${rabbitmq.password}"/>
<rabbit:admin id="rabbitmqAdmin" connection-factory="rabbitConnectionFactory" />
<rabbit:template id="importAmqpTemplate"
connection-factory="rabbitConnectionFactory">
</rabbit:template>
and this is my exchanges, queues, listeners, replyQueues, replyHandlers configuration
<rabbit:queue name="${process1.queue}" />
<rabbit:queue name="${process1.reply.queue}" />
<rabbit:queue name="${process2.queue}" />
<rabbit:queue name="${process2.reply.queue}" />
<rabbit:direct-exchange name="${myExchange}">
<rabbit:bindings>
<rabbit:binding queue="${process1.queue}"
key="${process1.routing.key}" />
<rabbit:binding queue="${process2.queue}"
key="${process2.routing.key}" />
</rabbit:bindings>
</rabbit:direct-exchange>
<rabbit:listener-container
connection-factory="rabbitConnectionFactory" concurrency="${my.listener.concurrency}"
requeue-rejected="false">
<rabbit:listener queues="${process1.queue}"
ref="foundation" method="process1" />
<rabbit:listener queues="${process2.queue}"
ref="foundation" method="process2s" />
</rabbit:listener-container>
<beans:beans profile="master">
<beans:bean id="process1Lbq" class="java.util.concurrent.LinkedBlockingQueue" />
<beans:bean id="process2Lbq" class="java.util.concurrent.LinkedBlockingQueue" />
<beans:bean id="process1sReplyHandler"
class="com.stockopedia.batch.foundation.ReplyHandler"
p:blockingQueue-ref="process1Lbq" />
<beans:bean id="process2ReplyHandler"
class="com.stockopedia.batch.foundation.ReplyHandler"
p:blockingQueue-ref="process2Lbq" />
<rabbit:listener-container
connection-factory="rabbitConnectionFactory" concurrency="1"
requeue-rejected="false">
<rabbit:listener queues="${process1.reply.queue}"
ref="process1sHandler" method="onMessage" />
<rabbit:listener queues="${process2.reply.queue}"
ref="process2ReplyHandler" method="onMessage" />
</rabbit:listener-container>
</beans:beans>
I have set this up on 6 different servers, and queuing up messages from master servers only. Other servers are only processing messages. All servers has same number of listeners running as set by concurrency.
The problem is, messages takes different time to process. Some messages take long time. So currently some of the servers do not pick up messages from queues even all listeners on those servers are done with processing there messages.
I can see the pending messages in queue to be processed and some servers just sitting idle. I want those server to pick up remaining messages while other servers are busy in processing their messages.
Do I need to set basic_Quos as mentioned in tutorial http://www.rabbitmq.com/tutorials/tutorial-two-java.html (Fair Dispatch) ?
int prefetchCount = 1;
channel.basicQos(prefetchCount);
or is it default for spring ampq ? If not how do i do it ?
basicQos(1) is the default setting for the listener container; it can be changed by setting prefetch on the container.
I can see the pending messages in queue to be processed and some servers just sitting idle.
You shouldn't see messages just sitting in the queue if you have idle consumers. If messages are marked as un-acked, they are being processed.
If you turn on DEBUG level logging, you will be able to see idle consumers polling an internal queue for new deliveries.