I have a question about the method of RocektMQ ConsumeMessageOrderlyService.sendMessageBack, The method comment:max reconsume times exceeded then send to dead letter queue。
But the message was actually sent to %RETYE%ConsumerGroup, this means that the message will then be consumed by the same group of consumers,and I have tried, the message was indeed sent first to SCHEDULE_TOPIC_XXXX, then delivered to %RETRY%ConsumerGroup, and never sent to Dead-Letter Queue.
this is the source code of ConsumeMessageOrderlyService.sendMessageBack:
public boolean sendMessageBack(final MessageExt msg) {
try {
// max reconsume times exceeded then send to dead letter queue.
Message newMsg = new Message(MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup()), msg.getBody());
MessageAccessor.setProperties(newMsg, msg.getProperties());
String originMsgId = MessageAccessor.getOriginMessageId(msg);
MessageAccessor.setOriginMessageId(newMsg, UtilAll.isBlank(originMsgId) ? msg.getMsgId() : originMsgId);
newMsg.setFlag(msg.getFlag());
MessageAccessor.putProperty(newMsg, MessageConst.PROPERTY_RETRY_TOPIC, msg.getTopic());
MessageAccessor.setReconsumeTime(newMsg, String.valueOf(msg.getReconsumeTimes()));
MessageAccessor.setMaxReconsumeTimes(newMsg, String.valueOf(getMaxReconsumeTimes()));
MessageAccessor.clearProperty(newMsg, MessageConst.PROPERTY_TRANSACTION_PREPARED);
newMsg.setDelayTimeLevel(3 + msg.getReconsumeTimes());
this.defaultMQPushConsumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer().send(newMsg);
return true;
} catch (Exception e) {
log.error("sendMessageBack exception, group: " + this.consumerGroup + " msg: " + msg.toString(), e);
}
return false;
}
I am confused about it.
Related
We are sending mails in an email service with org.springframework.mail.javamail.JavaMailSender via an office 365 account and SMTP and set the following parameters in application.yml:
spring:
mail:
host: ${EMAIL_HOST:smtp.office365.com}
port: ${EMAIL_PORT:587}
username: ${EMAIL_USERNAME}
password: ${EMAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
starttls:
enable: true
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
The strange thing is: if we set the connectiontimeout to 5s, the service gets a response after 5s. If we set it to 20s, the o365 responds after 20s.
My expectation is that <connectiontimeout> is the maximum amount of time, that the sending may take and not the actual time.
Funny thing is that when setting another provider than office365, connectiontimeout works as expected.
Does anyone have this issue as well and maybe know how to solve that?
Our sender service:
#PostMapping
#ResponseStatus(HttpStatus.ACCEPTED)
public void sendMail(#RequestHeader(name = "X-API-KEY", required = true) String requestApiKey, #Valid #RequestBody EmailSendRequest email, HttpServletResponse response) {
if(!apiKey.equals(requestApiKey)){
LOGGER.error("Unauthorized api key" + requestApiKey);
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
try {
LOGGER.info("Received request to send mail Subject=[{}] To=[{}] From=[{}]", email.getSubject(), email.getTo(), email.getFrom());
MimeMessage message = mailSender.createMimeMessage();
message.setFrom(new InternetAddress(email.getFrom().getEmail()));
message.addRecipients(Message.RecipientType.TO, toAddressArray(email.getTo()));
message.addRecipients(Message.RecipientType.CC, toAddressArray(email.getCc()));
message.addRecipients(Message.RecipientType.BCC, toAddressArray(email.getBcc()));
message.setSubject(email.getSubject());
message.setSentDate(new Date());
Multipart multipart = new MimeMultipart();
MimeBodyPart messageText = new MimeBodyPart();
messageText.setContent(email.getContent().getValue(),
email.getContent().getType() == null ? DEFAULT_CONTENT_MIMETYPE : email.getContent().getType());
multipart.addBodyPart(messageText);
addAttachments(multipart, email.getAttachments());
message.setContent(multipart);
if(message.getRecipients(Message.RecipientType.TO) != null ||
message.getRecipients(Message.RecipientType.CC) != null ||
message.getRecipients(Message.RecipientType.BCC) != null)
{
mailSender.send(message);
}
else {
LOGGER.warn("Email not send! No recipients or all ignored.");
response.setHeader("X-Ignored","true");
}
LOGGER.info("Mail Subject=[{}] To=[{}}] From=[{}] successfully sent.",email.getSubject(),email.getTo(),email.getFrom());
} catch (MessagingException e) {
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]:", email.getSubject(), email.getTo(), email.getFrom(), e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
} catch (MailSendException mailSendException) {
Exception[] exceptions = mailSendException.getMessageExceptions();
for (Exception e : exceptions){
if (e instanceof SMTPSendFailedException && (((SMTPSendFailedException)e).getReturnCode() == 554)){
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]: This sender mail address is not allowed.", email.getSubject(), email.getTo(), email.getFrom());
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
}
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]:", email.getSubject(), email.getTo(), email.getFrom(), mailSendException);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
} catch (MailAuthenticationException e) {
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]: Wrong SMTP login credentials provided. \nMSG:{}", email.getSubject(), email.getTo(), email.getFrom(),e.getMessage());
throw new ResponseStatusException(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
}
}
It seems, that the SocketFactory was responsible for this behaviour. Removing the following lines from application.yml makes the application work as expected:
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
I am using the following code to consume a Redis stream using a Spring Data Redis consumer group, but even though I have commented out the acknowledge command, my messages are not re-read after a server restart.
I would expect that if I didn't acknowledge the message, it should be re-read when the server gets killed and restarted. What am I missing here?
#Bean
#Autowired
public StreamMessageListenerContainer eventStreamPersistenceListenerContainerTwo(RedisConnectionFactory streamRedisConnectionFactory, RedisTemplate streamRedisTemplate) {
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String, String, String>> containerOptions = StreamMessageListenerContainer.StreamMessageListenerContainerOptions
.builder().pollTimeout(Duration.ofMillis(100)).build();
StreamMessageListenerContainer<String, MapRecord<String, String, String>> container = StreamMessageListenerContainer.create(streamRedisConnectionFactory,
containerOptions);
container.receive(Consumer.from("my-group", "my-consumer"),
StreamOffset.create("event-stream", ReadOffset.latest()),
message -> {
System.out.println("MessageId: " + message.getId());
System.out.println("Stream: " + message.getStream());
System.out.println("Body: " + message.getValue());
//streamRedisTemplate.opsForStream().acknowledge("my-group", message);
});
container.start();
return container;
}
After reading the Redis documentation on how streams work, I came up with the following to automatically process any unacknowledged but previously delivered messages for the consumer:
// Check for any previously unacknowledged messages that were delivered to this consumer.
log.info("STREAM - Checking for previously unacknowledged messages for " + this.getClass().getSimpleName() + " event stream listener.");
String offset = "0";
while ((offset = processUnacknowledgedMessage(offset)) != null) {
log.info("STREAM - Finished processing one unacknowledged message for " + this.getClass().getSimpleName() + " event stream listener: " + offset);
}
log.info("STREAM - Finished checking for previously unacknowledged messages for " + this.getClass().getSimpleName() + " event stream listener.");
And the method that processes the messages:
/**
* Processes, and acknowledges the next previously delivered message, beginning
* at the given message id offset.
*
* #param offset The last read message id offset.
* #return The message that was just processed, or null if there are no more messages.
*/
public String processUnacknowledgedMessage(String offset) {
List<MapRecord> messages = streamRedisTemplate.opsForStream().read(Consumer.from(groupName(), consumerName()),
StreamReadOptions.empty().noack().count(1),
StreamOffset.create(streamKey(), ReadOffset.from(offset)));
String lastMessageId = null;
for (MapRecord message : messages) {
if (log.isDebugEnabled()) log.debug(String.format("STREAM - Processing event(%s) from stream(%s) during startup: %s", message.getId(), message.getStream(), message.getValue()));
processRecord(message);
if (log.isDebugEnabled()) log.debug(String.format("STREAM - Finished processing event(%s) from stream(%s) during startup.", message.getId(), message.getStream()));
streamRedisTemplate.opsForStream().acknowledge(groupName(), message);
lastMessageId = message.getId().getValue();
}
return lastMessageId;
}
I have this subscriber code:
try {
//subscriber
syncSubscriber.createSubscriber(SdkServiceConfig.s.SUBSCRIPTION_NAME_PARTNER_REQUEST);
final List<ReceivedMessage> messages = syncSubscriber.fetch(10, true);//get all current messages.
List<String> ackIds = new ArrayList<>();
for (ReceivedMessage message : messages) {
requestToCofmanSender.receiveMessage(message.getMessage());
ackIds.add(message.getAckId());
}
//preferred bulk ack, due to network performance
syncSubscriber.sendAck(ackIds);
requestToCofmanSender.getWazePublisher().shutdown();
}
and
public void sendAck(Collection<String> ackIdList) {
if (ackIdList != null && ackIdList.size() != 0) {
String subscriptionName = SubscriptionName.format(this.getProjectId(), this.subscriptionId);
AcknowledgeRequest acknowledgeRequest = AcknowledgeRequest.newBuilder().setSubscription(subscriptionName).addAllAckIds(ackIdList).build();
this.subscriber.acknowledgeCallable().call(acknowledgeRequest);
}
}
I poll the pubsub queue in loop
and even though the code sends ack i still get the same messages.
how should i ack otherwise?
My problem was that i had a break point between receiving the message and sending ack. My pubsub was configured to 10 seconds timeout.
We are sending audit log messages to a RabbitMQ cluster which is sometimes unavailable for reasons we cannot influence.
When the queue is not available, log messages start to accumulate locally and we get a out-of-memory eventually on the client.
We are using a AMQP Appender to submit our messages.
Is there a way we can query the count of pending log messages and raise an alert when messages start adding up?
Well, it isn't possible. There is just no any hooks to do that.
You can consider, though, to decrease maxSenderRetries from default 30 to 1 or 2. After that you'll start to lose log messages:
int retries = event.incrementRetries();
if (retries < AmqpAppender.this.maxSenderRetries) {
// Schedule a retry based on the number of times I've tried to re-send this
AmqpAppender.this.retryTimer.schedule(new TimerTask() {
#Override
public void run() {
AmqpAppender.this.events.add(event);
}
}, (long) (Math.pow(retries, Math.log(retries)) * 1000));
}
else {
addError("Could not send log message " + logEvent.getMessage()
+ " after " + AmqpAppender.this.maxSenderRetries + " retries", e);
}
We might have to expose queueSize option instead of default:
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
Feel free to raise a JIRA on the matter.
I am trying to remove messages from JMS queue on Wildfly 9.0.2 (JBoss) using JMX, see following code:
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
try {
String name = "jboss.as.expr:subsystem=messaging,hornetq-server=default,jms-queue=MyQueue";
ObjectName objectName = new ObjectName(objectNameString);
String result = (String) server.invoke(objectName, "removeMessages", new Object[]{null},
new String[]{"java.lang.String"});
return result;
} catch (MalformedObjectNameException | InstanceNotFoundException | MBeanException | ReflectionException ignored) {
log.errorv(ignored, "Error removing messages from JMS queue [{0}]", name);
return null;
}
There is an active consumer on that queue. The code runs without exception and returns string "0", but no messages are actually removed. I tried also to use some value as message filter (*), but got failure:
javax.management.ReflectionException: "HQ119020: Invalid filter: *"
Any idea how to remove the messages?