Java RabbitMQ主题交换消息排序

Java RabbitMQ主题交换消息排序,java,rabbitmq,amqp,spring-amqp,spring-rabbit,Java,Rabbitmq,Amqp,Spring Amqp,Spring Rabbit,在中可以找到: AMQP 0-9-1核心规范的第4.7节解释了保证排序的条件:在一个通道中发布的消息,通过一个交换机、一个队列和一个传出通道,将以与发送相同的顺序接收。RabbitMQ自2.7.0版以来提供了更强的保证 但是如果存在类似于exchange1->exchange2->Queue 1的绑定,该怎么办呢 订购是否仍有保证? 我们假设确实如此,但我们在应用程序中发现情况可能并非如此。我们使用spring-rabbit-2.1.6-RELEASE(它使用amqp-client-5.4.3)

在中可以找到:

AMQP 0-9-1核心规范的第4.7节解释了保证排序的条件:在一个通道中发布的消息,通过一个交换机、一个队列和一个传出通道,将以与发送相同的顺序接收。RabbitMQ自2.7.0版以来提供了更强的保证

但是如果存在类似于
exchange1->exchange2->Queue 1
的绑定,该怎么办呢

订购是否仍有保证?

我们假设确实如此,但我们在应用程序中发现情况可能并非如此。我们使用
spring-rabbit-2.1.6-RELEASE
(它使用
amqp-client-5.4.3

出版商、装订商和消费者如下:

Client 1 publishes to Exchange 1 -> Exchange 2 -> Queue 1 - consumed by Client 2
                                 -> Queue 2 - consumed by Client 3
我们可以看到,
Client 1
按以下顺序发布3条消息:

  • 消息1
  • 消息2
  • 消息3
但是客户端2和客户端3都按以下顺序接收消息:

  • 消息3
  • 消息1
  • 消息2
编辑1(弹簧配置) 对于发布服务器(
Client 1
),使用了以下XML配置(rabbit的
ConnectionFactory
)上没有设置额外的属性:


发布通过以下方式完成:

AmqpTemplate::发送(字符串交换、字符串路由键、消息)
专用线程中

Client 2
使用默认的spring配置和
SimpleMessageListenerContainer

客户端3
实际上不是我们的应用程序,所以我不知道真正的设置。就是他们向我们报告了一个错误,消息排序不正确

当然,我们仍然有可能记录了带有一些bug的消息发布。但是我三次检查了它-它来自一个线程,每个消息的自定义头中都有序列号,在
客户端1
上正确递增

编辑2 我做了进一步分析,以找出错误消息排序的频率。结果如下:

我在事件发生前后+-2小时内(总共4小时)获取了日志和数据,共发送了42706条消息,其中只有3条在
客户端2上排序错误。所有3条消息均在7毫秒的间隔内发送

然后,我随机选择了另一个长达14小时的时间窗。有531904条消息被发送,并且
Client 2
正确的顺序接收所有消息。平均消息速率约为每秒11条消息

消息的分布不均匀,因此7毫秒内的3条消息没有什么特别之处-恰恰相反。通常在3-5毫秒内会发送多条消息

从这个分析中,我认为兔子群上发生了一些奇怪的事情。不幸的是,我再也没有它的日志了

某种种族状况的可能性在我看来非常低

谢谢,


Frank

Spring AMQP使用缓存作为通道;在多线程环境中,无法保证同一线程始终使用相同的通道;因此不能保证订购

在当前版本中,解决方案是使用它,它将保证一系列出版物在同一频道上出现,并保证顺序


在下一个版本(2.3,今年晚些时候发布)中,我们还添加了具有相同功能的。这种情况再次发生,我们能够找到答案

在整个过程中,负责频道娱乐的是兔子健康指标,因此负责错误的排序。有一个作业定期调用health endpoint

正如Gary正确提到的:


Spring AMQP为通道使用缓存;在多线程环境中,无法保证同一线程始终使用相同的通道;因此不能保证订购

健康状态是从不同的线程检查的,它使用生产者的通道

作为短期解决方案,这将起作用:

management.health.rabbit.enabled=false
如果生产者是真正的单线程,并且连接工厂的设置如描述中所示,则可以保证排序

另一个(可能是合适的)解决方案是创建单独的
连接工厂
,并且不使用兔子健康检查的自动配置

@Bean(“rabbitHealthIndicator”)
公共健康指示器rabbitHealthIndicator(连接工厂健康检查连接工厂){
rabbitmplate rabbitmplate=new-rabbitmplate(healthCheckConnectionFactory);//确保它与保证排序的连接工厂不同
返回新的RabbitHealthIndicator(rabbitTemplate);
}
这就成功了

干杯,谢谢加里的帮助


弗兰克

非常感谢你,加里。我们为此线程使用专用模板和连接工厂。连接工厂已定义通道缓存大小=“1”缓存模式=“通道”。我们只使用AmqpTemplate::send(字符串交换、字符串路由键、消息消息)。这不意味着只使用一个频道吗?对于corePoolSize=5的所有(3)个工厂都设置了一个公共执行器(但它只用于直接回复容器,不是吗?)是的;通过这些设置,您应该只能看到一个频道正在使用。遗嘱执行人与此无关。谢谢您的及时回复。在这种情况下,我仍然不清楚这些消息是如何被洗牌的。有什么想法吗?我没有,对不起;我唯一的另一个建议是添加一个工厂时间限制(
cha