RabbitMQ消息可靠性保证机制6--可靠性分析

在使用消息中间件的过程中,难免会出现消息错误或者消息丢失等异常情况。这个时候就需要有一个良好的机制来跟踪记录消息的过程(轨迹溯源),帮助我们排查问题。

在RabbitMQ中可以使用Firehose实现消息的跟踪,Firehose可以记录每一次发送或者消息的记录,方便RabbitMQ的使用都进行调试、排错等。

FireHose的原理是将生产者投递给RabbitMQ的消息,或者RabbitMQ投递给消费者的消息按照指定的格式,发送到默认交换器上,这个默认交换器的名称是:amq.rabbitmq.trace它是一个topic类型的交换器。发送到交换器的消息的路由键为publis.{exchangename}deliver.{queuename}。其中exchangename和queuename为交换器和队列名字。分别对应生产者投递到交换器的消息和消费者从队列中获取的消息。

上图是一个样例。生产者将消息发送至trace.ex交换器,交换器将消息路由至trace.qu这个队列,然后由消息者将消息取走。当消息到达trace.ex这个队列后,消息就会投递一份到名称为amq.rabbitmq.trace的交换器,按收到交换器的名称加上一个前缀变更publish.trace.ex作为路由的KEY,投递一份至publishtrace这个队列中;接收消息也样如此,当消费都取走消息时,会将消息发送一份到名称为amq.rabbitmq.trace的交换器,按消费者队列的名称加一个前缀变成deliver.trace.qu作为路由的KEY,投递至delivertrace这个队列中。

Firehose命令:

sh 复制代码
# 开启命令
rabbitmqctl trace_on [-p vhose]
# [-p vhose]是可选参数,用来指定虚拟主机的vhose

# 关闭命令
rabbitmqctl trace_off [-p vhose]

Firehose默认情况下处于关闭状态,并且Firehose的状态是非持久化的,会在RabbitMQ服务重启的时候还原成默认的状态。Firehose开启之后会影响RabbitMQ整体服务性能,因为它会引起额外的消息生成、路由和存储 。

7.9.1 Firehose验证

首先开启追溯

sh 复制代码
[root@nullnull-os rabbitmq]# rabbitmqctl trace_on -p / 
Starting tracing for vhost "/" ...
Trace enabled for vhost /

生产者

sh 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

public class TraceProduce {
  public static void main(String[] args) throws Exception {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");

    try (Connection connection = factory.newConnection();
        Channel channel = connection.createChannel(); ) {
      // 定义交换机
      channel.exchangeDeclare("trace.ex", BuiltinExchangeType.DIRECT, false, true, null);
      // 定义队列
      channel.queueDeclare("trace.qu", false, false, true, null);
      // 队列绑定
      channel.queueBind("trace.qu", "trace.ex", "");

      // 定义保留数据队列
      channel.queueDeclare("publishtrace", false, false, false, null);
      // 绑定
      channel.queueBind("publishtrace", "amq.rabbitmq.trace", "publish.trace.ex");

      for (int i = 0; i < 100; i++) {
        String msg = "这是发送的消息:" + i;
        channel.basicPublish("trace.ex", "", null, msg.getBytes(StandardCharsets.UTF_8));
      }
    } catch (IOException e) {
      e.printStackTrace();
    } catch (TimeoutException e) {
      e.printStackTrace();
    }
  }
}

检查队列的信息:

sh 复制代码
[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌──────────────┬────────────────┬─────────────────────────┬──────────┬───────────┐
│ name         │ messages_ready │ messages_unacknowledged │ messages │ consumers │
├──────────────┼────────────────┼─────────────────────────┼──────────┼───────────┤
│ publishtrace │ 100            │ 0                       │ 100      │ 0         │
├──────────────┼────────────────┼─────────────────────────┼──────────┼───────────┤
│ trace.qu     │ 100            │ 0                       │ 100      │ 0         │
└──────────────┴────────────────┴─────────────────────────┴──────────┴───────────┘
[root@nullnull-os rabbitmq]# 

这样生产者发送的消息就已经被保存至publishtrace中了,后缀便可以通过检查队列中的消息,检查消息内容。

消费者

sh 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.GetResponse;
import java.nio.charset.StandardCharsets;

public class TraceConsumer {
  public static void main(String[] args) throws Exception {

    // 资源限制
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUri("amqp://root:123456@node1:5672/%2f");

    try (Connection connection = factory.newConnection();
        Channel channel = connection.createChannel(); ) {

      // 定义交换机
      channel.exchangeDeclare("trace.ex", BuiltinExchangeType.DIRECT, false, true, null);
      // 定义队列
      channel.queueDeclare("trace.qu", false, false, true, null);
      // 队列绑定
      channel.queueBind("trace.qu", "trace.ex", "");

      // 定义队列
      channel.queueDeclare("delivertrace", false, false, true, null);
      // 队列绑定
      channel.queueBind("delivertrace", "amq.rabbitmq.trace", "deliver.trace.qu");

      // 接收消息
      for (int i = 0; i < 25; i++) {
        GetResponse getResponse = channel.basicGet("trace.qu", true);
        String msg = new String(getResponse.getBody(), StandardCharsets.UTF_8);
        System.out.println("收到的消息:" + msg);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

此处采用的是拉模式,从队列中获取了25条记录,也就是说队列中还剩余75条记录。

首先查看控制台输出:

sh 复制代码
收到的消息:这是发送的消息:0
收到的消息:这是发送的消息:1
收到的消息:这是发送的消息:2
收到的消息:这是发送的消息:3
......
收到的消息:这是发送的消息:22
收到的消息:这是发送的消息:23
收到的消息:这是发送的消息:24

检查队列的情况:

sh 复制代码
[root@nullnull-os rabbitmq]#  rabbitmqctl list_queues name,messages_ready,messages_unacknowledged,messages,consumers  --formatter pretty_table
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
┌──────────────┬────────────────┬─────────────────────────┬──────────┬───────────┐
│ name         │ messages_ready │ messages_unacknowledged │ messages │ consumers │
├──────────────┼────────────────┼─────────────────────────┼──────────┼───────────┤
│ delivertrace │ 25             │ 0                       │ 25       │ 0         │
├──────────────┼────────────────┼─────────────────────────┼──────────┼───────────┤
│ publishtrace │ 100            │ 0                       │ 100      │ 0         │
├──────────────┼────────────────┼─────────────────────────┼──────────┼───────────┤
│ trace.qu     │ 75             │ 0                       │ 75       │ 0         │
└──────────────┴────────────────┴─────────────────────────┴──────────┴───────────┘
[root@nullnull-os rabbitmq]# 

可以发现,拉的消息,都已经被推送到了delivertrace中了。

最后关闭Tracehose

sh 复制代码
[root@nullnull-os rabbitmq]# rabbitmqctl trace_off -p / 
Stopping tracing for vhost "/" ...
Trace disabled for vhost /

使用Firehose验证完成。

相关推荐
YH行业报告分析1 小时前
2026年直流配电网市场结构性增长解析:分布式能源并网与智能电网升级驱动下的技术路线博弈
分布式·能源
剑胆琴心静水深流10 小时前
全栈之路6---web集成与呈现
前端·vue.js·spring boot·分布式·spring·前端框架·npm
KAIWEILIUCC14 小时前
Ray:高性能、易扩展的Python分布式计算框架
分布式·llm
一路向北finish17 小时前
《CentOS 7 搭建 Hadoop 3.3.6 分布式集群:NFS 共享方案完整实操》
linux·运维·hadoop·分布式·centos
天涯明月199318 小时前
Ray 架构解析——以动态任务图统一 AI 计算的分布式框架
大数据·人工智能·分布式·架构·ray
小张同学a.20 小时前
MooseFS 分布式存储实战指南:部署、运维与 Pacemaker+SBD 高可用落地
linux·运维·分布式·pacemaker·moosefs·sbd
BYSJMG21 小时前
大数据毕业设计选题方向|【基于大数据的燃油消耗数据可视化与分析】Hadoop+PySpark+FP-Growth
大数据·hadoop·分布式·信息可视化·数据分析·课程设计
NJCloud1 天前
MooseFS 分布式存储部署与高可用架构实践
linux·运维·分布式·架构
zcmodeltech1 天前
钢铁冶金沙盘模型控制系统设计与实现:高炉-连铸-热轧多工序联动方案
分布式·stm32·单片机·嵌入式硬件·交互
肠畔码农2 天前
深度解密 Redis 分布式锁:从单机原子语义到集群架构博弈
redis·分布式·架构