Rabbitmq java client consumer handleDelivery method does not get called - java

This is a very contrived example of a rabbitmq app which is both producer and consumer of messages in one single main method. The problem is the code inside overridden handleDelivery method never gets executed. I use Rabbitmq dashboard and see the queue fills up and consumes. And the line in handleConsumeOk gets printed.
Since I am new to rabbitmq I'm wondering if I have done something fundamentally wrong or I just got the idea of "Called when a basic.deliver is received for this consumer" wrong.
public class RabbitMain {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = Utils.getConnection("test");
String payload = "hello world!";
try (Channel channel = connection.createChannel()){
channel.exchangeDeclare("sampleExchange", BuiltinExchangeType.TOPIC, true);
channel.basicPublish("sampleExchange", "testKey", null, payload.getBytes());
}
System.out.println("Consume...");
try (Channel channel = connection.createChannel()){
channel.exchangeDeclare("sampleExchange", BuiltinExchangeType.TOPIC, true);
channel.queueDeclare("testQueue", true, false, false, null);
channel.queueBind("testQueue", "sampleExchange", "testKey");
Consumer consumer = new DefaultConsumer(channel){
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body);
System.out.println("Received: " + message);
}
#Override
public void handleConsumeOk(String consumerTag) {
System.out.println("handled consume ok");
}
};
Thread.sleep(2000);
channel.basicConsume("testQueue", true, consumer);
}
}
}

You publish the message to the exchnage before any queue is bound to it. RabbitMQ will discard any message it can't route to a queue.
Do
channel.queueDeclare("testQueue", true, false, false, null);
channel.queueBind("testQueue", "sampleExchange", "testKey");
before you call channel.basicPublish.

Related

How can we not receive a new message from the queue in Java rabbitmq until I have not responded to a message?

Receive single message from the queue in RabbitMQ using java:
I'm new to RabbitMQ and was wondering of a good approach to this problem I'm mulling over. I want to create a service that subscribes to a queue and Receive only one message and receive the next one after processing.
DeliverCallback deliverCallback = new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
System.out.println("Tag: "+delivery.getEnvelope().getDeliveryTag());
String message = new String(delivery.getBody(), "UTF-8");
}
};
channel.basicConsume(QUEUE_NAME, false, deliverCallback, new CancelCallback() {
public void handle(String consumerTag) throws IOException {}
});
To receive a single message from the queue, I did the following steps:
Step 1: Accept only one unack-ed message at a time:
channel.basicQos(1);
Step 2: Setting autoAck to false:
boolean autoAck = false;
Step 3: Work Queues using the Java Client
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
try {
System.out.println("Tag: "+delivery.getEnvelope().getDeliveryTag());
String message = new String(delivery.getBody(), "UTF-8");
} finally {
System.out.println(" [Message] Task Done");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> { });
see: rabbitmq documentation
RabbitMQ supports polling a single message from a Queue at a time, but the official document doesnt encourage this approach.
Poll the message by disabling auto acknowledgement, finally send an ack after processing.
boolean autoAck = false;
GetResponse response = channel.basicGet(queueName, autoAck);
if (response != null) {
//process the message and acknowledge it
}
For more details please refer to the official document section 'Retrieving Individual Messages ("Pull API")'
https://www.rabbitmq.com/api-guide.html#getting

Resend messages after timeout

I have a list of objects that I put in Spring AMQP. Objects come from the controller. There is a service that processes these objects. And this service may crash with an OutOfMemoryException. Therefore, I run several instances of the application.
There is a problem: when the service crashes, I lose the received messages. I read about NACK. And could use it in case of Exception or RuntimeException. But my service crashes in Error. Therefore, I cannot send NACK. Is it possible to set a timeout in AMQP, after which I would be sent a message again if I had not confirmed the messages that had arrived earlier?
Here is the code I wrote:
public class Exchanges {
public static final String EXC_RENDER_NAME = "render.exchange.topic";
public static final TopicExchange EXC_RENDER = new TopicExchange(EXC_RENDER_NAME, true, false);
}
public class Queues {
public static final String RENDER_NAME = "render.queue.topic";
public static final Queue RENDER = new Queue(RENDER_NAME);
}
#RequiredArgsConstructor
#Service
public class RenderRabbitEventListener extends RabbitEventListener {
private final ApplicationEventPublisher eventPublisher;
#RabbitListener(bindings = #QueueBinding(value = #Queue(Queues.RENDER_NAME),
exchange = #Exchange(value = Exchanges.EXC_RENDER_NAME, type = "topic"),
key = "render.#")
)
public void onMessage(Message message, Channel channel) {
String routingKey = parseRoutingKey(message);
log.debug(String.format("Event %s", routingKey));
RenderQueueObject queueObject = parseRender(message, RenderQueueObject.class);
handleMessage(queueObject);
}
public void handleMessage(RenderQueueObject render) {
GenericSpringEvent<RenderQueueObject> springEvent = new GenericSpringEvent<>(render);
springEvent.setRender(true);
eventPublisher.publishEvent(springEvent);
}
}
And this is the method that sends messages:
    #Async ("threadPoolTaskExecutor")
    #EventListener (condition = "# event.queue")
    public void start (GenericSpringEvent <RenderQueueObject> event) {
        RenderQueueObject renderQueueObject = event.getWhat ();
        send (RENDER_NAME, renderQueueObject);
}
private void send(String routingKey, Object queue) {
try {
rabbitTemplate.convertAndSend(routingKey, objectMapper.writeValueAsString(queue));
} catch (JsonProcessingException e) {
log.warn("Can't send event!", e);
}
}
You need to close the connection to get the message re-queued.
It's best to terminate the application after an OOME (which, of course, will close the connection).

Kafka Java Consumer SDK long pull without using while

I try to use Kafka Java SDK to implement a consumer however most consumer examples I saw are using while(true) loop and inside the loop call consume method to get a message.
while (true) {
final ConsumerRecords<Long, String> consumerRecords =
consumer.poll(1000);
if (consumerRecords.count()==0) {
noRecordsCount++;
if (noRecordsCount > giveUp) break;
else continue;
}
consumerRecords.forEach(record -> {
System.out.printf("Consumer Record:(%d, %s, %d, %d)\n",
record.key(), record.value(),
record.partition(), record.offset());
});
consumer.commitAsync();
}
I am wondering are there any elegant way to handle this without using while loop which is similar to RabbitMQ implementation following:
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
You can try using Spring-kafka which has #KafkaListener annotation and makes the method to listen topic, for more info here
Because in apache-kafka there is no elegant way to make method as a listener to topic, since consumer need to poll records for certain intervals, needed that code in loop
#KafkaListener(topics = "topicName", group = "foo")
public void listen(String message) {
System.out.println("Received Messasge in group foo: " + message);
}
Poll Loop is the only way to consume the messages in Kafka. The elegant code to handle the message should be within the loop.

How to access the payload of the message arrived of the callback method (messageArrived) in the main method Eclipse Paho?

Problem statement:- I am trying to automate a MQTT flow, for that I a need to publish and subscribe to multiple topics but in a sequential order. The trick part is that the message received from the first publish has some value which will be passed in the next sub/pub commands.
For eg.
Sub to topicA/abc
Pub to topicA/abc
Message received on topicA/abc is xyz
sub to topic topicA/xyz
pub to topic topicA/xyz
I am able to receive the message on the first topic but I am not getting how to access the payload of the received message in the main method and pass and attach it to the next topic for next sub.
Is there a way to get the retrieved the message payload from messageArrived callback method to the main method where is client instance is created?
Note:- I am using a single client for publish and subscribe.
kindly help me out as I have ran out of options and methods to do so.
Edited:-
Code snippet
Main class
public class MqttOverSSL {
String deviceId;
MqttClient client = null;
public MqttOverSSL() {
}
public MqttOverSSL(String deviceId) throws MqttException, InterruptedException {
this.deviceId = deviceId;
MqttConnection mqttConObj = new MqttConnection();
this.client = mqttConObj.mqttConnection();
}
public void getLinkCodeMethod() throws MqttException, InterruptedException {
client.subscribe("abc/multi/" + deviceId + "/linkcode", 0);
publish(client, "abc/multi/" + deviceId + "/getlinkcode", 0, "".getBytes());
}
}
Mqtt Claback impl:-
public class SimpleMqttCallBack implements MqttCallback {
String arrivedMessage;
#Override
public void connectionLost(Throwable throwable) {
System.out.println("Connection to MQTT broker lost!");
}
#Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
arrivedMessage = mqttMessage.toString();
System.out.println("Message received:\t" + arrivedMessage);
linkCode(arrivedMessage);
}
#Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
System.out.println("Delivery complete callback: Publish Completed "+ Arrays.toString(iMqttDeliveryToken.getTopics()));
}
public void linkCode(String arrivedMessage) throws MqttException {
System.out.println("String is "+ arrivedMessage);
Gson g = new Gson();
GetCode code = g.fromJson(arrivedMessage, GetCode.class);
System.out.println(code.getLinkCode());
}
}
Publisher class:-
public class Publisher {
public static void publish(MqttClient client, String topicName, int qos, byte[] payload) throws MqttException {
String time = new Timestamp(System.currentTimeMillis()).toString();
log("Publishing at: "+time+ " to topic \""+topicName+"\" qos "+qos);
// Create and configure a message
MqttMessage message = new MqttMessage(payload);
message.setQos(qos);
// Send the message to the server, control is not returned until
// it has been delivered to the server meeting the specified
// quality of service.
client.publish(topicName, message);
}
static private void log(String message) {
boolean quietMode = false;
if (!quietMode) {
System.out.println(message);
}
}
}
OK, it's a little clearer what you are trying to do now.
Short answer No, you can not pass values back to the "main method". MQTT is asynchronous that means you have no idea when a message will arrive for a topic you subscribe to.
You need to update your code to deal check what the incoming message topic is and then deal do what ever action you wanted to do with that response in the messageArrived() handler. If you have a sequence of task to do then you may need to implement what is known as a state machine in order to keep track of where you are in the sequence.

How do I write an error to a WebSocket channel in Netty?

I have a Netty 4.0.19 app where I'm using a TextWebSocketFrames to send and receive messages.
These messages, when they are received, all fire through the "onmessage" event handler.
What would I do to make error messages instead on the onerror handler?
For example, how would the following's error condition be sent back to the web socket client as an error and not a message?
public void channelRead0(final ChannelHandlerContext ctx, final TextWebSocketFrame msg) throws Exception
{
final String message = msg.retain().text();
Envelope envelope = null;
boolean errorFree = true;
try
{
envelope = EnvelopeJsonEncoder.decode(MessageType.PUBLISH, message).build();
}
catch (final Exception e)
{
errorFree = false;
final WebSocketFrame outFrame = new CloseWebSocketFrame(1002, "Closing due to error");
ctx.writeAndFlush(outFrame);
}
if (errorFree)
{
ctx.fireChannelRead(envelope);
}
}
Normally you would write a CloseWebSocketFrame and specify the right close code and reason.

Categories

Resources