我正在尝试找出处理可能在服务中发生的错误的最佳方法,该服务在发生聚合的组超时之后被调用,该服务模仿与满足releaseExpression相同的流程。
这是我的设置:
我有一个AmqpInboundChannelAdapter,可以接收消息并将其发送到我的聚合器。
满足releaseExpression且在groupTimeout到期之前,如果在我的ServiceActivator中引发了异常,则该消息将被发送到该MessageGroup中所有消息的死信队列中。 (在下面的示例中有10条消息,仅用于说明目的)这就是我所期望的。
如果尚未满足我的releaseExpression但已满足groupTimeout且该组超时,则如果在我的ServiceActivator中抛出异常,则消息将不会发送到我的死信队列中并被确认。
在阅读另一篇博客文章之后, link1 它提到发生这种情况是因为该处理发生在MessageGroupStoreReaper的另一个线程中,而不是SimpleMessageListenerContainer所在的线程中。一旦处理从SimpleMessageListener的线程移开,消息将被自动确认。
我添加了上面链接中提到的配置,并看到错误消息被发送到我的错误处理程序。我的主要问题是,什么是处理这种情况以最大程度地减少消息丢失的最佳方法。
以下是我正在探索的选项:
在我的自定义错误处理程序中使用BatchRabbitTemplate将失败的消息发布到如果满足releaseExpression时将进入的相同死信队列。 (这是我在下面概述的方法,但是如果发布期间发生错误,我担心消息会丢失)
调查是否可以解决这个问题,我可以让SimpleMessageListener知道发生的错误,然后将失败的消息批次发送到死信队列?我怀疑这是否可行,因为似乎已经确认了该消息。
请不要将SimpleMessageListenerContainer设置为AcknowledgeMode.AUTO,并在满足releaseExpression或groupTimeOut发生时通过服务对消息进行手动确认。 (这似乎有些混乱,因为MessageGroup中可能有1..N条消息,但想看看其他人做了什么)
理想情况下,我想拥有一个流程,该流程将在遇到releaseExpression时模仿相同的流程,以使消息不会丢失。
有人对过去使用过的这种情况的最佳方式提出建议吗?
感谢您的帮助和/或建议!
这是我当前使用Spring Integration DSL的配置
@Bean
public SimpleMessageListenerContainer workListenerContainer() {
SimpleMessageListenerContainer container =
new SimpleMessageListenerContainer(rabbitConnectionFactory);
container.setQueues(worksQueue());
container.setConcurrentConsumers(4);
container.setDefaultRequeueRejected(false);
container.setTransactionManager(transactionManager);
container.setChannelTransacted(true);
container.setTxSize(10);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
return container;
}
@Bean
public AmqpInboundChannelAdapter inboundRabbitMessages() {
AmqpInboundChannelAdapter adapter = new AmqpInboundChannelAdapter(workListenerContainer());
return adapter;
}
我已经定义了一个错误通道,并定义了自己的TaskScheduler用于MessageStoreRepear
@Bean
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler ts = new ThreadPoolTaskScheduler();
MessagePublishingErrorHandler mpe = new MessagePublishingErrorHandler();
mpe.setDefaultErrorChannel(myErrorChannel());
ts.setErrorHandler(mpe);
return ts;
}
@Bean
public PollableChannel myErrorChannel() {
return new QueueChannel();
}
public IntegrationFlow aggregationFlow() {
return IntegrationFlows.from(inboundRabbitMessages())
.transform(Transformers.fromJson(SomeObject.class))
.aggregate(a->{
a.sendPartialResultOnExpiry(true);
a.groupTimeout(3000);
a.expireGroupsUponCompletion(true);
a.expireGroupsUponTimeout(true);
a.correlationExpression("T(Thread).currentThread().id");
a.releaseExpression("size() == 10");
a.transactional(true);
}
)
.handle("someService", "processMessages")
.get();
}
这是我的自定义错误流
@Bean
public IntegrationFlow errorResponse() {
return IntegrationFlows.from("myErrorChannel")
.<MessagingException, Message<?>>transform(MessagingException::getFailedMessage,
e -> e.poller(p -> p.fixedDelay(100)))
.channel("myErrorChannelHandler")
.handle("myErrorHandler","handleFailedMessage")
.log()
.get();
}
这是自定义错误处理程序
@Component
public class MyErrorHandler {
@Autowired
BatchingRabbitTemplate batchingRabbitTemplate;
@ServiceActivator(inputChannel = "myErrorChannelHandler")
public void handleFailedMessage(Message<?> message) {
ArrayList<SomeObject> payload = (ArrayList<SomeObject>)message.getPayload();
payload.forEach(m->batchingRabbitTemplate.convertAndSend("some.dlq","#", m));
}
}
这是BatchingRabbitTemplate bean
@Bean
public BatchingRabbitTemplate batchingRabbitTemplate() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5);
scheduler.initialize();
BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(10, Integer.MAX_VALUE, 30000);
BatchingRabbitTemplate batchingRabbitTemplate = new BatchingRabbitTemplate(batchingStrategy, scheduler);
batchingRabbitTemplate.setConnectionFactory(rabbitConnectionFactory);
return batchingRabbitTemplate;
}
更新1 )以显示自定义MessageGroupProcessor:
public class CustomAggregtingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor {
@Override
protected final Object aggregatePayloads(MessageGroup group, Map<String, Object> headers) {
return group;
}
}
示例服务:
@Slf4j
public class SomeService {
@ServiceActivator
public void processMessages(MessageGroup messageGroup) throws IOException {
Collection<Message<?>> messages = messageGroup.getMessages();
//Do business logic
//ack messages in the group
for (Message<?> m : messages) {
com.rabbitmq.client.Channel channel = (com.rabbitmq.client.Channel)
m.getHeaders().get("amqp_channel");
long deliveryTag = (long) m.getHeaders().get("amqp_deliveryTag");
log.debug(" deliveryTag = {}",deliveryTag);
log.debug("Channel = {}",channel);
channel.basicAck(deliveryTag, false);
}
}
}
更新的IntegrationFlow
public IntegrationFlow aggregationFlowWithCustomMessageProcessor() {
return IntegrationFlows.from(inboundRabbitMessages()).transform(Transformers.fromJson(SomeObject.class))
.aggregate(a -> {
a.sendPartialResultOnExpiry(true);
a.groupTimeout(3000);
a.expireGroupsUponCompletion(true);
a.expireGroupsUponTimeout(true);
a.correlationExpression("T(Thread).currentThread().id");
a.releaseExpression("size() == 10");
a.transactional(true);
a.outputProcessor(new CustomAggregtingMessageGroupProcessor());
}).handle("someService", "processMessages").get();
}
使用新的ErrorHandler进行小操作
public class MyErrorHandler {
@ServiceActivator(inputChannel = "myErrorChannelHandler")
public void handleFailedMessage(MessageGroup messageGroup) throws IOException {
if(messageGroup!=null) {
log.debug("Nack messages size = {}", messageGroup.getMessages().size());
Collection<Message<?>> messages = messageGroup.getMessages();
for (Message<?> m : messages) {
com.rabbitmq.client.Channel channel = (com.rabbitmq.client.Channel)
m.getHeaders().get("amqp_channel");
long deliveryTag = (long) m.getHeaders().get("amqp_deliveryTag");
log.debug("deliveryTag = {}",deliveryTag);
log.debug("channel = {}",channel);
channel.basicNack(deliveryTag, false, false);
}
}
}
}
更新2 添加了自定义的ReleaseStratgedy并将其更改为aggegator
public class CustomMeasureGroupReleaseStratgedy implements ReleaseStrategy {
private static final int MAX_MESSAGE_COUNT = 10;
public boolean canRelease(MessageGroup messageGroup) {
return messageGroup.getMessages().size() >= MAX_MESSAGE_COUNT;
}
}
public IntegrationFlow aggregationFlowWithCustomMessageProcessorAndReleaseStratgedy() {
return IntegrationFlows.from(inboundRabbitMessages()).transform(Transformers.fromJson(SomeObject.class))
.aggregate(a -> {
a.sendPartialResultOnExpiry(true);
a.groupTimeout(3000);
a.expireGroupsUponCompletion(true);
a.expireGroupsUponTimeout(true);
a.correlationExpression("T(Thread).currentThread().id");
a.transactional(true);
a.releaseStrategy(new CustomMeasureGroupReleaseStratgedy());
a.outputProcessor(new CustomAggregtingMessageGroupProcessor());
}).handle("someService", "processMessages").get();
}
答案 0 :(得分:0)
您的理解中存在一些缺陷。如果使用AUTO,则在发生异常时仅最后一条消息将变成死字母。在发布之前成功存储在组中的邮件将被立即确认。
实现所需目标的唯一方法是使用MANUAL插口。
没有办法“告诉侦听器容器将消息发送到DLQ”。容器从不将消息发送到DLQ,它拒绝消息,然后代理将其发送到DLX / DLQ。