RabbitMQ原理架构解析:消息传递的核心机制

文章目录

一、RabbitMQ简介

1.1、概述

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

1.2、特性

可伸缩性 :集群服务
消息持久化:从内存持久化消息到硬盘,再从硬盘加载到内存

解决什么问题:

  1. 进程间的通讯。程序间解耦(耦合)。
  2. web高并发。来不及进行同步处理。"too many connections"(同步 异步切换)

二、RabbitMQ原理架构


中间的Broker表示RabbitMQ服务,每个Broker里面至少有一个Virtual host虚拟主机,每个虚拟主机中有自己的Exchange交换机、Queue队列以及Exchange交换机与Queue队列之间的绑定关系Binding。producer(生产者)和consumer(消费者)通过与Broker建立Connection来保持连接,然后在Connection的基础上建立若干Channel信道,用来发送与接收消息
名词解释:

  1. exchange:它指定消息按什么规则,路由到哪个列队 给消息分类
  2. queue:消息载体,每个消息都会被投递到一个或多个列队里面。
  3. binding:exchange和queue按照规则绑定
  4. connections:producer和consumer用TCPconnections链接到rabbitMQ
  5. channels:TCP中的虚拟链接。(不损耗端口号)
  6. Routing key 路由关键字(消息路由)
  7. Send Message 发送消息
  8. Receive Message 收消息
  9. Broker 缓存代理

三、RabbitMQ应用场景

3.1、简单模式

做最简单的事情,一个生产者对应一个消费者,RabbitMQ相当于一个消息代理,负责将A的消息转发给B

应用场景:将发送的电子邮件放到消息队列,然后邮件服务在队列中获取邮件并发送给收件人、聊天等

3.2、工作模式

  1. 一条消息只会被一个消费者接收;
  2. rabbit采用轮询的方式将消息是平均发送给消费者的;
  3. 消费者在处理完某条消息后,才会收到下一条消息。
    应用场景:对于 任务过重或任务较多情况使用工作队列可以提高任务处理的速度。

3.3、发布订阅

  1. 每个消费者监听自己的队列。
  2. 生产者将消息发给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息
    应用场景:用户通知,当用户充值成功或转账完成系统通知用户,通知方式有短信等多种方法 。比如邮件群发,群聊天,广告等。

3.4、路由模式

  1. 每个消费者监听自己的队列,并且设置routingkey。
  2. 生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
    应用场景:如在商品库存中增加了1台iphone13,iphone13促销活动消费者指定routing key为iphone13,只有此促销活动会接收到消息,其它促销活动不关心也不会消费此routing key的消息

3.5 主题订阅模式

根据主题(Topics)来接收消息,将路由key和某模式进行匹配,此时队列需要绑定在一个模式上,#匹配一个词或多个词,*只匹配一个词。

应用场景:同上,iphone促销活动可以接收主题为iphone的消息,如iphone12、iphone13等

四、同类中间件对比

特性 ActiveMQ RabbitMQ RockerMQ Kafka
producer-comsumer(生产消费) 支持 支持 支持 支持
pubkish-subscribe(发布订阅) 支持 支持 支持 支持
request-reply(请求应答) 支持 支持
API完整性
多语言支持 支持, JAVA优先 无关 JAVA 支持, JAVA优先
单机吞吐量 万级 万级 万级 十万级
消息延时 毫秒 微妙 毫秒 毫秒
可用性 高(主从) 高(主从) 高(分布式) 高(分布式)
消息丢失 理论上 不会丢失 理论上 不会丢失
消息重复 可控制 理论上 会有重复
文档完整性
提供快速入门
社区活跃度
商业支持 阿里云
成熟度 成熟 成熟 比较成熟 成熟日志领域
特点 功能齐全,被大量开源项目使用 由于Erlang语言开发,性能好 各环节分布式设计,多种消费模式 可靠性、可扩展、持久性、性能高
协议 OPENWITE、STOP、REST、XMPP、AMQP AMQP 自定义(社区提供JMS) PLAINTEXT、SSL、SASL_PLAINTEXT、SASL_SSL
持久化 内存、文件、 数据库 内存、文件 磁盘文件 磁盘、文件
事务 支持 支持 支持 支持
负载均衡 支持 支持 支持 支持
管理界面 一般 有web console实现
优点 成熟的产品,已经在很多项目得到应用。有较多文档,各种协议支持较好,多重语言的成熟客户端 由于Erlang语言开发,mq性能好,管理页面丰富,多种语言支持,amqp客户端可用 模型简单,接口易用,阿里大规模应用性能好,支持多种消费,开发都较活跃 性能卓越、可用性非常高、日志领域比较成熟、功能简单、web界面友好
缺点 会莫名丢失消息,目前社区对重心版本不是太活跃,5.x维护较少,不适合大规模队列 由于Erlang语言开发,难度较大,不支持动态扩容 产品文档匮乏,没有在mq核心去实现JMS等接口,对已有系统不能完美兼容 消息失败不支持重试、消息顺序可能会导致消息乱序、社区更新较慢

五、RabbitMQ部署

5.1、单机部署

5.1.1安装erlang

yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel ncurses-devel  
wget https://github.com/erlang/otp/releases/download/OTP-24.0/otp_src_24.0.tar.gz 
tar -xzvf /otp_src_24.0.tar.gz 
cd /otp_src_24.0 
./configure --prefix=/usr/local/erlang --with-ssl --enable-threads --enable-smp-support --enable-kernel-poll --enable-hipe  
make && make install

5.1.2安装rabbitmq

wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.10.5/rabbitmq-server-3.10.5-1.el8.noarch.rpm 
rpm -ivh --nodeps rabbitmq-server-3.10.5-1.el8.noarch.rpm 
添加开机启动RabbitMQ服务 
chkconfig rabbitmq-server on 
启动RabbitMQ服务 
rabbitmq-server start 
后台启动RabbitMQ服务 
rabbitmq-server -detached 
停止RabbitMQ服务 
rabbitmqctl stop 
查看RabbitMQ服务状态 
rabbitmqctl status 
添加帐号:name 密码:
passwd rabbitmqctl add_user name passwd 
赋予其administrator角色 
rabbitmqctl set_user_tags name administrator 
删除角色 
rabbitmqctl delete_user Username 
设置权限
rabbitmqctl set_permissions -p / name ".*" ".*" ".*" 
查看用户的权限 
rabbitmqctl list_user_permissions username 

启动成功后,rabbitMQ的相关文件所在位置

  1. 相关命令 :/usr/lib/rabbitmq/bin/
  2. 相关的日志:/var/log/rabbitmq/
  3. 相关的配置 : /etc/rabbitmq/
  4. 设置的用户权限等元数据信息:/var/lib/rabbitmq/mnesia/
    http://ip:15672/尝试访问rabbitmq的web页面

可能会遇到报错情况,Node的错误,大概的意思就是说,hosts中的地址和node中的地址不一样,不匹配导致的。修改hosts文件中的本机的IP地址,hosts中的本机的计算机名称和Node的节点的名称保持一致即可。

5.2、集群部署(镜像模式)

普通模式 :普通集群模式,就是将 RabbitMQ 部署到多台服务器上,每个服务器启动一个 RabbitMQ 实例,多个实例之间进行消息通信。

此时我们创建的队列 Queue,它的元数据(主要就是 Queue 的一些配置信息)会在所有的 RabbitMQ 实例中进行同步,但是队列中的消息只会存在于一个 RabbitMQ 实例上,而不会同步到其他队列。

当我们消费消息的时候,如果连接到了另外一个实例,那么那个实例会通过元数据定位到 Queue 所在的位置,然后访问 Queue 所在的实例,拉取数据过来发送给消费者。

这种集群可以提高 RabbitMQ 的消息吞吐能力,但是无法保证高可用,因为一旦一个 RabbitMQ 实例挂了,消息就没法访问了,如果消息队列做了持久化,那么等 RabbitMQ 实例恢复后,就可以继续访问了;如果消息队列没做持久化,那么消息就丢了。

镜像模式:它和普通集群最大的区别在于 Queue 数据和原数据不再是单独存储在一台机器上,而是同时存储在多台机器上。也就是说每个 RabbitMQ 实例都有一份镜像数据(副本数据)。每次写入消息的时候都会自动把数据同步到多台实例上去,这样一旦其中一台机器发生故障,其他机器还有一份副本数据可以继续提供服务,也就实现了高可用。

5.2.1 配置节点

1、配置两台机器的hosts

vim /etc/hosts修改后使用

2、停止服务

rabbitmqctl stop

3、设置erlang cookie

这里将第一台的该文件复制到 其他节点,由于这个文件权限是400,为方便传输,先修改权限,所以需要先修改该文件权限为 777。

集群各节点的cookie必须保持一致,否则无法通信。

erlang是通过主机名来连接服务,必须保证各个主机名之间可以ping通。可以通过编辑/etc/hosts来手工添加主机名和IP对应关系。如果主机名ping不通,rabbitmq服务启动会失败。

注意.erlang.cookie的目录,也有可能在/var/lib/rabbitmq/.erlang.cookie

将权限和所属用户/组修改回来

node2:

chmod 400 /root/.erlang.cookie

4、启动个节点

rabbitmq-server --detached

查看集群状态

[root@test-1]# rabbitmqctl cluster_status 
Cluster status of node rabbit@mq1 ... 
Basics 
Cluster name: rabbit@mq1.example.local    #集群名称 
Disk Nodes    #磁盘节点 
rabbit@mq1 
Running Nodes    #运作中节点
rabbit@mq1 
Versions    #版本 
rabbit@mq1: 
RabbitMQ 3.9.0 on Erlang 24.0.4 

5.2.2创建集群

rabbitmqctl stop_app    #停止 app 服务 
rabbitmqctl reset    #清空元数据 
rabbitmqctl join_cluster rabbit@mq1 --ram    #将rabbitmq-server2添加到集群当中,并成为内存节点,不加--ram默认是磁盘节点 
rabbitmqctl start_app    #启动 app 服务 
rabbitmqctl stop_app    #停止 app 服务 
rabbitmqctl reset    #清空元数据
rabbitmqctl join_cluster rabbit@mq1 --ram    #
将rabbitmq-server2添加到集群当中,并成为内存节点,不加--ram默认是磁盘节点 
rabbitmqctl start_app    #启动 app 服务  
​
将集群设置为镜像模式(只要在其中一台节点执行以下命令即可) rabbitmqctl set_policy ha-all "#" '{"ha-mode":"all"}'
http://ip:15672/尝试访问rabbitmq的web页面

5.3、K8s部署

5.3.1 创建yaml文件

包括(comfigmap、secret、rbac、sts、svc)五个yaml文件

Comfigmap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: rabbitmq-config
  namespace: test
data:
  enabled_plugins: |
      [rabbitmq_management,rabbitmq_peer_discovery_k8s].
#启用插件rabbitmq_management和rabbitmq_peer_discovery_k8s
  rabbitmq.conf: |
      cluster_formation.peer_discovery_backend  = rabbit_peer_discovery_k8s
      cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
      cluster_formation.k8s.address_type = hostname
      #################################################
      # rabbit-mq is rabbitmq-cluster's namespace#
      #################################################
      cluster_formation.k8s.hostname_suffix = .rabbitmq-headless.rabbit-mq.svc.cluster.local
      cluster_formation.node_cleanup.interval = 30
      cluster_formation.node_cleanup.only_log_warning = true
      cluster_partition_handling = autoheal
      queue_master_locator=min-masters
      cluster_formation.randomized_startup_delay_range.max = 2
      vm_memory_high_watermark.absolute = 1GB
      disk_free_limit.absolute = 2GB
      loopback_users.guest = false

Secret.yaml:

用来存储rabbitmq的用户名、密码及erlang.cookie。

erlang创建步骤:erlang是集群之间互访的秘钥

首先需要生成一个erlang.cookie的文件: echo $(openssl rand -base64 32) > erlang.cookie

然后再生成base64的值 如:echo -n 'v/sWCz4uKETUvneRyJVn87Jg15si2eGaWg54Yvefhrk=' |base64

 apiVersion: v1
kind: Secret
metadata:
  name: devsecret
  namespace: test
type: Opaque
data:
  rabbitDefaulUser: "YWRtaW4="  # echo -n 'admin' |base64
  rabbitDefaultPass: "YWRtaW4="
  erlang.cookie: "di96VHZ4VmhOY1Uxc3dzTG4zOHdyMmk2S1IrTG82L2xqUEdTTFUwYmdwVDRBPQ=="

Rbac.yaml:

apiVersion: v1
kind: ServiceAccount  #集群访问apiserver的凭证
metadata:
  name: rabbitmq
  namespace: test
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: endpoint-reader
  namespace: test
rules:
- apiGroups: [""]
  resources: ["endpoints"]
  verbs: ["get"]
---
kind: RoleBinding  #将角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: endpoint-reader
  namespace: test
subjects:
- kind: ServiceAccount
  name: rabbitmq
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: endpoint-reader

Statefulset.yaml:

要提前创建好挂载目录

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rabbitmq
  namespace: test
spec:
  serviceName: rabbitmq-headless
  selector:
    matchLabels:
      app: rabbitmq  #在apps/v1中,需与 .spec.template.metadata.label 相同,用于hostname传播访问pod
  replicas: 3  #副本数3
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: "app"
                    operator: In
                    values:
                    - rabbitmq
              topologyKey: "kubernetes.io/hostname"
      serviceAccountName: rabbitmq
      terminationGracePeriodSeconds: 10
      containers:
      - name: rabbitmq
        image: rabbitmq:latest
        imagePullPolicy: Never
        resources:
          limits:
            cpu: 1
            memory: 500Mi
          requests:
            cpu: 1
            memory: 500Mi
        volumeMounts:
          - name: config-volume
            mountPath: /etc/rabbitmq
          - name: rabbitmq-data
            mountPath: /var/lib/rabbitmq/mnesia
        ports:
          - name: http
            protocol: TCP
            containerPort: 15672
          - name: amqp
            protocol: TCP
            containerPort: 5672
        livenessProbe:
          exec:
            command: ["rabbitmq-diagnostics", "status"]
          initialDelaySeconds: 60
          periodSeconds: 60
          timeoutSeconds: 5
        readinessProbe:
          exec:
            command: ["rabbitmq-diagnostics", "status"]
          initialDelaySeconds: 20
          periodSeconds: 60
          timeoutSeconds: 5
        env:
        - name: RABBITMQ_DEFAULT_USER
          valueFrom:
            secretKeyRef:
              key: rabbitDefaulUser
              name: devsecret                     #登陆用户名和密码都存储在一个secret对象中
        - name: RABBITMQ_DEFAULT_PASS
          valueFrom:
            secretKeyRef:
              key: rabbitDefaultPass
              name: devsecret
        - name: RABBITMQ_ERLANG_COOKIE
          valueFrom:
            secretKeyRef:
              name: devsecret
              key: erlang.cookie
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: MY_POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: RABBITMQ_USE_LONGNAME
          value: "true"
        - name: K8S_SERVICE_NAME
          value: "rabbitmq-headless"
        - name: RABBITMQ_NODENAME
          value: rabbit@$(HOSTNAME).$(K8S_SERVICE_NAME).$(MY_POD_NAMESPACE).svc.cluster.local
        - name: K8S_HOSTNAME_SUFFIX
          value: .$(K8S_SERVICE_NAME).$(MY_POD_NAMESPACE).svc.cluster.local
      volumes:
      - name: config-volume
        configMap:
          name: rabbitmq-config
          items:
          - key: rabbitmq.conf
            path: rabbitmq.conf
          - key: enabled_plugins
            path: enabled_plugins
      - name: rabbitmq-data
        hostPath:
          path: /root/rabbitmq/data
          type: Directory

Service.yaml

kind: Service
apiVersion: v1
metadata:
  name: rabbitmq-headless
  namespace: test
spec:
  clusterIP: None
  publishNotReadyAddresses: true
  ports:
   - name: amqp
     port: 5672
   - name: http
     port: 15672
  selector:
    app: rabbitmq
    
---
kind: Service
apiVersion: v1
metadata:
  namespace: test
  name: rabbitmq-service
spec:
  ports:
  - name: http
    protocol: TCP
    port: 15672
    nodePort: 30672  #管理web界面
  - name: amqp
    protocol: TCP
    port: 5672
    targetPort: 5672
    nodePort: 30671
  selector:
    app: rabbitmq
  type: NodePort

5.3.2 申请资源清单(启动各个yaml文件)

kubectl apply -f rabbitmq-configmap.yaml
kubectl apply -f rabbitmq-secret.yaml
kubectl apply -f rabbitmq-rbac.yaml
kubectl apply -f rabbitmq-sts.yaml
kubectl apply -f rabbitmq-svc.yaml


5.3.3 访问Rabbirmq页面

http://ip:30672/尝试访问rabbitmq的web页面(需要手动创建用户并授权)

六、常用命令

  1. 用户管理
    A. 查看用户列表:rabbitmqctl list_users;
    B. 添加用户:rabbitmqctl add_user ;
    C. 修改密码:rabbitmqctl change_password ;
    D. 删除用户:rabbitmqctl delete_user ;
    E. 设置用户角色:rabbitmqctl set_user_tags <tag1,tag2>,角色有
    management:用户可以访问management管理插件;administrator:所有权限;
    monitoring:用户可以访问management管理插件,查看所有连接、通道以及与节点相关的信息;policymaker:用户可以访问management管理插件,并管理他们有权访问的vhost的策略和参数;
  2. 节点与应用管理
    A. 启动rabbitmq应用程序:rabbitmqctl start_app;
    B. 关闭rabbitmq应用程序,但Erlang VM保持运行:rabbitmqctl stop_app;
    C. 关闭所有应用和节点:rabbitmqctl stop;
    D. 将Rabbitmq节点返回到原始状态,包括删除数据:rabbitmqctl reset;
  3. 集群节点
    A. 查看集群状态:rabbitmqctl cluster_status;
    B. 添加节点:rabbitmqctl join_cluster {--ram|--disk} rabbit@node;
    C. 移除节点:rabbitmqctl forget_cluster_node rabbit@node,注意先关闭应用程序rabbitmqctl stop_app;
    D. 节点健康检查:rabbitmqctl node_health_check;
  4. 插件管理
    A. 显示所有插件:rabbitmq-plugins list;
    B. 启用指定的插件:rabbitmq-plugins enable rabbitmq_management rabbitmq_mqtt rabbitmq_prometheus;
  5. 查看有效配置
    A. rabbitmqctl environment

七、RabbitMQ配置详解

tcp_listeners	监听AMQP连接的端口或主机/对。
Default: [5672]
num_tcp_acceptors	Erlang进程的数量,接受TCP监听器的连接数。
Default: 10
handshake_timeout	对AMQP 0-8/0-9/0-9-1握手的最大时间(在套接字连接和SSL握手之后),以毫秒为间隔
Default: 10000
ssl_listeners	如上所述,用于SSL连接。
Default: []
num_ssl_acceptors	用于接受SSL监听连接的Erlang进程的数量。
Default: 1
ssl_options	SSL配置参数. 详情请看  SSL documentation.
Default: []
ssl_handshake_timeout	SSL握手超时,以毫秒为间隔。
Default: 5000
vm_memory_high_watermark	触发流控制的内存阈值。详情请看  memory-based flow control.
Default: 0.4
vm_memory_high_watermark_paging_ratio	设置当内存使用超过总内存百分比多少时,队列开始将消息持久化到磁盘以释放内存。 详情请看  memory-based flow control.
Default: 0.5
disk_free_limit	RabbitMQ存储数据的分区的磁盘空间限制。当可用的磁盘空间低于这个限制时,就会触发流控制。值可以相对于RAM的总数设置(例如,内存比例,1.0)。该值也可以设置为整数的字节数。或者,单位(例如"50 mb")。默认情况下,空闲磁盘空间必须超过50MB。详情请看  Disk Alarms.
Default: 50000000
log_levels	控制日志的粒度。该值是一个日志事件类别和日志级别对的列表。
可设置级别:
  'none'
  'error'
  'warning'
  'info' 
  'debug' 
  以上下一层级别的日志输出均包含上层级别日志输出(如: warning包含warning和error), none为不输出日志
另外,当前未分类的事件总是记录在日志中
The categories are:
channel - 所有与AMQP通道有关的事件
connection - 对于所有与网络连接有关的事件
federation - 对于所有与 federation有关的事件
mirroring - 对于与镜像队列相关的所有事件
Default: [{connection, info}]

frame_max	
框架最大允许大小(以字节为单位)与消费者进行数据交换。设置为0意味着"无限",但会在一些QPid客户端触发一个bug。
设置更大的值可能会提高吞吐量;
设置较小的值可能会提高延迟。
Default: 131072

channel_max	
与消费者进行谈判的最大允许数量。设置为0意味着"无限"。
使用更多的通道会增加代理的内存占用。
Default: 0

channel_operation_timeout	通道操作超时为毫秒(内部使用,由于消息传递协议的差异和限制而不直接暴露于客户机)。
Default: 15000

heartbeat	
该值表示服务器在连接中发送的心跳延迟,在几秒钟内。优化框架。如果设置为0,则会禁用心跳。客户端可能不会遵循服务器的建议,请参阅AMQP参考以了解更多细节。
在有大量连接的情况下,禁用心跳可能改善性能,但可能会导致连接在关闭非活动连接的网络设备的出现。
Default: 60 (580 prior to release 3.5.5)

default_vhost	当RabbitMQ创建一个新的数据库时,创建一个虚拟主机。交换amq.rabbitmq.logwill存在于这个虚拟主机中。
Default: <<"/">>

default_user	当RabbitMQ从头创建一个新数据库时,要创建用户名。 
Default: <<"guest">>

default_pass	默认用户的密码。
Default: <<"guest">>

default_user_tags	默认用户的标记。
Default: [administrator]

default_permissions	在创建时分配给默认用户的权限。
Default:  [<<".*">>, <<".*">>, <<".*">>]

loopback_users	
只允许通过环回接口连接到代理的用户列表(即localhost)。 如果您希望允许缺省的来宾用户远程连接,则需要将其更改为 [].
Default:  [<<"guest">>]

cluster_nodes	当一个节点开始第一次启动时,将它设置为使集群自动发生。元组的第一个元素是节点试图集群到的节点。第二个元素是磁盘或ram,并确定节点类型。
Default: {[], disc}

server_properties	键值对的列表,在连接上向客户端宣布。
Default: []

collect_statistics	统计数据收集模式。主要与管理插件有关。选项有:
none (不要发布统计数据)
coarse (发出每个队列/每个通道/每个连接统计信息)
fine (发出的每条数据)
通常情况下不需要设置该参数
Default: none

collect_statistics_interval	统计数据收集间隔以毫秒为间隔。 主要相关插件  management plugin.
Default: 5000

management_db_cache_multiplier	管理插件将缓存诸如队列清单之类的代价较高的查询的时间。缓存将把最后一个查询的运行时间乘以这个值,并在此时间内缓存结果。
Default: 5

auth_mechanisms	 提供给客户端的SASL身份验证机制。
Default: ['PLAIN', 'AMQPLAIN']

auth_backends	
要使用的身份验证和授权后端列表。
rabbit\u auth\u backend\u Internal之外的其他数据库可以通过插件获得。Default: [rabbit_auth_backend_internal]

reverse_dns_lookups	设置为true,让RabbitMQ对客户端连接执行反向DNS查找,并通过rabbitmqctl和管理插件呈现该信息。
Default: false

delegate_count	用于集群内部通信的委托进程的数量。当为多核CPU时可以考虑设置该值
Default: 16

trace_vhosts	Used internally by the  tracer. 通常情况下不需要设置该参数
Default: []

tcp_listen_options	默认的套接字选项。通常情况下不需要设置该参数
Default:

[{backlog,       128},
 {nodelay,       true},
 {linger,        {true,0}},
 {exit_on_close, false}]
 
hipe_compile	设置为true,使用HiPE预编译RabbitMQ的部分,这是Erlang的即时编译器。这将增加服务器的吞吐量,以增加启动时间的成本。
您可能会看到,在启动时延迟几分钟,您的性能会提高20-50%。这些数据是高度工作负载和硬件依赖的。
HiPE支持可能不会编译到您的Erlang安装中。如果不是这样,启用这个选项只会导致一个警告消息被显示,而启动将照常进行。例如,Debian/Ubuntu用户需要安装erlangbase-base-hipe包。
HiPE在某些平台上是不可用的,尤其是Windows。
HiPE在17.5之前就已经知道了erlangp/otp版本的问题。HiPE推荐使用最新的erlangp/otp版本
Default: false

cluster_partition_handling	如何处理网络分区。可用模式: 
ignore
pause_minority
{pause_if_all_down, [nodes], ignore | autoheal}
(例: ['rabbit@node1', 'rabbit@node2'])
autoheal
Default: ignore

cluster_keepalive_interval	节点应该多频繁地将keepalive消息发送到其他节点(以毫秒为单位)。请注意,这与netticktime不一样; 错过的keepalive消息不会导致节点被认为挂机。
Default: 10000

queue_index_embed_msgs_below	在消息的字节数中,消息将被直接嵌入到队列索引中。详情请看  persister tuning
Default: 4096

msg_store_index_module	用于队列索引的实现模块。 详情请看  persister tuning
Default: rabbit_msg_store_ets_index

backing_queue_module	队列内容的实现模块。通常情况下不需要设置该参数
Default: rabbit_variable_queue

msg_store_file_size_limit	Tunable value for the persister.  通常情况下不需要设置该参数
Default: 16777216

mnesia_table_loading_retry_limit	在等待集群中的Mnesia tables可用时,需要重试的次数。
Default: 10

mnesia_table_loading_retry_timeout	在集群中等待每个重试的时间,以便可用
Default: 30000

queue_index_max_ journal_entries	Tunable value for the persister.  通常情况下不需要设置该参数
Default: 65536

queue_master_locator	
Queue master定位策略

可用策略:
<<"min-masters">>
<<"client-local">>
<<"random">>
详情请看  documentation on queue master location
Default: <<"client-local">>

lazy_queue_explicit_gc_run_operation_threshold	
调优: 只有在内存压力下有延迟队列时。
这是触发垃圾收集器和其他内存减少活动的阈值。一个低的值可以降低性能,一个高的值可以提高性能,但是会导致更高的内存消耗。通常情况下不需要设置该参数
Default: 1000

queue_explicit_gc_run_operation_threshold	
调优: 在内存压力较大时。
这是触发垃圾收集器和其他内存减少活动的阈值。一个低的值可以降低性能,一个高的值可以提高性能,但是会导致更高的内存消耗。通常情况下不需要设置该参数
Default: 1000

八、RabbitMQ优化

8.1、加大服务器带宽

访问量大时,较长的数据容易将带宽占满。如服务器上传带宽为10M,则实际上传带宽可认为1M,每秒上传量为1M/1K=1K。如果把带宽加到100M,则每秒上传量为10K。一般情况下,RabbitMq服务器能够接受的每秒写入量为20K-50K(8G内存),因此在带宽在不足200M的时候,加大带宽会产生很明显的提升作用,再往上效果就可能不那么明显了。

8.2、加大内存

RabbitMq的机制是先将消息放在内存中,然后分批写入硬盘。小批量数据的写入基本上都放在内存中,内存数据量过大时会一边把客户端的数据往内存里写,一边将内存里的陈旧数据往硬盘里写,这样会对速度造成较严重的损耗。适度的增加内存,队列将会更多的消息放在内存中,增加系统的处理速度。

8.3、使用固态硬盘

机械硬盘的写入速度较慢,处理大量数据时机械硬盘对性能的损耗十分严重。如果要存放1亿条数据,所需要的硬盘大小为100G,建议采用100-500G固态硬盘。再加上消费端在不断的处理数据,一般待处理的消息能够达到千万级别已经相当不容易了

8.4、增加生产者

服务器可以建立多个连接,单个生产者往往不能充分利用服务器的潜能,建立多个生产者之后,服务器处理能力将会得到充分利用。一般情况下,一个生产者每秒可以传入1000-5000条消息,在1-10这个范围内,每增加一个生产者,处理速度就会相对单生产者增加一倍

8.5、增加消费者

消费者增多时,出队速度有明显改善。该方案对于出队速度的影响有限,1-10个进程范围内,相对于单进程只有一倍左右的提升,建议线程开2-5个即可,再多可能影响不大。出现该现象的原因可能是服务器带宽或硬件处理能力有限,消费者增加了也没什么用处。因为没有实际环境测试,此条只列为建议,采用前可以多做实验

8.6、改网络访问为本地访问

该方案在消费端/生产端与RabbitMq服务器部署在同一台电脑时有用,因为省略了网络传输,大大节省了处理时间。如果消费端/生产端与RabbitMq服务器分开部署,该方案就不能使用了。在代码中将IP地址127.0.0.1改为localhost即可

8.7、消费端使用长连接

如果每次消费都建立一个连接,无疑将极大的消耗网络与消费端资源,可以只建立一个长链接,所有消息都使用这个长连接来消费。此处需要注意的是,如果长连接出问题,可能会导致队列不能继续消费,需要注意做异常控制。

8.8、消息不要超过4M

消息包大小由1K到10MB,当包大小达到4.5MB时,服务器的性能出现明显的异常,传输率尤其是每秒订阅消息的数量,出现波动,不稳定;同时有一部分订阅者的TCP连接出现断开的现象。可能是客户端底层或者RabbitMQ服务端在进行拆包,组包的时候,出现了明显的压力,而导致异常的发生

九、监控

可以通过接口 来获取性能指标

http://ip:15672/api/nodes

http://IP:15672/api/queues

http://IP:15672/api/overview

通过rabbitmq_exporter来获取RabbitMQ监控指标

获取节点信息的API:

GET /api/nodes/{node} 返回单个节点的状态

GET /api/nodes 返回所有集群成员的统计信息

指标 JSON field name
使用的内存总量memory used mem_used
内存使用阈值 mem_limit
当内存使用超过阈值时将触发报警memory alarm mem_alarm
剩余磁盘空间阈值 disk_free_limit
当空闲磁盘空间低于配置的限制时,将触发报警 disk_free_alarm
可用文件描述符总数 fd_total
当前使用的文件描述符 fd_used
尝试打开的文件描述符数量 io_file_handle_open_attempt_count
socket可用 sockets_total
已经使用的socket数量 sockets_used
Message store disk reads message_stats.disk_reads
Message store disk writes message_stats.disk_writes
Inter-node communication links cluster_links
GC runs gc_num
gc回收的字节 gc_bytes_reclaimed
erlang进程限制 proc_total
已经使用erlang进程 proc_used
正在运行的队列 run_queue

可以从任一节点获取集群监控数据

API:GET /api/overview

指标 JSON field name
集群名称 cluster_name
集群范围的消息速率 message_stats
连接总数 object_totals.connections
channel总数 object_totals.channels
队列总数 object_totals.queues
消费者总数 object_totals.consumers
消息总数(ready+unacked) queue_totals.messages
准备交付的消息数量 queue_totals.messages_ready
未确认的消息数量 queue_totals.messages_unacknowledged
最近发布的消息数量 message_stats.publish
消息发布的速率 message_stats.publish_details.rate
最近发送给消费者的消息数量 message_stats.deliver_get
消息交付速率 message_stats.deliver_get.rate

单个队列监控API地址: GET /api/queues/{vhost}/{qname}

指标 JSON field name
内存 memory
消息总数(ready+unacknowledged) messages
准备交付的消息数量 messages_ready
未确认的消息数量 messages_unacknowledged
最近发布的消息数量 message_stats.publish
消息发布速度 message_stats.publish_details.rate
最近交付的消息数量 message_stats.deliver_get
消息交付速度 message_stats.deliver_get.rate
其他消息状态 this document message_stats

整理监控指标情况

监控项 指标名称 阈值
rabbitmq节点状态 rabbitmq-status != 1 紧急
rabbitmq节点内存使用率 rabbitmq-node_mem_used > 0.8中度 >0.9严重
rabbitmq文件句柄数使用率 rabbitmq_fd_used > 0.8中度 >0.9严重
rabbitmq堆积消息监控 rabbitmq_queue_messages_unack > 500中度 > 1000严重
rabbitmq推送错误监控 rabbitmq_failed_to_publish_total >5中度
rabbitmq可消费消息数监控 rabbitmq_queue_messages_ready > 5000中度
rabbitmq推送消息监控 rabbitmq_queue_messages_published_total ==0中度
rabbitmq消息投递监控 rabbitmq_queue_messages_delivered_total ==0中度
rabbitmq消息连接恢复 rabbitmq_connection_recovery_total >10中度
rabbitmq消息连接数 rabbitmq_connections ==0严重
rabbitmq通道 rabbitmq_channel ==0严重
rabbitmq消费者 rabbitmq_consumers ==0严重
rabbitmq的sockets使用率 rabbitmq_sockets_used > 0.8中度 >0.9严重

十、常见故障与排查

RabbitMQ注意小计(故障恢复提示):

  1. 保证集群中至少有一个磁盘类型的节点以防数据丢失,在更改节点类型时尤其要注意。
  2. 若整个集群被停掉了,应保证最后一个down掉的节点被最先启动,若不能则要使用forget_cluster_node命令将其移出集群。
  3. 若集群中节点几乎同时以不可控的方式down 了,此时再其中一个节点使用force_boot 命令重启节点。
  4. 如果加入集群后,意外关闭等造成rabbitmq-server启动不成功,可以尝试一下步骤:/var/lib/rabbitmq/mnesia 目录下存在rabbit@localhost.pid、rabbit@localhost、rabbit@localhost-plugins-expand,删除这3项后,并且删除 /var/lib/rabbitmq/ 目录下 .erlang.cookie和erl_crash.dump 再使用systemctl start rabbitmq-server启动

10.1、消费慢

问题分析:

可以看到RabbitMQ的内存 占用占用已经使用了7.8G 允许的值为 .6G左右

因为 vm_memory_high_watermark 值设置的是0.4 也就是物理内存的40% ;服务器为16G * 40% = 6.4G

一般在产生的原因是长期的生产者发送速率大于消费者消费速率导致. 触发了RabbitMQ 的流控;

解决方案:

  1. 增加消费者端的消费能力,或者增加消费者(根本解决)
  2. 控制消息产生者端的发送速率(不太现实)
  3. 增加mq的内存(治标不治本)

10.2、消息丢失

这个是RabbitMQ最常见的问题,RabbitMQ丢失分三种情况,生产者消息丢失,RabbitMQ消息丢失,消费者消息丢失.

  1. 生产者消息丢失
    生产者在发送消息给RabbitMQ,在中途有可能因为网络问题导致消息丢失,我们可以选择RabbitMQ提供的事务,也可以用confirm模式,就是生产者确认
    事务:
    channel.tsSelect开启事务;
    channel.txRollback出现问题事务回滚
    channel.txCommit成功后提交事务
    confirm模式:
    生产者发送消息,每个消息分配一个id,如果RabbitMQ接收到消息返回ack,如果没有收到放回nack,这个时候生产者只需要重新发送这个消息即可,可以结合这个机制在内存里设置消息id的状态,多长时间没有f返回ack就是失败,重新发送消息.
    事务和confirm对比:
    事务是属于同步的,在你提交一个事务会阻塞在那里,其他的消息是不能继续发送的,这样很容易影响性能,而confirm是异步的就是发送完我可以继续发送另一条消息,在正常使用中推荐使用confirm模式
  2. RabbitMQ消息丢失
    RabbitMQ数据丢失,可以用持久化分为两个步骤:
    首选创建queue的时候将其设置为持久化,这样可以保证RabbitMQ持久化queue的元数据,而不会持久化queue的数据,第二步将消息的deliveryMode设置为2,就是将消息设置为持久化,此时消息就会持久化到磁盘上,即使RabbitMQ重启,也会从磁盘上恢复queue,恢复queue里的数据
    跟生产者的confirm配合,如过持久化到磁盘上再返回ack,如果还没持久化就RabbitMQ挂掉了,生产者没有收到ack,还是会重新发送消息.
  3. 消费者消息丢失
    消费者有可能因为刚接到消息还没有处理就重启了,但是RabbitMQ以为他处理了,这样就会导致消息丢失,合理的解决方案是关闭自动ack,可以通过api来进行调用,等自己的代码处理完成之后,可以ack,这样即使你没有消费RabbitMQ也知道你是没有去消费的,这个时候RabbitMQ可以把消息交给其他的consumer进行消费,避免消息丢失

10.3、消息重复消费

出现原因:消费者消息消息的时候,MQ没有收到消息的ack应答。

场景:

  1. 消费者消费消息后没有ack。
  2. 消费者在消费消息后,ack时网络异常。

解决步骤:

  • 消费者消费后,记录通过缓存记录消息的消费标识,消息id如redis的setnx
  • 如果消费成功且ack成功,则删除记录的消息标记。
  • 如果ack失败,消息下次被消费消息时候,先去查询消息的消费标识,已经消费则直接ack,未消费则继续消费。

10.4、报错"error in config file "/etc/rabbitmq/rabbitmq.config" (none): no ending found"

rabbitmq config的配置相关的官方文档: http://www.rabbitmq.com/configure.html 官方给出的一个 示例配置: https://github.com/rabbitmq/rabbitmq-server/blob/v3.8.x/deps/rabbit/docs/rabbitmq.conf.example 拷贝以上实例文档 到 对应的rabbitmq的安装目录下的文件:/etc/rabbitmq , 取名配置文件名称为 rabbitmq.config,重启rabbit,那么当前文件 就为 当前rabbit所使用。

10.5、生产者发送消息报错

channel is already closed due to channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - home node 'rabbit@xxx' of durable queue 'xxx_queue' in vhost '/' is down or inaccessible, class-id=50, method-id=10)

生产者连接不上vhost'/',看报错像是服务端有问题,经排查后发现,发现服务集群有一个节点满了,导致连接到这个节点的Connection都出问题了。

解决方案:添加节点,重启生产者服务。

10.6、浏览器打开IP地址,无法访问 RabbitMQ(白屏没有结果)

服务器对应的安全组15672端口没有开启(入规则),导致浏览器无法访问到服务器的任何内容。

相关推荐
一條狗5 小时前
20250219 隨筆 [特殊字符] 查看短鏈的實現方式與解決方案優化
rabbitmq·冗餘雙寫
车载诊断技术5 小时前
电子电气架构 --- 电子电器新技术及发展趋势
网络·架构·汽车·电子电器框架·车载充电器(obc)·电子电器新技术及发展趋势
呱牛do it5 小时前
【系列专栏】银行IT的云原生架构-混合云弹性架构 13
微服务·云原生·金融·架构
uhakadotcom7 小时前
约束求解领域的最新研究进展
人工智能·面试·架构
小猫猫猫◍˃ᵕ˂◍7 小时前
rabbitmq五种模式的实现——springboot
spring boot·rabbitmq·java-rabbitmq
文军的烹饪实验室10 小时前
我国有哪些芯片使用的是arm架构处理器
arm开发·架构
微学AI12 小时前
Deepseekv3原理架构中的数学公式,通过高度概括实现快速入门
开发语言·人工智能·python·架构·deepseek
martian66513 小时前
【Java高级篇】——第13篇:深入探讨设计模式与Java实践
java·开发语言·架构
power-辰南14 小时前
AI Agent架构深度解析:从ReAct到AutoGPT,自主智能体的技术演进与工程实践
人工智能·react.js·架构·ai agent
叫我龙翔16 小时前
【项目日记】仿RabbitMQ实现消息队列 --- 模块设计
运维·服务器·网络·c++·分布式·http·rabbitmq