I'm sending and receiving messages with SQS and Java SDK. Almost all messages are working fine, but some of them are lost and I can't understand why. This is the code to send a message:
final SendMessageRequest msg = new SendMessageRequest(
this.queueUrl, data.toString()
).withMessageGroupId(projectId);
final Map<String, MessageAttributeValue> attrs = new HashMap<>(1);
// message signature - HMAC calculated from message body
final String signature = // calculating signature
attrs.put(
"signature",
new MessageAttributeValue()
.withDataType("String")
.withStringValue(signature)
);
attrs.put(
"project",
new MessageAttributeValue()
.withDataType("String")
.withStringValue(projectId)
);
attrs.put(
"priority",
new MessageAttributeValue()
.withDataType("String")
.withStringValue(priority)
);
msg.setMessageDeduplicationId(
String.format("%s:%s", projectId, signature)
);
msg.setMessageAttributes(attrs);
Logger.debug(this, "sending message: %s", msg);
final SendMessageResult res = this.sqs.sendMessage(msg);
Logger.info(
this,
"message '%s' (%s) was sent: %s",
data.id(), data.type(), res
);
and the code for receiving (running in a loop):
final List<Message> messages = this.sqs.receiveMessage(
new ReceiveMessageRequest(url)
.withMessageAttributeNames(
"project", "signature", "expires", "priority"
)
.withVisibilityTimeout(
(int) Duration.ofMinutes(2L).getSeconds()
)
.withMaxNumberOfMessages(8)
).getMessages();
Logger.info(this, "received %d messages", messages.size());
for (final Message message : messages) {
Logger.debug(this, "received message: %s", message);
// actual logic here
this.sqs.deleteMessage(
new DeleteMessageRequest()
.withQueueUrl(this.queue)
.withReceiptHandle(message.getReceiptHandle())
);
}
The problem is that I'm able to receive some messages, but some not (always same type of data data.type()). The code to send and receive is the same for all messages.
App logs:
sending message: {QueueUrl: https://sqs.us-east-1.amazonaws.com/0000000/my-queue.fifo, MessageBody:some-unique-body,MessageAttributes: {priority={StringValue: HIGH,StringListValues: [],BinaryListValues: [],DataType: String}, signature={StringValue: EXTvx7WWrZ7uTU63szJ2C4VN/6ZOiw/wKL83qW7V3i0=,StringListValues: [],BinaryListValues: [],DataType: String}, project={StringValue: PMO,StringListValues: [],BinaryListValues: [],DataType: String}},MessageDeduplicationId: PMO:EXTvx7WWrZ7uTU63szJ2C4VN/6ZOiw/wKL83qW7V3i0=,MessageGroupId: PMO}
message 'e29baf85-7be4-449b-824f-e405c59cf7c4' ([test]) was send: {MD5OfMessageBody: 9d6e98e0e85c8f5ca7cc4c23378dc14b,MD5OfMessageAttributes: fe94ccb1b405588e0691c91392d2c8ea,MessageId: c8ce957a-93c8-49ef-9a08-9e12cb5952b4,SequenceNumber: 18848975946990575872}
receiving messages: limit=8; timout=2m
received 0 messages
receiving messages: limit=8; timout=2m
received 0 messages
receiving messages: limit=8; timout=2m
received 0 messages
receiving messages: limit=8; timout=2m
received 0 messages
I checked that deduplication-id of these messages are always different and it has different content. How can I debug this issue deeper?
Update:
It seems my queue is full of messages, but I can't receive them:
$ aws sqs get-queue-attributes --queue-url="$QURL" --attribute-names=ApproximateNumberOfMessages
{
"Attributes": {
"ApproximateNumberOfMessages": "1490"
}
}
It's strange, since some messages are delivered in a few seconds even with full queue.
Update2:
I tried to use long-polling, but it didn't help.
Check whether is there any inflight requests for that queue in AWS console, as i can see you are using the same messagegroupid for all the messages, so the messages will be processed sequentially, if any one message is received by any consumer, until the acknowledgement of the message or visibility timeout expires, no other consumers will receive any of the messages.
Related
I have been trying to get a connection to a mqtt server working for a while,
and while the subscription is received, it executes once and after that it only executes after a server restart
if (!mqttClient.isConnected){
mqttClient.connect(mqttConnectOptions).waitForCompletion()
}
val iMqttToken = mqttClient.subscribe("devices/+/+", 0) { topic, message ->
// gets printed once
println(topic + String(message.payload))
val pointTo = topic.substring(topic.indexOf("/") + 1, topic.length)
val result = Math.random() > 0.5
val publishTopic = "server/$pointTo"
// gets printed once
println("enviando respuesta a $publishTopic")
// publish goes out and gets received by MQTT device
mqttClient.publish(publishTopic,
"{\"granted\":$result,\"transactionId\":21334}".encodeToByteArray(),
0,
false)
Timer().schedule(2000) {
// Prints true
println(mqttClient.isConnected)
}
}
Not sure what am I doing wrong any help is apreciated.
I referred this to receive messages from my Azure Service bus via subscription
I am able to receive the messages, but I am continuously receiving the messages until I manually terminate the program
I have a timeout option and want to receive messages only till the timeout.
It would be helpful if you can explain how the below code works and how I can modify the below code to receive messages for a particular time frame and stop receiving once my timeout has been reached.
static void registerMessageHandlerOnClient(SubscriptionClient receiveClient, ExecutorService executorService) throws Exception {
// register the RegisterMessageHandler callback
receiveClient.registerMessageHandler(
new IMessageHandler() {
// callback invoked when the message handler loop has obtained a message
public CompletableFuture<Void> onMessageAsync(IMessage message) {
// receives message is passed to callback
if (message.getLabel() != null &&
message.getContentType() != null &&
message.getLabel().contentEquals("Scientist") &&
message.getContentType().contentEquals("application/json")) {
byte[] body = message.getBody();
Map scientist = GSON.fromJson(new String(body, UTF_8), Map.class);
System.out.printf(
"\n\t\t\t\t%s Message received: \n\t\t\t\t\t\tMessageId = %s, \n\t\t\t\t\t\tSequenceNumber = %s, \n\t\t\t\t\t\tEnqueuedTimeUtc = %s," +
"\n\t\t\t\t\t\tExpiresAtUtc = %s, \n\t\t\t\t\t\tContentType = \"%s\", \n\t\t\t\t\t\tContent: [ firstName = %s, name = %s ]\n",
receiveClient.getEntityPath(),
message.getMessageId(),
message.getSequenceNumber(),
message.getEnqueuedTimeUtc(),
message.getExpiresAtUtc(),
message.getContentType(),
scientist != null ? scientist.get("firstName") : "",
scientist != null ? scientist.get("name") : "");
}
return receiveClient.completeAsync(message.getLockToken());
}
// callback invoked when the message handler has an exception to report
public void notifyException(Throwable throwable, ExceptionPhase exceptionPhase) {
System.out.printf(exceptionPhase + "-" + throwable.getMessage());
}
},
// 1 concurrent call, messages are auto-completed, auto-renew duration
new MessageHandlerOptions(1, false, Duration.ofMinutes(1)),
executorService);
}
This cannot be done in your subscription code.
There are two options/workarounds which you can do:
Don't send a message to the topic continuously, have time control there.
Create a Timer Trigger that makes a REST API call Subscriptions - Create Or Update to make EntityStatus = ReceiveDisabled and use the similar function to make EntityStatus = Active.
I'm writing code in java (using Azure SDK for Java), I have a Service bus queue that contains sessionful messages. I want to receive those messages and process them to another place.
I make a connection to the Queue by using QueueClient, and then I use registerSessionHandler to process through the messages (code below).
The problem is that whenever a message is received, I can print all details about it including the content, but it is printed 10 times and after each time it prints an Exception.
(printing 10 times: I understand that this is because there is a 10 times retry policy before it throws the message to the Dead letter queue and goes to the next message.)
The Exception says
> USERCALLBACK-Receiver not created. Registering a MessageHandler creates a receiver.
The output with the Exception
But I'm sure that the SessionHandler does the same thing as MessageHandler but includes support for sessions, so it should create a receiver since it receives messages. I have tried to use MessageHandler but it won't even work and stops the whole program because it doesn't support sessionful messages, and the ones I receive have sessions.
My problem is understanding what the Exception wants me to do, and how can I fix the code so it won't give me any exceptions? Does anyone have suggestions on how to improve the code? or other methods that do the same thing?
QueueClient qc = new QueueClient(
new ConnectionStringBuilder(connectionString),
ReceiveMode.PEEKLOCK);
qc.registerSessionHandler(
new ISessionHandler() {
#Override
public CompletableFuture<Void> onMessageAsync(IMessageSession messageSession, IMessage message) {
System.out.printf(
"\nMessage received: " +
"\n --> MessageId = %s " +
"\n --> SessionId = %s" +
"\n --> Content Type = %s" +
"\n --> Content = \n\t\t %s",
message.getMessageId(),
messageSession.getSessionId(),
message.getContentType(),
getMessageContent(message)
);
return qc.completeAsync(message.getLockToken());
}
#Override
public CompletableFuture<Void> OnCloseSessionAsync(IMessageSession iMessageSession) {
return CompletableFuture.completedFuture(null);
}
#Override
public void notifyException(Throwable throwable, ExceptionPhase exceptionPhase) {
System.out.println("\n Exception " + exceptionPhase + "-" + throwable.getMessage());
}
},
new SessionHandlerOptions(1, true, Duration.ofMinutes(1)),
Executors.newSingleThreadExecutor()
);
(The getMessageContent(message) method is a separate method, for those interested:)
public String getMessageContent(IMessage message){
List<byte[]> content = message.getMessageBody().getBinaryData();
StringBuilder sb = new StringBuilder();
for (byte[] b : content) {
sb.append(new String(b)
);
}
return sb.toString();
}
For those who wonder, I managed to solve the problem!
It was simply done by using Azure Functions ServiceBusQueueTrigger, it will then listen to the Service bus Queue and process the messages. By setting isSessionsEnabled to true, it will accept sessionful messages as I wanted :)
So instead of writing more than 100 lines of code, the code looks like this now:
public class Function {
#FunctionName("QueueFunction")
public void run(
#ServiceBusQueueTrigger(
name = "TriggerName", //Any name you choose
queueName = "queueName", //QueueName from the portal
connection = "ConnectionString", //ConnectionString from the portal
isSessionsEnabled = true
) String message,
ExecutionContext context
) {
// Write the code you want to do with the message here
// Using the variable messsage which contains the messageContent, messageId, sessionId etc.
}
}
I am trying to send rabbit mq message on a exchange - exchange-X to a message queue - queque-X with routing key -mc, its being received well on my local rabbit mq but on production rabbit mq the message does not appear. The exchange and the queue is binded with the specified routing key. In the below message isSent is true always but actually message does not reach at the queue only on prod rabbitmq env. Is routing key mc case sensitive ?
public void sendMessageCenterNotification(Map<String, Object> headerMap,String correlationId,String message) {
boolean isSent = false;
try {
isSent = rabbitMQ.messageSender(message, headerMap, "mc", correlationId);
} catch (Exception e) {
logger.error(correlationId + " - Exception occured in sendMessageCenterNotification:", e);
} finally {
logger.info(correlationId
+ "-inside sendMessageCenterNotification message sending to message center was "+(isSent?"successfull":"failed")+", message:"
+ message);
}
}
From the documentation:
A fanout exchange routes messages to all of the queues that are bound
to it and the routing key is ignored.
You probably want a different type of exchange for what you are trying to accomplish.
As a side note, the protocol (0.9) reference does not appear to mention case sensitivity, but in the client implementations routing keys are case-sensitive.
I am working on a POC for RabbitMQ for an M2M solution. I have a large number of physical devices that will be publishing data (simulating clients using the Java client for now - eventually over MQTT). I want to:
subscribe and journal all raw data to the database
subscribe to sub-sets of the data by data type so I can scale
solutions for those types of data independently
publish new events through the exchange (e.g. take a raw event, make
it more useful and resubmit it through the system)
Each message has a routing key like key:value.key:value.key:value.messageType:1 and data from the devices has an extra key of FROMDEVICE.MESSAGETYPE:1.key:value... etc. The subscriber that saves the raw data from the device builds a queue from the exchange with the routing key #.FROMDEVICE.# (case #1 above). The subscriber that takes a specific message type and value-adds it builds a queue with the routing key #.MESSAGETYPE:1.# (case #2 above) and submits a new message to the same exchange removing FROMDEVICE from the routing key and replacing .MESSAGETYPE:1 with .MESSAGETYPE:101 (case #3 above). There is then an independent subscriber/queue for the new message type.
Everything is fine except my subscriber that should only receive the data from the devices is also getting the value added data (MESSAGETYPE:101) even though the routingKey it should be searching for does not exist in the re-published/value-added message.
FROMDEVICE.MESSAGETYPE:1 ->
should match routing key #.FROMDEVICE.#
should match #.MESSAGETYPE:1.#
MESSAGETYPE:101
should match routing key #.MESSAGETYPE:101.#
should NOT match #.FROMDEVICE.# (but does)
Code to subscribe to data from devices only:
public class HandlerWriteEverythingFromDevice {
private final static String EXCHANGE_NAME = "logsTopicDurable";
private final static String QUEUE_NAME = "fromDevice";
/**
* Writes all data from device to a data store.
*/
public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.101");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
System.out.println(" [*] listens for messages from devices - durable!");
channel.basicQos(1);
String routingKey = "#.fromDevice.#".toUpperCase();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey); //bind to all selected messages
System.out.println(" [*] subscribing to: " + routingKey);
System.out.println(" [*] Waiting for messages. To exit press CTRL_C");
QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false; //ack back when done
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
int msgCount = 0;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Message Count: " + ++msgCount + " ROUTINGKEY: '" + delivery.getEnvelope().getRoutingKey() + "\n MESSAGE: '" + message + "'");
Thread.sleep(250); //simulate some time to insert into the db.
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
Code to subscribe only to messageType:1 and re-publish messageType:101
private final static String EXCHANGE_NAME = "logsTopicDurable";
private final static String QUEUE_NAME = "messageType1";
/**
* Handler for messageType:1
*/
public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.101");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
System.out.println(" [*] listens for messageType:1 and submits messageType:101");
channel.basicQos(1);
String routingKey = "#.messageType:1.#".toUpperCase();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey); //bind to all selected messages
System.out.println(" [*] subscribing to: " + routingKey);
System.out.println(" [*] Waiting for messages. To exit press CTRL_C");
QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false; //ack back when done
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
int msgCount = 0;
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Message Count: " + ++msgCount + " ROUTINGKEY: '" + delivery.getEnvelope().getRoutingKey() + "\n MESSAGE: '" + message + "'");
channel.basicPublish(EXCHANGE_NAME,
delivery.getEnvelope().
getRoutingKey().
replaceAll("messageType:1", "messageType:101").
replaceAll(".FROMDEVICE", "").
replaceAll("FROMDEVICE.", "").trim(),
true,
MessageProperties.PERSISTENT_BASIC,
message.getBytes());
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
There is publisher code and subscriber code for messageType:101 but I don't think they are needed for this discussion. I've wondered if publishing to a channel that has a queue bound to it might be the cause, but I tried creating two channels (same connection object) and had the same result and a lot uglier code.
I would suggest that you are being a bit to liberal with your binding keys. To make things a little more clear you should use the term binding key and routing key differently. The routing key is what is sent by the producer. The binding key is what you use to bind the queue to the topic exchange.
As I cannot be sure which you are referring to when you say
"should match routing key #.MESSAGETYPE:101.#"
are you sending a message with a routing key #.MESSAGETYPE:101.# because that would be a bad idea. I presume not, but if you are don't!
Lets assume then that this is your binding key. I am not sure as I haven't done any testing of this specifically but the # before and after maybe causing some problems. You should think about a specification for your routing keys. Some format that they must conform to. It may be extendable but not completely free. That way you can have much more specific binding keys using * instead of # which will give a little more control.