Spring integration 如何防止ListenerExecutionFailedException:侦听器引发异常

Spring integration 如何防止ListenerExecutionFailedException:侦听器引发异常,spring-integration,spring-batch,spring-amqp,Spring Integration,Spring Batch,Spring Amqp,我需要做什么来防止以下异常,这可能是由RabbitMQ引发的 org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFaile

我需要做什么来防止以下异常,这可能是由RabbitMQ引发的

org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:877)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:787)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:707)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:98)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:189)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1236)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:688)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1190)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1174)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1200(SimpleMessageListenerContainer.java:98)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1363)
    at java.lang.Thread.run(Thread.java:748)
    Caused by: org.springframework.messaging.MessageDeliveryException: failed to send Message to channel 'amqpLaunchSpringBatchJobFlow.channel#0'; nested exception is jp.ixam_drive.batch.service.JobExecutionRuntimeException: Failed to start job with name ads-insights-import and parameters {accessToken=<ACCESS_TOKEN>, id=act_1234567890, classifier=stats, report_run_id=1482330625184792, job_request_id=32}
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:449)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
    at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:171)
    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter.access$400(AmqpInboundChannelAdapter.java:45)
    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$1.onMessage(AmqpInboundChannelAdapter.java:95)
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:784)
    ... 10 common frames omitted
    Caused by: jp.ixam_drive.batch.service.JobExecutionRuntimeException: Failed to start job with name ads-insights-import and parameters {accessToken=<ACCESS_TOKEN>, id=act_1234567890, classifier=stats, report_run_id=1482330625184792, job_request_id=32}
    at jp.ixam_drive.facebook.SpringBatchLauncher.launchJob(SpringBatchLauncher.java:42)
    at jp.ixam_drive.facebook.AmqpBatchLaunchIntegrationFlows.lambda$amqpLaunchSpringBatchJobFlow$1(AmqpBatchLaunchIntegrationFlows.java:71)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
    ... 18 common frames omitted
    Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={accessToken=<ACCESS_TOKEN>, id=act_1234567890, classifier=stats, report_run_id=1482330625184792, job_request_id=32}.  If you want to run this job again, change the parameters.
    at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:126)
    at sun.reflect.GeneratedMethodAccessor193.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.batch.core.repository.support.AbstractJobRepositoryFactoryBean$1.invoke(AbstractJobRepositoryFactoryBean.java:172)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy125.createJobExecution(Unknown Source)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:125)
    at jp.ixam_drive.batch.service.JobOperationsService.launch(JobOperationsService.java:64)
    at jp.ixam_drive.facebook.SpringBatchLauncher.launchJob(SpringBatchLauncher.java:37)
    ... 24 common frames omitted
启动两个实例后,不会立即引发异常,而是在一段时间后引发。启动Spring批处理作业确实成功,但随后开始失败,作业实例已存在且已完成

该工作用于检索facebook广告结果

如果您能深入了解造成上述错误的原因,我将不胜感激

我也有这个配置,它不使用AMQP,工作没有任何问题,但它只用于一个实例

@Configuration
@Conditional(SimpleBatchLaunchCondition.class)
@Slf4j
public class SimpleBatchLaunchIntegrationFlows {

    @Autowired
    SpringBatchLauncher batchLauncher;

    @Autowired
    DataSource dataSource;

    @Bean(name = "batch_launch_channel")
    public MessageChannel batchLaunchChannel() {
        return MessageChannels.queue(jdbcChannelMessageStore(), "batch_launch_channel").get();
    }

    @Bean
    public ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider() {
        return new MySqlChannelMessageStoreQueryProvider();
    }

    @Bean
    public JdbcChannelMessageStore jdbcChannelMessageStore() {
        JdbcChannelMessageStore channelMessageStore = new JdbcChannelMessageStore(dataSource);
        channelMessageStore.setChannelMessageStoreQueryProvider(channelMessageStoreQueryProvider());
        channelMessageStore.setUsingIdCache(true);
        channelMessageStore.setPriorityEnabled(true);
        return channelMessageStore;
    }

    @Bean
    public IntegrationFlow launchSpringBatchJobFlow(@Qualifier("batch_launch_channel")
            MessageChannel batchLaunchChannel) {
        return IntegrationFlows.from(batchLaunchChannel)
                .handle(message -> {
                    String jobName = (String) message.getHeaders().get("job_name");
                    JobParameters jobParameters = (JobParameters) message.getPayload();
                    batchLauncher.launchJob(jobName, jobParameters);
                }, e->e.poller(Pollers.fixedRate(500).receiveTimeout(500))).get();
    }
}

请参阅Spring批处理文档。启动作业的新实例时,作业参数必须唯一

一个常见的解决方案是添加一个UUID或类似的伪参数,但batch提供了一种策略,例如每次增加一个数值参数

编辑

有一类例外情况,其中成员被认为是无法恢复的,并且尝试重新交付是没有意义的

示例包括MessageConversionException——如果我们不能在第一次转换它,我们可能无法在重新交付时转换。ConditionalRejectingErrorHandler是一种机制,通过它我们可以检测此类异常,并导致它们被永久拒绝,而不是重新交付

其他异常会导致默认情况下重新传递消息-存在另一个属性defaultRequeuRejected,可以将其设置为false以永久拒绝所有不建议的失败

您可以通过子类化其DefaultExceptionStrategy-override isUserCauseFatalThrowable cause来自定义错误处理程序,以扫描原因树以查找JobInstanceAlreadyCompleteException并返回true cause.getCause.getCause instanceof

我认为它是由运行已异常的SpringBatch作业引发的错误触发的

这仍然表明您以某种方式收到了具有相同参数的第二条消息;这是另一个错误,因为原始作业仍在运行;该消息将被拒绝并重新查询,但在随后的交付中,您将获得已完成的异常

因此,我仍然认为问题的根本原因是重复的请求,但是您可以使用通道适配器的侦听器容器中的自定义错误处理程序来避免这种行为


我建议您记录重复的消息,这样您就可以找出收到这些消息的原因。

谢谢您的回复。我知道这不是作业参数,因为每个正在执行的作业参数都有一个唯一可识别的作业参数组合。作业确实执行了一段时间,并且没有错误地完成了一段时间,直到抛出此异常,并且当我开始在控制台中看到这两个实例可能从AMQP中拾取相同的作业可识别参数时,这在抛出上述异常之前不会发生。我认为Listener抛出的异常就是这种情况,我想知道如何防止这种情况。这是明确的。如果出现该异常,则尝试使用相同的参数启动作业。这与rabbitmq无关。调试日志应该可以帮助您跟踪它。我看到的是日志2017-10-07 10:26:24.572 WARN 55577-[erContainer0-1]s.a.r.l.ConditionalRejectingErrorHandler:兔子消息侦听器的执行失败。根据文档,它将有条件地将异常包装在AmqpRejectAndDontRequeueException中,我猜这会导致使用相同参数重新运行相同的作业。什么是AmqpRejectAndDontRequeueException?不要紧,我认为它是由运行exception的SpringBatch作业引发的错误触发的。我不明白的是,作业执行完成了数百个作业,然后它突然开始抛出上述异常。它似乎与2017-10-07 11:51:27.571 WARN 59922-[erContainer0-1]s.a.r.l.ConditionalRejectingErrorHandler:兔子消息侦听器的执行失败有关。发生这种情况时,会重新提取相同的消息,因此会引发Spring批处理异常。我添加了不同的@Configuration,该@Configuration不使用AMQPSimpleBatchLaunchIntegrationFlows,但仅用于单个实例,因此不会共享工作
   1. channel[async_ads_insights] ->IntegrationFlow[amqpOutboundAsyncAdsInsights]->[AMQP]->IntegrationFlow[amqpAdsInsightsAsyncJobRequestFlow]->channel[ad_report_run_polling_channel]->IntegrationFlow[adReportRunPollingLoopFlow]-IF END LOOP->channel[batch_launch_channel] ELSE -> channel[ad_report_run_polling_channel]

   2. channel[batch_launch_channel] -> IntegrationFlow[amqpOutbound]-> IntegrationFlow[amqpLaunchSpringBatchJobFlow]

   3. Spring Batch Job is launched.
@Configuration
@Conditional(SimpleBatchLaunchCondition.class)
@Slf4j
public class SimpleBatchLaunchIntegrationFlows {

    @Autowired
    SpringBatchLauncher batchLauncher;

    @Autowired
    DataSource dataSource;

    @Bean(name = "batch_launch_channel")
    public MessageChannel batchLaunchChannel() {
        return MessageChannels.queue(jdbcChannelMessageStore(), "batch_launch_channel").get();
    }

    @Bean
    public ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider() {
        return new MySqlChannelMessageStoreQueryProvider();
    }

    @Bean
    public JdbcChannelMessageStore jdbcChannelMessageStore() {
        JdbcChannelMessageStore channelMessageStore = new JdbcChannelMessageStore(dataSource);
        channelMessageStore.setChannelMessageStoreQueryProvider(channelMessageStoreQueryProvider());
        channelMessageStore.setUsingIdCache(true);
        channelMessageStore.setPriorityEnabled(true);
        return channelMessageStore;
    }

    @Bean
    public IntegrationFlow launchSpringBatchJobFlow(@Qualifier("batch_launch_channel")
            MessageChannel batchLaunchChannel) {
        return IntegrationFlows.from(batchLaunchChannel)
                .handle(message -> {
                    String jobName = (String) message.getHeaders().get("job_name");
                    JobParameters jobParameters = (JobParameters) message.getPayload();
                    batchLauncher.launchJob(jobName, jobParameters);
                }, e->e.poller(Pollers.fixedRate(500).receiveTimeout(500))).get();
    }
}