Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading SpringBoot:手动确认多条AMQP消息_Multithreading_Spring Boot_Rabbitmq_Spring Amqp_Spring Rabbit - Fatal编程技术网

Multithreading SpringBoot:手动确认多条AMQP消息

Multithreading SpringBoot:手动确认多条AMQP消息,multithreading,spring-boot,rabbitmq,spring-amqp,spring-rabbit,Multithreading,Spring Boot,Rabbitmq,Spring Amqp,Spring Rabbit,只有在成功处理和存储多条消息之后,我才需要在rabbit侦听器中手动确认这些消息。使用的Spring引导配置如下所示 listener: concurrency: 2 max-concurrency: 20 acknowledge-mode: manual prefetch: 30 class DeliveredMesssagingEvent { int deliveryTag Event event } class ChannelData { Channel c

只有在成功处理和存储多条消息之后,我才需要在rabbit侦听器中手动确认这些消息。使用的Spring引导配置如下所示

listener:
  concurrency: 2
  max-concurrency: 20
  acknowledge-mode: manual
  prefetch: 30
class DeliveredMesssagingEvent {
  int deliveryTag
  Event event
}

class ChannelData {
  Channel channel
  List<DeliveredEvent> deliveredEvents = new ArrayList<>()

  ChannelData(Channel channel) {
    this.channel = channel
  }

  void addEvent(long tag, Event event) {
    deliveredEvents.add(new DeliveredEvent(deliveryTag: tag, event: event))
  }
}
消息应一次存储20个批次。只有在成功存储它们时,才应发送多个ack。还有一个与存储机制相关联的超时,它应该在20秒后存储消息,即使没有20条消息。目前,我有以下代码

@Slf4j
@Component    
class EventListener {

  @Autowired
  private EventsStorage eventsStorage
  private ConcurrentMap<Integer, ChannelData> channelEvents = new ConcurrentHashMap<>()

  @RabbitListener(queues = 'event-queue')
  void processEvent(@Payload Event event, Channel channel, @Header(DELIVERY_TAG) long tag) {
    log.debug("Event received for channel $channel.channelNumber")
    channelEvents.compute(channel.channelNumber, { k, channelData -> addEventAndStoreIfNeeded(channel, event, tag, channelData) })
  }

  private ChannelData addEventAndStoreIfNeeded(Channel channel, Event event, long tag, ChannelData channelData) {
    if (channelData) {
      channelData.addEvent(tag, event)
      if (channelData.getDeliveredEvents().size() >=   batchSize) {
        storeAndAckChannelEvents(channel.channelNumber)
      }
      return channelData
    } else {
      ChannelData newChannelData = new ChannelData(channel)
      newChannelData.addEvent(tag, event)
      return newChannelData
    }
  }

  void storeAndAckChannelEvents(Integer channelNumber) {
    channelEvents.compute(channelNumber, { k, channelData ->
      List<DeliveredEvent> deliveredEvents = channelData.deliveredEvents
      if (!deliveredEvents.isEmpty()) {
        def events = deliveredEvents.stream()
          .map({ DeliveredEvent deliveredEvent -> deliveredEvent.event })
          .collect(Collectors.toList())

        eventsStorage.store(events)
        long lastDeliveryTag = deliveredEvents.get(deliveredEvents.size() - 1).deliveryTag
        channelData.channel.basicAck(lastDeliveryTag, true)
        deliveredEvents.clear()
      }
    })
  }

  @Scheduled(fixedRate = 20000L)
  void storeMessagingEvents() {
    channelEvents.forEach({ k, channelData -> storeAndAckChannelEvents(channelData) })
  }

}
使用的
频道是
com.rabbitmq.client.Channel
。关于此接口状态:

通道实例不能在线程之间共享。应用程序应该更喜欢每个线程使用一个通道,而不是跨多个线程共享同一个通道

所以,我的做法正好相反,在
调度程序
SimpleMessageListenerContainer
工作线程之间共享通道。我的应用程序的输出如下所示:

[SimpleAsyncTaskExecutor-3] DEBUG EventListener - Event received for channel 2 
[SimpleAsyncTaskExecutor-4] DEBUG EventListener - Event received for channel 3 
[SimpleAsyncTaskExecutor-5] DEBUG EventListener - Event received for channel 1 
[SimpleAsyncTaskExecutor-1] DEBUG EventListener - Event received for channel 5 
[SimpleAsyncTaskExecutor-2] DEBUG EventListener - Event received for channel 4 
[SimpleAsyncTaskExecutor-3] DEBUG EventListener - Event received for channel 2 
[SimpleAsyncTaskExecutor-1] DEBUG EventListener - Event received for channel 5 
[SimpleAsyncTaskExecutor-2] DEBUG EventListener - Event received for channel 4 
[SimpleAsyncTaskExecutor-3] DEBUG EventListener - Event received for channel 2
[pool-4-thread-1] DEBUG EventListener - Storing channel 5 events 
[pool-4-thread-1] DEBUG EventListener - Storing channel 2 events 
...
SimpleMessageListenerContainer
工作线程有自己的通道,不会随时间而改变

考虑到我同步了
调度程序
SimpleMessageListenerContainer
工作线程,是否有人看到此代码不具有线程安全性的原因


在Spring boot中是否有其他方法可以尝试手动确认多条消息?

只要同步线程,就可以了

但是,请记住,如果连接丢失,您将获得一个新的使用者,并且您的同步线程将具有过时的数据(未确认的消息将被重新传递)

但是,您也可以使用

当使用者线程空闲了一段时间后,事件将在同一侦听器线程上发布,因此您可以在那里执行定时确认,而不必担心同步问题

您还可以检测连接是否丢失