Timeout of basicPublish when server is outofspace - rabbitmq

My case is rabbitmq server got out of space, just as below
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/ramonubuntu--vg-root 6299376 5956336 0 100% /
The producer publishes message to server(the message needs to be persisted), and then will be blocked forever, it will keeping waiting the response of publishing. Sure we should avoid the situation of server out of space, but is there any timeout mechanism to let producer quit the waiting?
I have tried heartbeat and SO_TIMEOUT, they both don't work, as the network works fine. Below is my producer.
protected void publish(byte[] message) throws Exception {
// ConnectionFactory can be reused between threads.
ConnectionFactory factory = new SoTimeoutConnectionFactory();
factory.setHost(this.getHost());
factory.setVirtualHost("te");
factory.setPort(5672);
factory.setUsername("amqp");
factory.setPassword("amqp");
factory.setConnectionTimeout(10 * 1000);
// doesn't help if server got out of space
factory.setRequestedHeartbeat(1);
final Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// declare a 'topic' type of exchange
channel.exchangeDeclare(this.exchangeName, "topic", true);
channel.addReturnListener(new ReturnListener() {
#Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey,
AMQP.BasicProperties properties, byte[] body) throws IOException {
logger.warn("[X]Returned message(replyCode:" + replyCode + ",replyText:" + replyText
+ ",exchange:" + exchange + ",routingKey:" + routingKey + ",body:" + new String(body));
}
});
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
#Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
logger.info("Ack: " + deliveryTag);
// RabbitMessagePublishMain.this.release(connection);
}
#Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
logger.info("Nack: " + deliveryTag);
// RabbitMessagePublishMain.this.release(connection);
}
});
channel.basicPublish(this.exchangeName, RabbitMessageConsumerMain.EXCHANGE_NAME + ".-1", true,
MessageProperties.PERSISTENT_BASIC, message);
channel.waitForConfirmsOrDie(10*1000);
// now we can close connection
connection.close();
}
It will block at 'channel.waitForConfirmsOrDie(10*1000);', and the SotimeoutConnectionFactory,
public class SoTimeoutConnectionFactory extends ConnectionFactory {
#Override
protected void configureSocket(Socket socket) throws IOException {
super.configureSocket(socket);
socket.setSoTimeout(10 * 1000);
}
}
Also I captured the network between producer and rabbimq,
Please help.

You need to implement Connection Block/Unblocked.
This is basically a way of notifying the publisher that the server is running out of resources. The advantage with this is that the publisher will also be notified once it is safe to publish again.
I would recommend that you take a look at this article. A simple way of implementing this is to have a flag that indicates if it is safe to publish, if it is not wait until it is.
As an example you can take a look on how I implemented this in one of my Python examples.

Related

RabbitMQ Camel Consumer - Consume a single message

I have a scenario where I want to "pull" messages of a RabbitMQ queue/topic and process them one at a time.
Specifically if there are already messages sitting on the queue when the consumer starts up.
I have tried the following with no success (meaning, each of these options reads the queue until it is either empty or until another thread closes the context).
1.Stopping route immediately it is first processed
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
RouteDefinition route = from("rabbitmq:harley?queue=IN&declare=false&autoDelete=false&hostname=localhost&portNumber=5672");
route.process(new Processor() {
Thread stopThread;
#Override
public void process(final Exchange exchange) throws Exception {
String name = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
String body = exchange.getIn().getBody(String.class);
// Doo some stuff
routeComplete[0] = true;
if (stopThread == null) {
stopThread = new Thread() {
#Override
public void run() {
try {
((DefaultCamelContext)exchange.getContext()).stopRoute("RabbitRoute");
} catch (Exception e) {}
}
};
}
stopThread.start();
}
});
}
});
context.start();
while(!routeComplete[0].booleanValue())
Thread.sleep(100);
context.stop();
}
Similar to 1 but using a latch rather than a while loop and sleep.
Using a PollingConsumer
final CamelContext context = new DefaultCamelContext();
context.start();
Endpoint re = context.getEndpoint(srcRoute);
re.start();
try {
PollingConsumer consumer = re.createPollingConsumer();
consumer.start();
Exchange exchange = consumer.receive();
String bb = exchange.getIn().getBody(String.class);
consumer.stop();
} catch(Exception e){
String mm = e.getMessage();
}
Using a ConsumerTemplate() - code similar to above.
I have also tried enabling preFetch and setting the max number of exchanges to 1.
None of these appear to work, if there are 3 messages on the queue, all are read before I am able to stop the route.
If I were to use the standard RabbitMQ Java API I would use a basicGet() call which lets me read a single message, but for other reasons I would prefer to use a Camel consumer.
Has anyone successfully been able to process a single message on a queue that holds multiple messages using a Camel RabbitMQ Consumer?
Thanks.
This is not the primary intention of the component as its for continued received. But I have created a ticket to look into supporting a basicGet (single receive). There is a new spring based rabbitmq component coming in 3.8 onwards so its going to be implemeneted there (first): https://issues.apache.org/jira/browse/CAMEL-16048

RabbitMQ queue declare never ends

I'm just trying to make a simple test for RabbitMQ, and I have Erlang installed as well as RabbitMQ running.
My receiver:
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
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");
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope,
BasicProperties properties, byte[] body) throws IOException
{
// TODO Auto-generated method stub
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}
It never prints out the first sysout, because it gets stuck declaring the queue on "channel.queueDeclare" line.
Rabbit log says it is accepting AMQP connection and user guest gets authenticated and granted access to vhost.
Any help would be appreciated.
I just copied/pasted your code with no problems...
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'foo'
I suggest you enable the management plugin and explore the admin UI.
Why did you add the spring-amqp spring-rabbitmq tags since this question has nothing to do with Spring and you are using the native client directly?

What is the use case of BrokerService in ActiveMQ and how to use it correctly

I am new about ActiveMQ. I'm trying to study and check how it works by checking the example code provided by Apache at this link:-
http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html
public class Server implements MessageListener {
private static int ackMode;
private static String messageQueueName;
private static String messageBrokerUrl;
private Session session;
private boolean transacted = false;
private MessageProducer replyProducer;
private MessageProtocol messageProtocol;
static {
messageBrokerUrl = "tcp://localhost:61616";
messageQueueName = "client.messages";
ackMode = Session.AUTO_ACKNOWLEDGE;
}
public Server() {
try {
//This message broker is embedded
BrokerService broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
broker.addConnector(messageBrokerUrl);
broker.start();
} catch (Exception e) {
System.out.println("Exception: "+e.getMessage());
//Handle the exception appropriately
}
//Delegating the handling of messages to another class, instantiate it before setting up JMS so it
//is ready to handle messages
this.messageProtocol = new MessageProtocol();
this.setupMessageQueueConsumer();
}
private void setupMessageQueueConsumer() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(messageBrokerUrl);
Connection connection;
try {
connection = connectionFactory.createConnection();
connection.start();
this.session = connection.createSession(this.transacted, ackMode);
Destination adminQueue = this.session.createQueue(messageQueueName);
//Setup a message producer to respond to messages from clients, we will get the destination
//to send to from the JMSReplyTo header field from a Message
this.replyProducer = this.session.createProducer(null);
this.replyProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//Set up a consumer to consume messages off of the admin queue
MessageConsumer consumer = this.session.createConsumer(adminQueue);
consumer.setMessageListener(this);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public void onMessage(Message message) {
try {
TextMessage response = this.session.createTextMessage();
if (message instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) message;
String messageText = txtMsg.getText();
response.setText(this.messageProtocol.handleProtocolMessage(messageText));
}
//Set the correlation ID from the received message to be the correlation id of the response message
//this lets the client identify which message this is a response to if it has more than
//one outstanding message to the server
response.setJMSCorrelationID(message.getJMSCorrelationID());
//Send the response to the Destination specified by the JMSReplyTo field of the received message,
//this is presumably a temporary queue created by the client
this.replyProducer.send(message.getJMSReplyTo(), response);
} catch (JMSException e) {
System.out.println("Exception: "+e.getMessage());
}
}
public static void main(String[] args) {
new Server();
}
}
My confusion about the messageBrokerUrl = "tcp://localhost:61616"; You know ActiveMQ service is running on port 61616 by default. Why does this example chooses same port. If I try to run the code thows eception as:
Exception: Failed to bind to server socket: tcp://localhost:61616 due to: java.net.BindException: Address already in use: JVM_Bind
Perhaps if I change the port number, I can execute the code.
Please let me know why it is like this in the example and how to work with BrokerService.
The BrokerService in this example is trying to create an in memory ActiveMQ broker for use in the example. Given the error you are seeing I'd guess you already have an ActiveMQ broker running on the machine that is bound to port 61616 as that's the default port and thus the two are conflicting. You could either stop the external broker and run the example or modify the example to not run the embedded broker and just rely on your external broker instance.
Embedded brokers are great for unit testing or for creating examples that don't require the user to have a broker installed and running.

Netty: How to implement a telnet client handler which needs authentication

This is my first time ask question through this platform. I am sorry. I am not good in English. I will try my best to let you understand my questions.
I am totally beginner in Netty. I would like to implement a program to send commands to a telnet server and receive response message. I modified the sample telnet program to connect and get response from the serve when there is no authentication of serve.
The question is that
When the authentication processes are setup in server. (Require login name and password)
How to implement the client side program?
How can I receive the serve login request and response it?
Should I implement another handler to handle the authentication?
below shows how i send the commands to the server
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new TelnetClientInitializer(sslCtx));
// Start the connection attempt.
ChannelFuture lastWriteFuture = null;
lastWriteFuture = b.connect(HOST, PORT).sync();
Channel ch = lastWriteFuture.channel();
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n", ch.newPromise());
lastWriteFuture = ch.writeAndFlush("status" + "\r\n");
lastWriteFuture = ch.writeAndFlush("ls" + "\r\n");
lastWriteFuture = ch.writeAndFlush("exit" + "\r\n");
// Wait until the connection is closed.
lastWriteFuture.channel().closeFuture().sync();
} finally {
// Shut down the event loop to terminate all threads.
group.shutdownGracefully();
}
but what should i do before send the above commands to login into the serve?
The following picture shows what i want to do in the program
Thank you very much!!!
If we talk about TELNET as a protocol you should know that Telnet client from Netty examples does not support TELNET protocol. His name is just confusing and you can't connect to any standard telnet servers. You can read more about TELNET protocol here - THE TELNET PROTOCOL .
I see 2 ways:
write your implementation for TELNET on Netty
use another implementation for examples Apache Commons Net
Example for the first way - modified netty client, i tested him on Linux servers. He has several dirty hacks like a timer but he works.
Example for the second - Java – Writing An Automated Telnet Client:
import org.apache.commons.net.telnet.*;
import java.io.InputStream;
import java.io.PrintStream;
public class AutomatedTelnetClient {
private TelnetClient telnet = new TelnetClient();
private InputStream in;
private PrintStream out;
private String prompt = "~>";
public AutomatedTelnetClient(String server) {
try {
// Connect to the specified server
telnet.connect(server, 8023);
TerminalTypeOptionHandler ttopt = new TerminalTypeOptionHandler("VT100", false, false, true, false);
EchoOptionHandler echoopt = new EchoOptionHandler(true, false, true, false);
SuppressGAOptionHandler gaopt = new SuppressGAOptionHandler(true, true, true, true);
try {
telnet.addOptionHandler(ttopt);
telnet.addOptionHandler(echoopt);
telnet.addOptionHandler(gaopt);
} catch (InvalidTelnetOptionException e) {
System.err.println("Error registering option handlers: " + e.getMessage());
}
// Get input and output stream references
in = telnet.getInputStream();
out = new PrintStream(telnet.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
// public void su(String password) {
// try {
// write(“su”);
// readUntil(“Password: “);
// write(password);
// prompt = “#”;
// readUntil(prompt + ” “);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
public String readUntil(String pattern) {
try {
char lastChar = pattern.charAt(pattern.length() - 1);
StringBuffer sb = new StringBuffer();
boolean found = false;
char ch = (char) in.read();
while (true) {
System.out.print(ch);
sb.append(ch);
if (ch == lastChar) {
if (sb.toString().endsWith(pattern)) {
return sb.toString();
}
}
ch = (char) in.read();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void write(String value) {
try {
out.println(value);
out.flush();
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
public String sendCommand(String command) {
try {
write(command);
return readUntil(prompt + " ");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void disconnect() {
try {
telnet.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String user = "test";
String password = "test";
AutomatedTelnetClient telnet = new AutomatedTelnetClient("localhost");
// Log the user on
telnet.readUntil("login:");
telnet.write(user);
telnet.readUntil("Password:");
telnet.write(password);
// Advance to a prompt
telnet.readUntil(telnet.prompt + " ");
telnet.sendCommand("ps -ef");
telnet.sendCommand("ls");
telnet.sendCommand("w");
telnet.disconnect();
}
}
Telnet has no real concept of a password packet, a password prompt is just like any normal text output. This means that you can just send the username and password when connection as separate lines, and the telnet server will use them correctly.
ch.writeAndFlush("administrator" + "\r\n");
ch.writeAndFlush("LetMeIn4!!" + "\r\n");
If you require connecting to server that don't always require the password, then you should read the output from the server, check if it contains "username", send the username, then keep reading if it contains "password" and send the password. This is prone to breaking as servers are not required to send those strings, and legit output may also contain those. This is the downside of the telnet protocol.
I hope this my article is helpful to someone.
Netty | Implement Telnet Automated Authentication
I had to use Telnet to control the sub-equipment while developing the space ground station software. Except for the authentication, Telnet is quite similar to regular TCP server communication. So, I implemented a Handler that automatically handles Telnet authentication to communicate with the Telnet server. When connecting to the Telnet server, the following introductory message, “Username: “, “Passwrod: “ messages are displayed in sequence, and user authentication is requested. Handler automatically handles the authentication process as if a human would input account information. Below is a brief description of the implementation.
c:\> telnet 192.168.0.1 12345
Power On Self Test (POST) Passed.
Integrated Control Unit (ICU) Build xxx (Build:xxxxxx) - Feb 7 2022, 17:57:16 (Network/TCP)
Date and Time: 2022-02-16 20:01:19 (GMT)
MAC Address : [00:xx:xx:xx:C6:8F]
Username: User
Password: 1234
>
Handler
TelnetAuthenticator Handler simply works as follows.
If the message contains the string “Username: “, send the username.
If the message contains the string “Password: “, the password is sent.
If the message contains the string “>” waiting for input, delete the authentication handler from the Pipeline. After authentication, TelnetAuthenticator Handler is unnecessary.
If the account is not registered on the Telnet server or the password does not match, the string “Username: “ or “Password: “ is repeatedly received. The authentication failure error is unrecoverable, notifying the user of a failed authentication process and forcing them to disconnect.
#Slf4j
#RequiredArgsConstructor
public class TelnetAuthenticator extends SimpleChannelInboundHandler<String> {
private final ChannelSpec channelSpec;
private boolean alreadyUserTried = false;
private boolean alreadyPasswordTried = false;
#Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
// If the message contains the string “Username: “, send the username.
if (msg.contains(channelSpec.getReqUserTag())) {
if (alreadyUserTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getUser() + channelSpec.getEndLine());
alreadyUserTried = true;
return;
}
// If the message contains the string “Password: “, the password is sent.
if (msg.contains(channelSpec.getReqPasswordTag())) {
if (alreadyPasswordTried) {
processFail(ctx);
}
ctx.channel().writeAndFlush(channelSpec.getAccount().getPassword() + channelSpec.getEndLine());
alreadyPasswordTried = true;
return;
}
// If the incoming message contains an input waiting message, the Pipeline deletes the current handler.
if (msg.contains(channelSpec.getStandByTag())) {
ctx.pipeline().remove(this.getClass());
}
}
private void processFail(ChannelHandlerContext ctx) {
ctx.fireUserEventTriggered(ErrorMessage.AUTHENTICATE_FAIL);
ctx.close();
}
}
Initialize ChannelPipeline
A ChannelPipeline configuration with a TelnetAuthenticator Handler can be: First, register InboundHandlers as follows.
First, add DelimiterBasedFrameDecoder with “Username: “, “Password: “, “>” strings as delimiters. The stripDelimiter option is set to false because all delimiters must be received to recognize the authentication process.
Add StringDecoder.
Add the implemented TelnetAuthenticator Handler.
Add other necessary business logic.
Simply add StringEncoder to Outbound. You can add other Handlers as needed.
public class PipelineInitializer extends ChannelInitializer<SocketChannel> {
private ChannelSpec channelSpec;
public void init(ChannelSpec channelSpec) {
this.channelSpec = channelSpec;
}
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
// Inbound
.addLast(new DelimiterBasedFrameDecoder(1024, false,
channelSpec.getDelimiter().reqUserTag(),
channelSpec.getDelimiter().reqPasswordTag(),
channelSpec.getDelimiter().standByTag()))
.addLast(new StringDecoder())
.addLast(new TelnetAuthenticator(channelSpec))
.addLast(new BusinessLogic())
// Outbound
.addLast(new StringEncoder());
}
}
ChannelSpec
ChannelSpec defines specifications required for communication with Telnet server. Manage server IP, port, account information, separator, etc.
#Getter
public class ChannelSpec {
private final String serverIp = "192.168.0.1";
private final int serverPort = 12345;
private final String endLine = "\r\n";
private final String standByTag = ">";
private final String reqUserTag = "Username: ";
private final String reqPasswordTag = "Password: ";
private final Account account = new Account("User", "1234");
private final Delimiter delimiter = new Delimiter();
public class Delimiter {
public ByteBuf standByTag() {
return toByteBuf(standByTag);
}
public ByteBuf reqUserTag() {
return toByteBuf(reqUserTag);
}
public ByteBuf reqPasswordTag() {
return toByteBuf(reqPasswordTag);
}
private ByteBuf toByteBuf(String input) {
ByteBuf delimiterBuf = Unpooled.buffer();
delimiterBuf.writeCharSequence(input, StandardCharsets.UTF_8);
return delimiterBuf;
}
}
}
#RequiredArgsConstructor
#Getter
public class Account {
private final String user;
private final String password;
}

ActiveMQ fail-over of producer and consumer with a shared directory doesn't happen

We have two ActiveMQ(version 5.10.0) instances running and I am using the shared storage to achieve HA.
However I am unable to see failover happening for the producer and consumer(s).
ActiveMQ broker-1 runs on IP1 and broker-2 on IP2
And under the activemq.xml of configuration I have modified persistence adapter to use a shared directory which is present on IP1.
<persistenceAdapter>
<kahaDB directory="\\IP1\shared-directory\for activemq\data"/>
</persistenceAdapter>
Both in producer and consumer sides I am using following JNDI configurations to get the connections and build sessions,etc.
jndi.properties
java.naming.factory.initial = ..........ActiveMQInitialContextFactory
java.naming.provider.url = failover:(tcp://IP1:61616,tcp://IP2:61616)?randomize=false
connectionFactoryNames = myConnectionFactory
queue.requestQ = my.RequestQ
Interesting part is :
When I start this broker pair, I see that one of the brokers becomes master.
When I start the producer, which puts the message on the Q (say producer has put 100 messages on the Q). While my producer is still running; I shutdown master broker, hence slave broker acquires the file-lock and becomes master.When I open the webconsole I see that 100 messages are still there on the Q. Even though producer is running it no longer puts any messages on this Q.
Similar to this for the consumers also.
Consumer was picking messages from the Q, this Q has say 100 messages unconsumed when master failed, now master goes down, slave becomes master, I see 100 messages are still unconsumed, but the consumer does not pick any message from the Q.
I waited them to failover for a long time.(>10 mins.)
Can any one please suggest what configuration am I missing ?
I am copy pasting producer and consumer as is (I've copied this from ActiveMQ in action book with minor modifications).
Producer
public class Producer {
private static String brokerURL = "failover:(tcp://IP1:3389,tcp://IP2:3389)";
private static transient ConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private transient MessageProducer producer;
private static int count = 10;
private static int total;
private static int id = 1000000;
private String jobs[] = new String[] { "suspend", "delete" };
public Producer() throws JMSException {
factory = new ActiveMQConnectionFactory(brokerURL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public static void main(String[] args) throws JMSException {
Producer producer = new Producer();
while (total < 1000) {
for (int i = 0; i < count; i++) {
producer.sendMessage();
}
total += count;
System.out.println("Sent '" + count + "' of '" + total
+ "' job messages");
try {
Thread.sleep(1000);
} catch (InterruptedException x) {
}
}
producer.close();
}
public void sendMessage() throws JMSException {
int idx = 0;
while (true) {
idx = (int) Math.round(jobs.length * Math.random());
if (idx < jobs.length) {
break;
}
}
String job = jobs[idx];
Destination destination = session.createQueue("JOBS." + job);
Message message = session.createObjectMessage(id++);
System.out.println("Sending: id: "
+ ((ObjectMessage) message).getObject() + " on queue: "
+ destination);
producer.send(destination, message);
}
}
Consumer
public class Consumer {
private static String brokerURL = "failover:(tcp://IP1:3389,tcp://IP2:3389)";
private static transient ConnectionFactory factory;
private transient Connection connection;
private transient Session session;
private String jobs[] = new String[] { "suspend", "delete" };
public Consumer() throws JMSException {
factory = new ActiveMQConnectionFactory(brokerURL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
public void close() throws JMSException {
if (connection != null) {
connection.close();
}
}
public static void main(String[] args) throws JMSException {
Consumer consumer = new Consumer();
for (String job : consumer.jobs) {
Destination destination = consumer.getSession().createQueue(
"JOBS." + job);
MessageConsumer messageConsumer = consumer.getSession()
.createConsumer(destination);
messageConsumer.setMessageListener(new Listener(job));
}
}
public Session getSession() {
return session;
}
}
Just one more thing:
I am more interested in consumer failover than producer.
One more observation is : Consumer stops and comes to the command prompt abruptly.
Thank you.
-JE