<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Miafetta&apos;s Cafe</title><description>记录技术和一些有趣的东西</description><link>https://miafetta.github.io/</link><language>zh_CN</language><item><title>RabbitMQ：配置与使用</title><link>https://miafetta.github.io/posts/rabbitmq-%E7%9A%84%E9%85%8D%E7%BD%AE%E4%B8%8E%E4%BD%BF%E7%94%A8/</link><guid isPermaLink="true">https://miafetta.github.io/posts/rabbitmq-%E7%9A%84%E9%85%8D%E7%BD%AE%E4%B8%8E%E4%BD%BF%E7%94%A8/</guid><description>了解 RabbitMQ 的架构设计、部署和使用方法</description><pubDate>Wed, 04 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;RabbitMQ 简介&lt;/h2&gt;
&lt;p&gt;RabbitMQ 是一个由 Erlang 语言开发的 AMQP（一种消息队列协议）的开源实现。&lt;/p&gt;
&lt;p&gt;RabbitMQ 主要用于两个及以上应用程序之间的相互通信。简单来说，&lt;strong&gt;RabbitMQ 可以连接在任意位置的任意程序&lt;/strong&gt;。应用程序可以使用不同的编程语言实现，可以运行在不同设备上，可以运行在非局域网环境下……只要连接到同一个 RabbitMQ 服务器（至少一个运行了 RabbitMQ 程序的设备），就可以按照用户预设的方式传递消息。&lt;/p&gt;
&lt;h3&gt;为什么要使用消息队列&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;解耦合：程序只需将消息发送到队列，无需关心哪个程序处理或何时处理。这种设计使得系统组件可以独立开发部署且易于扩展。&lt;/li&gt;
&lt;li&gt;异步处理：程序发送消息后无需等待消息被处理，可以继续执行其他任务，提高了系统的响应速度和吞吐量。&lt;/li&gt;
&lt;li&gt;流量削峰：消息队列可以作为缓冲区，暂存突发的大量请求，避免后端服务因瞬时压力过大而崩溃。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;消息队列的基本应用场景&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;单 $\to$ 单&lt;/strong&gt;：即从一个应用程序发送到另一个应用程序，如订单处理程序和库存处理程序。用户下单后，订单处理程序生成一个“扣减库存”消息放入队列，库存服务从队列中取出消息并进行处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单 $\to$ 多&lt;/strong&gt;：即从一个应用程序发送到多个应用程序，如新闻推送程序（将同一个新闻发送到多个客户端中）或订单分发系统（将订单分发到不同的客户端中，且同一个订单不会被多个客户端接收）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多 $\to$ 单&lt;/strong&gt;：即从多个应用程序发送到一个应用程序，如多个应用程序将产生的日志发送到统一的日志处理程序中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多 $\to$ 多&lt;/strong&gt;：即从多个应用程序发送到其他多个应用程序，如物联网中枢。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;运用消息队列时，一个发送消息的程序只需要将消息发送到某个消息队列，而无需了解其他发送消息的程序或者接收消息的程序；而多个程序发送消息时，对于消息队列本身来说，相当于同一个程序先后发送不同的消息。于是 &lt;strong&gt;多个应用程序发送消息的情况可以视作单个应用程序发送多条消息&lt;/strong&gt;，即 &lt;strong&gt;只需要考虑单 $\to$ 单和单 $\to$ 多两种情况&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而在单 $\to$ 多的情况下，也有两种模式：第一种是每个接收消息的程序获得相同的消息，第二种是每个接收消息的程序获得不同的消息。前者就如之前提到的新闻推送程序，将同一个新闻发送到多个客户端中；后者就如订单分发系统，将订单分发到不同的客户端中，且需要保证同一个订单不会被多个客户端接收。&lt;/p&gt;
&lt;p&gt;总而言之，运用消息队列时，有三种典型的应用场景：&lt;strong&gt;单 $\to$ 单、单 $\to$ 多（接收的消息相同）和单 $\to$ 多（接收的消息不同）&lt;/strong&gt;。针对这三种应用场景，RabbitMQ 有着与之对应的三种模式：&lt;strong&gt;简单模式、工作模式与交换机模式&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;RabbitMQ 的抽象结构&lt;/h2&gt;
&lt;p&gt;RabbitMQ 的抽象结构如下图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E6%8A%BD%E8%B1%A1%E7%BB%93%E6%9E%84.png&quot; alt=&quot;抽象结构&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;消息（Message）&lt;/strong&gt;：由消息头（properties）和消息体（body）组成。
&lt;ul&gt;
&lt;li&gt;消息头包含了消息的一系列可选属性，包括路由键 &lt;code&gt;routing key&lt;/code&gt;、可持久化标签 &lt;code&gt;delivery mode&lt;/code&gt;、优先级 &lt;code&gt;priority&lt;/code&gt; 等。&lt;/li&gt;
&lt;li&gt;消息体可以是任何被表示为二进制序列的数据，对于 RabbitMQ 是不透明的。RabbitMQ 不会检查和审核消息体的内容。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产者（Producer）&lt;/strong&gt;：一个发送消息的应用程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消费者（Consumer）&lt;/strong&gt;：一个从消息队列中接收消息的应用程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接（Connection）&lt;/strong&gt;：网络连接，比如一个 TCP 连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;信道（Channel）&lt;/strong&gt;：Connection 中的一条双向数据流通道。Channel 是建立在真实的 TCP 连接内的虚拟连接，不管是发布消息、订阅队列还是接收消息，都需要通过 Channel 完成。多线程程序会经常建立和销毁 TCP 连接，产生极大的开销，于是引入了 Channel 以复用一条 TCP 连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;物理主机（Broker）&lt;/strong&gt;：一个 RabbitMQ 服务器实体，即一个运行了 RabbitMQ 程序的设备。使用 RabbitMQ 时，Producer 和 Consumer 无需安装 RabbitMQ，RabbitMQ 程序只在 Broker 上真正运行着。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;交换机（Exchange）&lt;/strong&gt;：一个从 Producer 接收消息，并将消息发送到指定队列（由交换机自身类型和 Binding 决定）的对象。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;队列（ Queue ）&lt;/strong&gt;：由 Producer （非交换机模式下）或 Consumer （交换机模式下）建立的消息存储容器。
&lt;ul&gt;
&lt;li&gt;Queue 容器的大小默认是无限的，只受到系统资源（内存、磁盘大小）的限制。&lt;/li&gt;
&lt;li&gt;当一个 Consumer 接收 Queue 中的一条消息并确认后，该消息才会被销毁。&lt;/li&gt;
&lt;li&gt;一个 Queue 中的一条消息只会被发送给一个 Consumer。如果需要将同一条消息发送给多个消费者，则需要 Exchange 将消息发送到每个消费者连接的 Queue 中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;绑定（Binding）&lt;/strong&gt;：一个决定 Queue 接收哪些消息的键值。
&lt;ul&gt;
&lt;li&gt;一个 Queue 与默认交换机绑定时的 Binding 是其名称。&lt;/li&gt;
&lt;li&gt;只有当消息的 routing key 与 Queue 的 Binding 相同时，该消息才能进入该 Queue 中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;虚拟主机（ Virtual Host / VHost）&lt;/strong&gt;：表示一组 Exchange、Queue 和相应的 Binding。
&lt;ul&gt;
&lt;li&gt;每一个 VHost 等价于一个小型的 RabbitMQ 服务器。&lt;/li&gt;
&lt;li&gt;每一个 RabbitMQ Broker 都有一个默认的 VHost “/”。&lt;/li&gt;
&lt;li&gt;在 RabbitMQ 中，用户只能以 VHost 为单位进行权限控制（例如，如果需要禁止 A 组访问 B 组的 Exchange、Queue 或者 Binding，必须为 A 和 B 分别创建一个虚拟主机）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;配置 RabbitMQ 服务器&lt;/h2&gt;
&lt;p&gt;RabbitMQ Broker 是唯一需要安装 RabbitMQ 服务的设备。无论是生产者还是消费者均无需安装 RabbitMQ。&lt;/p&gt;
&lt;h3&gt;安装并启动 RabbitMQ&lt;/h3&gt;
&lt;p&gt;RabbitMQ 是使用 Erlang 语言开发的，在安装前必须先安装和配置 Erlang 环境。具体可以查阅：&lt;a href=&quot;https://www.rabbitmq.com/docs/download&quot;&gt;Installing RabbitMQ | RabbitMQ&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;由于 Windows 系统下 Erlang 的安装与配置较为繁琐，在此以 Ubuntu 24.04 为例。可以将 RabbitMQ 安装在 Docker 中，也可以直接安装在主机环境中。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;/p&gt;
&lt;p&gt;此部分更新于 2026/03/04，RabbitMQ 的最新版本为 4.2.4。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;方法一：在 Docker 中安装并启动 RabbbitMQ&lt;/h4&gt;
&lt;h5&gt;1 安装并配置 Docker&lt;/h5&gt;
&lt;p&gt;若主机中尚未安装 Docker，需要先安装 Docker。同时，为了解决传统模式下每次启动容器时，运行 &lt;code&gt;docker run&lt;/code&gt; 需要记住大量参数和命令的问题，使用 Docker Compose 简化 Docker 容器的管理和部署。&lt;/p&gt;
&lt;h6&gt;1.1 安装  Docker 及 Docker Compose&lt;/h6&gt;
&lt;p&gt;以下操作需要以 root 用户身份完成，请使用 &lt;code&gt;sudo -i&lt;/code&gt; 或 &lt;code&gt;su root&lt;/code&gt; 切换到 root 用户。&lt;/p&gt;
&lt;p&gt;首先，运行以下命令，安装一些必要的软件包：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y
sudo apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后加入 Docker 的 GPG 公钥和 apt 源：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -sSL https://download.docker.com/linux/debian/gpg | gpg --dearmor &amp;gt; /usr/share/keyrings/docker-ce.gpg
echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable&quot; &amp;gt; /etc/apt/sources.list.d/docker.list
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;/p&gt;
&lt;p&gt;如遇到网络问题，可以用 &lt;a href=&quot;https://mirrors.tuna.tsinghua.edu.cn/&quot;&gt;清华大学开源软件镜像站 | Tsinghua Open Source Mirror&lt;/a&gt; 的镜像源：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -sSL https://download.docker.com/linux/debian/gpg | gpg --dearmor &amp;gt; /usr/share/keyrings/docker-ce.gpg
echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/debian $(lsb_release -sc) stable&quot; &amp;gt; /etc/apt/sources.list.d/docker.list
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;之后可以直接安装 Docker 和 Docker Compose 了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update &amp;amp;&amp;amp; apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Docker Compose 可以作为 Docker 插件 &lt;code&gt;docker-compose-plugin&lt;/code&gt; 安装，也能以 &lt;code&gt;docker-compose&lt;/code&gt; 软件包单独安装，二者区别如下：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;安装方式&lt;/th&gt;
&lt;th&gt;兼容性&lt;/th&gt;
&lt;th&gt;启动命令&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose-plugin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;较好&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker compose&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;最好&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这里以 Docker 插件 &lt;code&gt;docker-compose-plugin&lt;/code&gt; 的形式安装。如遇到兼容性问题，则可以单独安装 Docker Compose。&lt;/p&gt;
&lt;h6&gt;1.2 验证安装&lt;/h6&gt;
&lt;p&gt;可以通过以下命令验证 Docker 和 Docker Compose 是否成功安装并已是最新版本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo docker version
sudo docker compose version
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;1.3 （可选）使用国内镜像加速 Docker&lt;/h6&gt;
&lt;p&gt;可以运行如下命令，添加国内镜像，并重启 Docker 服务。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json &amp;lt;&amp;lt;-&apos;EOF&apos;
{
    &quot;registry-mirrors&quot;: [
        &quot;https://docker.1ms.run&quot;,
        &quot;https://dockercf.jsdelivr.fyi/&quot;,
        &quot;https://docker.m.daocloud.io&quot;
    ]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;1.4 将当前用户加入 docker 用户组&lt;/h6&gt;
&lt;p&gt;在使用 docker 命令前，每次都需要添加 &lt;code&gt;sudo&lt;/code&gt; 前缀。通过将当前用户加入 docker 用户组，可以让当前用户直接运行 docker 命令。&lt;/p&gt;
&lt;p&gt;首先创建 docker 用户组。通常在安装 Docker 时，docker 用户组会自动创建，但也可以运行以下命令来创建或确认：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo groupadd docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果已存在 docker 用户组则会提示：&lt;code&gt;groupadd：“docker”组已存在&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;然后将当前用户添加到 docker 用户组中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo usermod -aG docker $USER

# 也可以使用下面的命令
# sudo usermod -aG docker $(whoami)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注销并重新登录当前用户，或使用 &lt;code&gt;newgrp docker&lt;/code&gt; 刷新 docker 用户组权限后生效。&lt;/p&gt;
&lt;h5&gt;2 在 Docker 中部署并启动 RabbitMQ&lt;/h5&gt;
&lt;p&gt;由于容器默认是非持久化的，需要先准备好持久化时所需的目录、文件及其权限，然后将 RabbitMQ 容器挂载在对应目录下。&lt;/p&gt;
&lt;p&gt;下面以安装在用户目录 &lt;code&gt;~/Documents/docker/rabbitmq/&lt;/code&gt; 下为例。运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p ~/Documents/docker/rabbitmq/{data,config,logs}
cd ~/Document/docker/rabbitmq
touch ./config/rabbitmq.conf
sudo chown -R 999:999 ./data ./logs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后创建 Docker Compose 配置文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tee ~/Documents/docker/rabbitmq/docker-compose.yml &amp;lt;&amp;lt; &apos;EOF&apos;
services:
  rabbitmq:
     # 镜像名
    image: rabbitmq:4-management
    # 容器名
    container_name: rabbitmq
    # 主机名
    hostname: rabbitmq-server
    # 重启策略
    restart: unless-stopped
    # 端口映射
    ports:
      - &quot;5672:5672&quot;      # AMQP 协议端口
      - &quot;15672:15672&quot;    # 管理界面 Web 端口
    # 环境变量
    environment:
      # 默认用户配置
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS=admin
      # Erlang cookie
      - RABBITMQ_ERLANG_COOKIE=secret_cookie_debian
      # 日志位置
      - RABBITMQ_LOGS=/var/log/rabbitmq/rabbitmq.log
      - RABBITMQ_SASL_LOGS=/var/log/rabbitmq/rabbitmq-sasl.log
    # 卷挂载
    volumes:
      # 数据持久化
      - ./data:/var/lib/rabbitmq
      # 日志持久化
      - ./logs:/var/log/rabbitmq
      # 自定义配置文件
      - ./config/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf
    # 容器间通信网络
    networks:
      - rabbitmq-network

networks:
  rabbitmq-network:
    name: rabbitmq-network
    driver: bridge
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后运行如下命令，部署并启动 RabbitMQ：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，RabbitMQ 服务器会在系统启动时自动启动。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]&lt;/p&gt;
&lt;p&gt;RabbitMQ 服务器默认的用户名为 &lt;code&gt;guest&lt;/code&gt; ，密码也为 &lt;code&gt;guest&lt;/code&gt;。但默认的 &lt;code&gt;guest&lt;/code&gt; 用户只允许本地连接，如果需要远程连接和管理 Broker，需要创建一个管理员用户。&lt;/p&gt;
&lt;p&gt;在先前的 &lt;code&gt;docker-compose.yml&lt;/code&gt; 中，已经定义好默认的管理员用户名为 &lt;code&gt;admin&lt;/code&gt;，密码也为 &lt;code&gt;admin&lt;/code&gt;，可以直接使用，但请注意，如果要将 RabbitMQ 服务器暴露在公网中，请务必修改用户名和密码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;3 通过命令行与 RabbitMQ 服务器交互&lt;/h5&gt;
&lt;p&gt;如果想通过命令行与 RabbitMQ 服务器交互，请使用如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec -it rabbitmq bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，就可以像在主机环境中一样，使用命令管理 RabbitMQ 服务器了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;/p&gt;
&lt;p&gt;由于容器中默认以 root 用户登录，因此若以此方式安装 RabbitMQ，在管理服务器时，命令前无需添加 &lt;code&gt;sudo&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;方法二：在主机环境中安装并启动 RabbbitMQ&lt;/h4&gt;
&lt;h5&gt;1 安装 RabbitMQ&lt;/h5&gt;
&lt;p&gt;若要安装在主机环境中，可以使用官网 &lt;a href=&quot;https://www.rabbitmq.com/docs/install-debian#apt-quick-start&quot;&gt;Installing on Debian and Ubuntu | RabbitMQ&lt;/a&gt; 中提供的自动脚本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/sh

sudo apt-get install curl gnupg apt-transport-https -y

## Team RabbitMQ&apos;s signing key
curl -1sLf &quot;https://keys.openpgp.org/vks/v1/by-fingerprint/0A9AF2115F4687BD29803A206B73A36E6026DFCA&quot; | sudo gpg --dearmor | sudo tee /usr/share/keyrings/com.rabbitmq.team.gpg &amp;gt; /dev/null

## Add apt repositories maintained by Team RabbitMQ
sudo tee /etc/apt/sources.list.d/rabbitmq.list &amp;lt;&amp;lt;EOF
## Modern Erlang/OTP releases
##
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-erlang/ubuntu/noble noble main

## Latest RabbitMQ releases
##
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb1.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main
deb [arch=amd64 signed-by=/usr/share/keyrings/com.rabbitmq.team.gpg] https://deb2.rabbitmq.com/rabbitmq-server/ubuntu/noble noble main
EOF

## Update package indices
sudo apt-get update -y

## Install Erlang packages
sudo apt-get install -y erlang-base \
                        erlang-asn1 erlang-crypto erlang-eldap erlang-ftp erlang-inets \
                        erlang-mnesia erlang-os-mon erlang-parsetools erlang-public-key \
                        erlang-runtime-tools erlang-snmp erlang-ssl \
                        erlang-syntax-tools erlang-tftp erlang-tools erlang-xmerl

## Install rabbitmq-server and its dependencies
sudo apt-get install rabbitmq-server -y --fix-missing
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;2 启动 RabbitMQ 服务器&lt;/h5&gt;
&lt;p&gt;若选择安装在主机环境内，RabbitMQ 将被安装在 &lt;code&gt;/usr/sbin/&lt;/code&gt; 目录下。&lt;/p&gt;
&lt;p&gt;由于该目录中包含系统管理命令，因此普通用户的 &lt;code&gt;PATH&lt;/code&gt; 环境变量中默认不会包含该目录。要启动 RabbitMQ，或使用 RabbitMQ 安装目录的完整路径，或将 &lt;code&gt;/usr/sbin/&lt;/code&gt; 目录加入当前用户的 &lt;code&gt;PATH&lt;/code&gt; 中，或临时切换为 root 用户。&lt;/p&gt;
&lt;h6&gt;方法一：使用完整路径&lt;/h6&gt;
&lt;p&gt;每次启动 RabbitMQ 服务器时，加上完整路径：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo /usr/sbin/rabbitmq-server
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;方法二：临时切换为 root 用户&lt;/h6&gt;
&lt;p&gt;首先运行 &lt;code&gt;sudo -i&lt;/code&gt; 或 &lt;code&gt;su root&lt;/code&gt; 命令，临时切换为 root 用户，然后使用如下的命令启动 RabbitMQ 服务器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rabbitmq-server
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;方法三：将安装目录加入环境变量（推荐）&lt;/h6&gt;
&lt;p&gt;运行以下命令，将 &lt;code&gt;/usr/sbin/&lt;/code&gt; 目录加入当前用户的 &lt;code&gt;PATH&lt;/code&gt; 中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=$PATH:/usr/sbin
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，可以使用如下的命令启动 RabbitMQ 服务器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rabbitmq-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以下均假设已按照方法三，将 &lt;code&gt;/usr/sbin/&lt;/code&gt; 目录加入当前用户的 &lt;code&gt;PATH&lt;/code&gt; 中。&lt;/p&gt;
&lt;h5&gt;3 创建 RabbitMQ 服务器用户&lt;/h5&gt;
&lt;p&gt;在开始使用前，需要先创建一个用于访问 RabbitMQ 服务器的用户。&lt;/p&gt;
&lt;p&gt;RabbitMQ 服务器默认的用户名为 &lt;code&gt;guest&lt;/code&gt; ，密码也为 &lt;code&gt;guest&lt;/code&gt;，但默认的 &lt;code&gt;guest&lt;/code&gt; 用户只允许本地连接，如果需要远程连接和管理 Broker，需要创建一个管理员用户。&lt;/p&gt;
&lt;p&gt;可以使用如下命令创建用户。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 如果 RabbitMQ 还未启动，运行以下命令以启动服务
sudo systemctl start rabbitmq

user_name=&quot;admin&quot;		# 新用户名
user_password=&quot;admin&quot;	# 新用户密码
sudo rabbitmqctl add_user &quot;$user_name&quot; &quot;$user_password&quot;		# 添加用户
sudo rabbitmqctl set_user_tags &quot;$user_name&quot; administrator	# 设置用户为管理员

# （可选）为该用户设置访问默认虚拟主机“/”的权限
sudo rabbitmqctl set_permissions -p / &quot;$user_name&quot; &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;
# （可选，在生产环境下推荐）删除默认的 guest 用户
sudo rabbitmqctl delete_user guest
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置 RabbitMQ 服务器&lt;/h3&gt;
&lt;p&gt;如果安装在主机环境中，RabbitMQ 的配置文件路径位于 &lt;code&gt;/etc/rabbitmq/rabbitmq.conf&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果安装在 Docker 中，RabbitMQ 的配置文件位于安装目录中的 &lt;code&gt;config/rabbitmq.conf&lt;/code&gt;，请在非容器环境（主机环境）下进行修改。&lt;/p&gt;
&lt;p&gt;关于 RabbitMQ 配置文件的参数，可以参考 &lt;a href=&quot;https://www.rabbitmq.com/docs/configure#config-items&quot;&gt;Configuration | RabbitMQ&lt;/a&gt;。以下是一些常见的参数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listeners.tcp.default = 5672	# 监听端口，默认为 5672
disk_free_limit.absolute = 50MB	# 磁盘空闲空间限制绝对大小，低于该值时 RabbitMQ 将阻止消息的写入，默认为 50MB
disk_free_limit.relative = 1.5	# 磁盘空闲空间限制相对于内存的大小
vm_memory_high_watermark.absolute = 2GB		# Broker 能占用的最大内存绝对大小
vm_memory_high_watermark.relative = 0.6		# Broker 能占用的最大内存相对大小，默认为 0.6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之前我们提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Queue 容器的大小默认是无限的，只受到系统资源（内存、磁盘大小）的限制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Queue 的大小其实就是受到 &lt;code&gt;disk_free_limit&lt;/code&gt;、&lt;code&gt;vm_memory_high_watermark&lt;/code&gt; 等参数的限制。&lt;/p&gt;
&lt;h3&gt;（可选）创建虚拟主机&lt;/h3&gt;
&lt;p&gt;RabbitMQ 的默认虚拟主机为 “/”。也可以使用以下命令自行创建。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vhost=&quot;/vhost&quot;		# 新 vhost 名
user=&quot;admin&quot;		# 可以访问该 vhost 的用户名
sudo rabbitmqctl add_vhost &quot;$vhost&quot;		# 新建 vhost
sudo rabbitmqctl set_permissions -p &quot;$vhost&quot; &quot;$user&quot; &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;	# 为用户设置可访问该 vhost 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;管理 RabbitMQ 服务器&lt;/h3&gt;
&lt;p&gt;常用的管理命令如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rabbitmqctl status				# 查看服务器状态
sudo rabbitmqctl list_exchanges		# 列出当前所有 exchange
sudo rabbitmqctl list_queues		# 列出当前所有 queue
sudo rabbitmqctl list_users			# 列出当前所有用户

sudo rabbitmqctl stop				# 关闭 RabbitMQ 服务器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以安装插件以通过网页 UI 管理 RabbitMQ 服务器。使用以下命令启用插件。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rabbitmq-plugins enable rabbitmq_management
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后重启 RabbitMQ 服务器。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rabbitmqctl stop
sudo rabbitmq-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过访问 &lt;code&gt;https://&amp;lt;server-ip&amp;gt;:15672&lt;/code&gt;（将 &lt;code&gt;&amp;lt;server-ip&amp;gt;&lt;/code&gt; 替换为服务器公网 IP 或 &lt;code&gt;localhost&lt;/code&gt;）并使用具有远程访问权限的管理员账户登录，即可进入 RabbitMQ 管理界面，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/RabbitMQ%E7%AE%A1%E7%90%86%E6%8F%92%E4%BB%B6-%E7%99%BB%E5%BD%95%E7%95%8C%E9%9D%A2.png&quot; alt=&quot;RabbitMQ 管理插件-登录界面&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/RabbitMQ%E7%AE%A1%E7%90%86%E6%8F%92%E4%BB%B6-%E7%AE%A1%E7%90%86%E7%95%8C%E9%9D%A2.png&quot; alt=&quot;RabbitMQ 管理插件-管理界面&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自此，RabbitMQ 服务器的配置已全部完成。&lt;/p&gt;
&lt;h2&gt;RabbitMQ 的使用&lt;/h2&gt;
&lt;p&gt;RabbitMQ 服务器可以通过任何支持 AMQP 协议的语言连接，只要引入对应的库即可。对于 Python 而言，需要引入 &lt;code&gt;pika&lt;/code&gt; 库；对于 Java 而言，需要使用 &lt;code&gt;amqp-client&lt;/code&gt; 库；对于 Node.js 而言，是 &lt;code&gt;amqplib&lt;/code&gt; 库；对于 Go 而言，是 &lt;code&gt;streadway/amqp&lt;/code&gt; 库……&lt;/p&gt;
&lt;p&gt;此处以 Python 为例。在生产者和消费者程序的开头，需要先导入 &lt;code&gt;pika&lt;/code&gt; 库。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pika
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先前，我们已经提到过使用 RabbitMQ 的三种模式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;运用消息队列时，有三种典型的应用场景：&lt;strong&gt;单 $\to$ 单、单 $\to$ 多（接收的消息相同）和单 $\to$ 多（接收的消息不同）&lt;/strong&gt;。针对这三种应用场景，RabbitMQ 有着与之对应的三种模式：&lt;strong&gt;简单模式、工作模式与交换机模式&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;并且已经了解了 RabbitMQ 的基本抽象结构。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E6%8A%BD%E8%B1%A1%E7%BB%93%E6%9E%84.png&quot; alt=&quot;抽象结构&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了使问题简化，我们一律只使用默认的虚拟主机 “/”，并且假设生产者、消费者和 RabbitMQ 服务器在同一设备上（即 RabbitMQ 服务器的地址为 &lt;code&gt;localhost&lt;/code&gt; 或 &lt;code&gt;127.0.0.1&lt;/code&gt;）。现在，让我们一一来看这三种模式。&lt;/p&gt;
&lt;h3&gt;简单模式&lt;/h3&gt;
&lt;p&gt;在简单模式下，是单 $\to$ 单的，一个生产者 Producer 产生的消息进入一个消息队列 Queue 后，将被直接发送给消费者 Consumer，因而不需要交换机 Exchange（因为 Exchange 用于将消息发送至多个队列）。于是我们只使用默认交换机 “”，并且无需设置 Binding。抽象结构如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AE%80%E5%8D%95%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;简单模式&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对于生产者而言，需要完成的步骤为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接到 RabbitMQ 服务器&lt;/li&gt;
&lt;li&gt;新建一个队列&lt;/li&gt;
&lt;li&gt;发送消息到队列中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而对于消费者而言，需要完成的步骤为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接到 RabbitMQ 服务器&lt;/li&gt;
&lt;li&gt;新建一个队列&lt;/li&gt;
&lt;li&gt;监听队列，接收消息&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么生产者和消费者都要新建一个队列？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为无法确定生产者和消费者的运行顺序。若只在生产者中新建队列，则当消费者先运行时，无法连接待监听队列；反之，若只在消费者中新建队列，则当生产者先运行时，无法发送消息到队列中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;生产者&lt;/h4&gt;
&lt;p&gt;简单模式下，一个完整的生产者程序示例如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection_params = pika.ConnectionParameters(host=&quot;localhost&quot;)
connection = pika.BlockingConnection(connection_params)
channel = connection.channel()

# 声明一个&apos;hello&apos;队列
queue_name = &apos;hello&apos;
channel.queue_declare(queue=queue_name)

# 发送消息到&apos;hello&apos;队列
message = &apos;Hello World!&apos;
channel.basic_publish(exchange=&apos;&apos;,
                      routing_key=queue_name,
                      body=message)

# 关闭连接
connection.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Step 1：连接 RabbitMQ 服务器&lt;/h5&gt;
&lt;p&gt;连接 RabbitMQ 服务器一般使用如下命令。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 设置连接参数
connection_params = pika.ConnectionParameters(host=&quot;localhost&quot;)

# 连接服务器
connection = pika.BlockingConnection(connection_params)
channel = connection.channel()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先设置了连接参数 &lt;code&gt;connection_params&lt;/code&gt; 为 &lt;code&gt;pika.ConnectionParameters(host = &quot;localhost&quot;)&lt;/code&gt;，表示连接本地主机 &lt;code&gt;localhost&lt;/code&gt;。&lt;code&gt;ConnectionParameters()&lt;/code&gt; 函数用于生成一个连接参数对象，一些常用参数的默认值如下。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;host = &quot;localhost&quot;&lt;/code&gt;：目标 RabbitMQ 服务器的主机名或 IP 地址&lt;/li&gt;
&lt;li&gt;&lt;code&gt;port = 5672&lt;/code&gt;：用于 AMQP 协议通信的端口号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;virtual_host = &apos;/&apos;&lt;/code&gt;：连接的虚拟主机名称&lt;/li&gt;
&lt;li&gt;&lt;code&gt;credentials = None&lt;/code&gt;：身份验证凭据。一般使用 &lt;code&gt;pika.PlainCredentials(username, password)&lt;/code&gt;；如为 &lt;code&gt;None&lt;/code&gt;，则默认使用用户名 &lt;code&gt;guest&lt;/code&gt; 和密码 &lt;code&gt;guest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;heartbeat = 60&lt;/code&gt;：心跳间隔。用于确保网络连接有效。设置为 0 将关闭心跳检测&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connection_attempts = 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;每次连接尝试之间等待的秒数：&lt;code&gt;retry_delay = 2.0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;pika.BlockingConnection(connection_params)&lt;/code&gt; 表示使用 &lt;code&gt;connection_params&lt;/code&gt; 参数，以阻塞方式连接 RabbitMQ 服务器。&lt;code&gt;BlockingConnection()&lt;/code&gt; 函数表示以阻塞方式连接，这是最常用的连接方式。还有一些其他连接方式，如 &lt;code&gt;SelectConnection()&lt;/code&gt;、&lt;code&gt;AsyncioConnection()&lt;/code&gt; 等。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;channel = connection.channel()&lt;/code&gt; 用于获取信道 Channel 对象，后续新建队列、发送信息等都是通过信道对象进行的。&lt;/p&gt;
&lt;h5&gt;Step 2：新建队列&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# 将队列名称设置为 &apos;hello&apos;
queue_name = &apos;hello&apos;

# 声明队列
channel.queue_declare(queue=queue_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;queue_declare()&lt;/code&gt; 方法可以新建一个队列，该方法至少传递一个参数 &lt;code&gt;queue&lt;/code&gt;，即队列名称。除了直接使用 &lt;code&gt;queue&lt;/code&gt; 参数指定队列名外，也可以将可选参数 &lt;code&gt;exclusive&lt;/code&gt; 设置为 &lt;code&gt;True&lt;/code&gt;，RabbitMQ 服务器将自动创建一个不会重名的队列，该队列可以通过 &lt;code&gt;.method.queue&lt;/code&gt; 获取名称。使用 &lt;code&gt;exclusive&lt;/code&gt; 参数的代码如下，这与上面给出的代码是等价的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 新建队列，并使用 .method.queue 获取队列名
queue_created = channel.queue_declare(&quot;&quot;,exclusive=True)
queue_name = queue_created.method.queue
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Step 3: 发送消息&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;# 将待发送的消息体设置为 &apos;Hello World!&apos;
message = &apos;Hello World!&apos;

# 发送消息
channel.basic_publish(exchange=&apos;&apos;,
                      routing_key=queue_name,
                      body=message)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;basic_publish()&lt;/code&gt; 方法可以将消息发送到指定队列。&lt;code&gt;basic_publish()&lt;/code&gt; 方法至少接收三个参数，包括交换机名称 &lt;code&gt;exchange&lt;/code&gt;、消息的路由键值 &lt;code&gt;routing_key&lt;/code&gt; 和消息体 &lt;code&gt;body&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在简单模式下，使用默认交换机 &lt;code&gt;&quot;&quot;&lt;/code&gt;，并将 &lt;code&gt;routing_key&lt;/code&gt; 设置为消息队列名称。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为什么要把 &lt;code&gt;routing_key&lt;/code&gt; 设置为消息队列名称？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先前，在 Binding 的名词解释中，我们提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;一个 Queue 与默认交换机绑定时的 Binding 是其名称。&lt;/li&gt;
&lt;li&gt;只有当消息的 routing key 与 Queue 的 Binding 相同时，该消息才能进入该 Queue 中。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这里，由于没有修改 Queue 的 Binding，所以消息的 &lt;code&gt;routing_key&lt;/code&gt; 应设置为 Queue 默认的 Binding，即消息队列的名称。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h5&gt;Step 4: 关闭连接&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;connection.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在生产者不必发送消息后，请养成良好的使用习惯，手动关闭连接。&lt;/p&gt;
&lt;h4&gt;消费者&lt;/h4&gt;
&lt;p&gt;简单模式下，一个完整的消费者程序示例如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection_params = pika.ConnectionParameters(host=&quot;localhost&quot;)
connection = pika.BlockingConnection(connection_params)
channel = connection.channel()

# 声明一个&apos;hello&apos;队列
queue_name = &apos;hello&apos;
channel.queue_declare(queue=queue_name)

# 以上部分消费者与生产者一致

# 定义callback函数，用于在收到消息后处理消息
def callback(ch, method, properties, body):
    print(&quot; [x] Received %r&quot; % body)

# 配置消费者：1.监听&apos;hello&apos;队列 2.收到消息后执行callback函数
channel.basic_consume(queue=queue_name,
                      auto_ack=True,    # 自动确认消息
                      on_message_callback=callback)

# 正式开始监听队列
channel.start_consuming()
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;Step 1：连接 RabbitMQ 服务器&lt;/h5&gt;
&lt;p&gt;同生产者中“连接 RabbitMQ 服务器”。&lt;/p&gt;
&lt;h5&gt;Step 2：新建队列&lt;/h5&gt;
&lt;p&gt;同生产者中“新建队列”。&lt;/p&gt;
&lt;h5&gt;Step 3：配置消费者&lt;/h5&gt;
&lt;p&gt;在开始监听队列、接收消息前，需要先配置好消费者。需要配置的项目包括需要监听的队列名称，以及收到消息后消费者的行为。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 定义callback函数，用于在收到消息后处理消息
# ch为channel对象，method为消息传递时用到的方法，properties为消息的属性，body为消息体
def callback(ch, method, properties, body):
    print(&quot; [x] Received %r&quot; % body)

# 配置消费者：1.监听&apos;hello&apos;队列 2.收到消息后执行callback函数
channel.basic_consume(queue=&apos;hello&apos;,
                      auto_ack=True,    # 自动确认消息
                      on_message_callback=callback)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个程序片段首先定义了 &lt;code&gt;callback()&lt;/code&gt; 函数，用于在收到消息后处理消息。&lt;code&gt;callback()&lt;/code&gt; 函数后的参数列表 &lt;code&gt;ch&lt;/code&gt;，&lt;code&gt;method&lt;/code&gt;，&lt;code&gt;properties&lt;/code&gt; 和 &lt;code&gt;body&lt;/code&gt; 是固定的，分别代表连接中的 Channel 对象、消息传递时用到的方法、消息的属性和消息体。在这个片段中，&lt;code&gt;callback()&lt;/code&gt; 函数只是简单地打印消息体的内容。&lt;/p&gt;
&lt;p&gt;在定义了 &lt;code&gt;callback()&lt;/code&gt; 函数后，需要使用 &lt;code&gt;basic_comsume()&lt;/code&gt; 方法配置消费者（注意此时还没有开始监听队列）。&lt;code&gt;basic_comsume()&lt;/code&gt; 方法至少接收两个参数，包括需要监听的队列名称 &lt;code&gt;queue&lt;/code&gt; 和收到消息后消费者的行为 &lt;code&gt;on_message_callback&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;你可能注意到，在 &lt;code&gt;basic_comsume()&lt;/code&gt; 方法中还有一个参数：&lt;code&gt;auto_ack&lt;/code&gt;。这个可选参数用于设置消费者是否自动确认消息，默认为 &lt;code&gt;False&lt;/code&gt;。关于自动 / 手动确认消息的内容在稍后会详细解释。&lt;/p&gt;
&lt;h5&gt;Step 4：正式监听队列并接收消息&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;channel.start_consuming()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时消费者会以阻塞方式持续监听 &lt;code&gt;hello&lt;/code&gt; 队列，当一条消息进入 &lt;code&gt;hello&lt;/code&gt; 队列中后，会被消费者立即接收。&lt;/p&gt;
&lt;h4&gt;常用参数&lt;/h4&gt;
&lt;h5&gt;自动 / 手动确认消息&lt;/h5&gt;
&lt;p&gt;先前，消费者程序示例的 &lt;code&gt;basic_comsume()&lt;/code&gt; 方法中出现了一个可选参数：&lt;code&gt;auto_ack&lt;/code&gt;。之前我们提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当一个 Consumer 接收 Queue 中的一条消息并确认后，该消息才会被销毁。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;auto_ack&lt;/code&gt; 便是用于配置消费者是否自动确认消息的。&lt;/p&gt;
&lt;p&gt;当 &lt;code&gt;auto_ack&lt;/code&gt; 设置为 &lt;code&gt;True&lt;/code&gt; 时，消息在发送到消费者中后，会立即被确认，随后在 Queue 中被销毁。然而，有时因为特殊原因，消费者程序在接收到消息后异常退出（比如在 &lt;code&gt;callback()&lt;/code&gt; 函数中出现语法错误），此时已发送到消费者程序中的消息将会永久丢失。若要在这种情况下不丢失消息，需要手动确认消息。&lt;/p&gt;
&lt;p&gt;当 &lt;code&gt;auto_ack&lt;/code&gt; 为 &lt;code&gt;False&lt;/code&gt; 时，消费者需要在 &lt;code&gt;callback()&lt;/code&gt; 函数中手动确认消息，如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 定义callback函数，用于在收到消息后处理消息
def callback(ch, method, properties, body):
    print(&quot; [x] Received %r&quot; % body)				# 打印消息体
    ch.basic_ack(delivery_tag=method.delivery_tag)  # 手动确认消息已被处理

# 配置消费者：1.监听&apos;hello2&apos;队列 2.收到消息后执行callback函数
channel.basic_consume(queue=&apos;hello&apos;,
                      auto_ack=False,    # 手动确认消息
                      on_message_callback=callback)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该程序片段只是在先前的消费者程序中改动了两点。&lt;/p&gt;
&lt;p&gt;首先，将 &lt;code&gt;basic_consume()&lt;/code&gt; 方法中的可选参数 &lt;code&gt;auto_ack&lt;/code&gt; 设置为 &lt;code&gt;False&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;然后，在 &lt;code&gt;callback()&lt;/code&gt; 函数中添加 &lt;code&gt;ch.basic_ack(delivery_tag=method.delivery_tag)&lt;/code&gt; 手动确认消息。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;basic_ack()&lt;/code&gt; 方法用于消费者确认消息。该方法接收两个可选参数：&lt;code&gt;delivery_tag&lt;/code&gt; 和 &lt;code&gt;multiple&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;delivery_tag&lt;/code&gt; 参数用于指定消息的投递标签，默认值为 &lt;code&gt;0&lt;/code&gt;。在同一个信道上，消息的投递标签按照时间顺序单调递增。通常使用 &lt;code&gt;method.delivery_tag&lt;/code&gt; 获取消费者当前正处理的消息。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;multiple&lt;/code&gt; 参数用于控制确认范围。默认值为 &lt;code&gt;False&lt;/code&gt;，表示只确认 &lt;code&gt;delivery_tag&lt;/code&gt; 指定的单条消息。若设置为 &lt;code&gt;True&lt;/code&gt;，则会确认所有投递标签小于等于 &lt;code&gt;delivery_tag&lt;/code&gt; 的尚未确认的消息。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;除了使用 &lt;code&gt;basic_ack()&lt;/code&gt; 方法确认消息外，还可以使用 &lt;code&gt;basic_nack()&lt;/code&gt; 和 &lt;code&gt;basic_reject()&lt;/code&gt; 拒绝消息。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;basic_nack()&lt;/code&gt; 方法除了接收 &lt;code&gt;delivery_tag&lt;/code&gt; 和 &lt;code&gt;multiple&lt;/code&gt; 两个可选参数外，还接收一个可选参数 &lt;code&gt;requeue&lt;/code&gt;。&lt;code&gt;requeue&lt;/code&gt; 参数用于配置被拒绝的消息是否重新回到消息队列 Queue 中。默认值为 &lt;code&gt;True&lt;/code&gt;，表示重新入队。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;basic_reject()&lt;/code&gt; 方法相比 &lt;code&gt;basic_nack()&lt;/code&gt; 方法只缺少一个可选参数 &lt;code&gt;multiple&lt;/code&gt;，即接收 &lt;code&gt;delivery_tag&lt;/code&gt; 和 &lt;code&gt;requeue&lt;/code&gt;两个可选参数，不批量拒绝消息。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;可持久化&lt;/h5&gt;
&lt;p&gt;除了消费者程序异常退出以外，Broker 服务器关闭后消息也会永久丢失。若要防止丢失消息，需要以下步骤。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建队列时，设置消息队列 Queue 为可持久化队列。&lt;/li&gt;
&lt;li&gt;发布消息时，设置生产者发送的消息为持久化消息。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;首先，在生产者和消费者程序中创建消息队列 Queue 时，设置消息队列为可持久化的，如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 在参数列表中添加 durable=True，以声明一个可持久化的&apos;durable_queue&apos;队列
channel.queue_declare(queue=&apos;durable_queue&apos;, durable=True)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;durable=True&lt;/code&gt; 表示创建的队列是可持久化队列。注意：“可持久化队列”不是“持久化队列”，其中的消息有持久化的，也有非持久化的。可持久化队列在关闭后只会保留持久化消息。&lt;/p&gt;
&lt;p&gt;然后，在生产者发送消息时，设置发送的消息为持久化消息，如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 在参数列表中添加 properties=pika.BasicProperties(delivery_mode=2)
# 发送持久化消息到&apos;hello&apos;队列
channel.basic_publish(exchange=&apos;&apos;,
                      routing_key=&apos;durable_queue&apos;,
                      body=&apos;Hello World!&apos;,
                      properties=pika.BasicProperties(delivery_mode=2))  # 标记消息为持久化
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;properties&lt;/code&gt; 参数为 &lt;code&gt;BasicProperties&lt;/code&gt; 对象类型，通常使用 &lt;code&gt;pika.BasicProperties()&lt;/code&gt; 生成一个 &lt;code&gt;BasicProperties&lt;/code&gt; 对象。其中，&lt;code&gt;delivery_mode&lt;/code&gt; 标记消息是否为持久化的，&lt;code&gt;1&lt;/code&gt; 代表非持久化，&lt;code&gt;2&lt;/code&gt; 代表持久化。&lt;/p&gt;
&lt;h5&gt;优先级&lt;/h5&gt;
&lt;p&gt;RabbitMQ 也支持优先级消息队列。使用优先级消息队列，需要以下步骤。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建队列时，设置消息队列 Queue 中消息的最大优先级。&lt;/li&gt;
&lt;li&gt;发布消息时，在消息的属性中设置消息的优先级。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果没有设置消息队列的最大优先级，那么即使在消息属性中设置了消息的优先级，消息的优先级也会被忽略。&lt;/p&gt;
&lt;p&gt;首先，在生产者和消费者程序中创建消息队列 Queue 时，添加 &lt;code&gt;x-max-priority&lt;/code&gt; 的参数，表示最大优先级。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 在参数列表中添加 &apos;x-max-priority&apos;，以声明一个&apos;priority_queue&apos;优先级队列
channel.queue_declare(
    queue=&apos;priority_queue&apos;,
    arguments={
        &apos;x-max-priority&apos;: 10  #设置最大优先级
    }
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;x-max-priority&lt;/code&gt; 参数的范围在 &lt;code&gt;1&lt;/code&gt; 到 &lt;code&gt;255&lt;/code&gt; 之间，必须在队列声明时设置，且后续不能修改。&lt;/p&gt;
&lt;p&gt;在生产者发送消息时，设置发送的消息为持久化消息，如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ch.basic_publish(exchange=&apos;&apos;,
            	routing_key=&apos;priority_queue&apos;,
            	body=&apos;Hello World!&apos;,
            	properties=pika.BasicProperties(priority=5))	# 标记消息的优先级为 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; 属性参数的范围在 &lt;code&gt;0&lt;/code&gt; 到 &lt;code&gt;x-max-priority&lt;/code&gt; 之间。&lt;code&gt;priority&lt;/code&gt; 的数值越大，优先级越高。当消息的 &lt;code&gt;priority&lt;/code&gt; 属性大于消息队列的 &lt;code&gt;x-max-priority&lt;/code&gt; 时，RabbitMQ 会将该消息的优先级截断为 &lt;code&gt;x-max-priority&lt;/code&gt; 的最大值。&lt;/p&gt;
&lt;h3&gt;工作模式&lt;/h3&gt;
&lt;p&gt;在工作模式下，是单 $\to$ 多，一个生产者 Producer 产生的消息进入一个消息队列 Queue 后，将被发送给多个消费者 Consumer，同样不需要交换机 Exchange。于是我们只使用默认交换机 “”，并且无需设置 Binding。抽象结构如下图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E5%B7%A5%E4%BD%9C%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;工作模式&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对于生产者 Producer 而言，需要完成的步骤不变。即：工作模式下，生产者程序的代码与简单模式下一致。&lt;/p&gt;
&lt;p&gt;对于消费者 Consumer 而言，需要完成的步骤也不变，可以沿用简单模式下消费者程序的代码。但是由于同一个消息队列 Queue 连接了多个消费者，Queue 发送消息到消费者的顺序就变得十分重要了。下面会一一介绍几种不同的分发顺序。&lt;/p&gt;
&lt;h4&gt;轮询分发&lt;/h4&gt;
&lt;p&gt;若直接运行简单模式下消费者程序的代码，RabbitMQ 会采用轮询分发模式，按照消费者连接到 Broker 的先后顺序，依次将消息发送给各个消费者，并且只有在当前消息被确认后，才会向下一个消费者发送下一条消息。&lt;/p&gt;
&lt;p&gt;例如，假设有两个消费者 &lt;code&gt;C1&lt;/code&gt; 和 &lt;code&gt;C2&lt;/code&gt;，且消费者 &lt;code&gt;C1&lt;/code&gt; 先于消费者 &lt;code&gt;C2&lt;/code&gt; 连接至 Broker。当 Queue 中存在两条消息 &lt;code&gt;M1&lt;/code&gt; 和 &lt;code&gt;M2&lt;/code&gt; 时，&lt;code&gt;M1&lt;/code&gt; 会先被发送给 &lt;code&gt;C1&lt;/code&gt;；只有在收到 &lt;code&gt;C1&lt;/code&gt; 对 &lt;code&gt;M1&lt;/code&gt; 的确认后，&lt;code&gt;M2&lt;/code&gt; 才会被发送给 &lt;code&gt;C2&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;然而，在默认的轮询分发模式下，若一个消费者处理消息的时间过长，会导致所有消费者无法接收消息。&lt;/p&gt;
&lt;h4&gt;公平分发&lt;/h4&gt;
&lt;p&gt;为了解决轮询分发的问题，RabbitMQ 提供了 &lt;code&gt;basic_qos()&lt;/code&gt; 方法，允许消费者提前取出一条或多条消息，进而调整分发模式。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;basic_qos()&lt;/code&gt; 方法的定义如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def basic_qos(
    prefetch_size: int = 0,
    prefetch_count: int = 0,
    global_qos: bool = False
) -&amp;gt; None
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;prefetch_size&lt;/code&gt; 代表了允许预取消息的总大小，单位为字节。默认值为 &lt;code&gt;0&lt;/code&gt;，表示不对消息的大小作出限制。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefetch_count&lt;/code&gt; 代表了预取消息的最大数量，这是最常用的参数。默认值为 &lt;code&gt;0&lt;/code&gt;，表示不允许预取。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;global_qos&lt;/code&gt; 代表当前参数是对当前连接 Connection 中所有信道 Channel 生效还是仅对本信道生效。默认值为 &lt;code&gt;False&lt;/code&gt;，表示仅对当前信道生效。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最常用的场景为公平分发模式，即每个消费者一次只处理一条消息。当一个消费者处理完消息后，会立即收到下一条消息。设置公平分发时，只需在消费者程序中正式监听前添加如下代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;channel.basic_qos(prefetch_count=1)
# 等同于 channel.basic_qos(prefetch_size=0, prefetch_count=1, global_=False)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，也可以将 &lt;code&gt;prefetch_count&lt;/code&gt; 设置为其他值，允许一个消费者程序同时取出多个消息。&lt;/p&gt;
&lt;h3&gt;交换机模式&lt;/h3&gt;
&lt;p&gt;交换机模式的抽象结构如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;交换机模式&quot; /&gt;&lt;/p&gt;
&lt;p&gt;交换机模式是 RabbitMQ 的核心内容。前面已经提到：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;交换机（Exchange）&lt;/strong&gt;：一个从 Producer 接收消息，并将消息发送到指定队列（由交换机自身类型和 Binding 决定）的对象。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;相比生产者 Producer 将消息直接发送到多个消息队列 Queue 中，使用交换机时，生产者只需要将消息发送一次到交换机中，能够显著降低生产者的网络带宽压力，将其转化为 RabbitMQ Broker 中的内部处理开销。&lt;/p&gt;
&lt;p&gt;基于交换机如何将消息分发到多个队列，RabbitMQ 界定了三种交换机类型，分别对应三种工作模式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;发布 / 订阅模式&lt;/strong&gt;：交换机类型为 &lt;code&gt;Fanout&lt;/code&gt;。交换机 Exchange 无条件地将消息转发到所有与其绑定的消息队列 Queue 中。此模式下，消息的 Routing Key 与消息队列的 Binding Key 均被忽略。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;关键字模式&lt;/strong&gt;：交换机类型为 &lt;code&gt;Direct&lt;/code&gt;。交换机 Exchange 接收到消息后，将消息发送到 Binding Key 与消息的 Routing Key 完全相同的消息队列 Queue 中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通配符模式&lt;/strong&gt;：交换机类型为 &lt;code&gt;Topic&lt;/code&gt;。交换机 Exchange 接收到消息后，将消息发送到 Binding Key 与消息的 Routing Key 模式匹配的消息队列 Queue 中。Binding Key 支持通配符，&lt;code&gt;*&lt;/code&gt; 匹配一个单词，&lt;code&gt;#&lt;/code&gt; 匹配零个或任意个单词。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在交换机模式下，对于生产者而言，需要完成的步骤为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接到 RabbitMQ 服务器&lt;/li&gt;
&lt;li&gt;新建一个交换机&lt;/li&gt;
&lt;li&gt;发送消息到交换机中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;而对于消费者而言，需要完成的步骤为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连接到 RabbitMQ 服务器&lt;/li&gt;
&lt;li&gt;新建一个交换机&lt;/li&gt;
&lt;li&gt;新建一个队列，并将队列绑定在交换机上&lt;/li&gt;
&lt;li&gt;监听队列，接收消息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;由于无法预知生产者程序与消费者程序的运行顺序，在二者中均需要新建交换机。&lt;/p&gt;
&lt;h4&gt;发布 / 订阅模式&lt;/h4&gt;
&lt;p&gt;在发布 / 订阅模式下，交换机将消息广播到多个消息队列的行为，称为“发布”；消息队列接收交换机发布的消息，称为“订阅”。交换机 Exchange 无条件地将消息发布到所有与其绑定的消息队列 Queue 中，示意图如下所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%A8%A1%E5%BC%8F-%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;交换机模式-发布订阅模式&quot; /&gt;&lt;/p&gt;
&lt;h5&gt;生产者&lt;/h5&gt;
&lt;p&gt;与简单模式下相比，发布 / 订阅模式下的生产者程序有以下几点区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不再新建消息队列，而是新建一个交换机&lt;/li&gt;
&lt;li&gt;发送消息时的交换机不再是默认交换机，并将 &lt;code&gt;routing_key&lt;/code&gt; 留空&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;发布 / 订阅模式下，一个完整的生产者程序示例如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(host=&quot;localhost&quot;))
channel = connection.channel()

# 声明一个&apos;logs&apos;交换机，设置类型为&apos;fanout&apos;
channel.exchange_declare(exchange = &apos;logs&apos;,
                         exchange_type = &apos;fanout&apos;)

# 发送消息到&apos;logs&apos;交换机
channel.basic_publish(exchange = &apos;logs&apos;,
                      routing_key = &apos;&apos;,
                      body = &apos;Hello World!&apos;)

# 关闭连接
connection.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Step 1：连接至 RabbitMQ 服务器&lt;/h6&gt;
&lt;h6&gt;Step 2：新建交换机&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;# 声明一个&apos;logs&apos;交换机
channel.exchange_declare(exchange = &apos;logs&apos;,
                         exchange_type = &apos;fanout&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;与新建消息队列 Queue 类似，使用 &lt;code&gt;exchange_declare()&lt;/code&gt; 函数可以新建一个交换机。&lt;code&gt;exchange_declare()&lt;/code&gt; 函数接收至少两个参数： 交换机名&lt;code&gt;exchange&lt;/code&gt; 和交换机类型 &lt;code&gt;exchange_type&lt;/code&gt;。当然，与新建消息队列时类似，也可以使用 &lt;code&gt;durable&lt;/code&gt; 和 &lt;code&gt;arguments&lt;/code&gt; 可选参数。&lt;/p&gt;
&lt;h6&gt;Step 3：发布消息&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;# 发送消息到&apos;logs&apos;交换机
channel.basic_publish(exchange = &apos;logs&apos;,
                      routing_key = &apos;&apos;,
                      body = &apos;Hello World!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;在发布 / 订阅模式下，需要将交换机类型 &lt;code&gt;exchange_type&lt;/code&gt; 设定为 &lt;code&gt;fanout&lt;/code&gt;，并在发送消息时将 &lt;code&gt;exchange&lt;/code&gt; 设置为交换机名称，并将 &lt;code&gt;routing_key&lt;/code&gt; 留空。&lt;/strong&gt; 虽然交换机在类型为 &lt;code&gt;fanout&lt;/code&gt; 时，会自动忽略消息的 &lt;code&gt;routing_key&lt;/code&gt;，但仍推荐将消息的 &lt;code&gt;routing_key&lt;/code&gt; 留空。&lt;/p&gt;
&lt;h6&gt;Step 4：关闭连接&lt;/h6&gt;
&lt;h5&gt;消费者&lt;/h5&gt;
&lt;p&gt;与简单模式下相比，发布 / 订阅模式下的消费者程序有以下几点区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;消费者程序在创建队列的同时，也需要创建一个交换机&lt;/li&gt;
&lt;li&gt;需要将队列绑定到交换机上&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;发布 / 订阅模式下，一个完整的消费者程序示例如下所示。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(&apos;localhost&apos;))
channel = connection.channel()

# 声明一个&apos;logs&apos;交换机，设置类型为&apos;fanout&apos;
channel.exchange_declare(exchange=&apos;logs&apos;,exchange_type=&apos;fanout&apos;)

# 创建一个队列
result = channel.queue_declare(&quot;&quot;,exclusive=True)
queue_name = result.method.queue

# 将队列绑定到交换机
channel.queue_bind(exchange=&apos;logs&apos;,queue=queue_name)

# 定义callback函数，用于在收到消息后处理消息
def callback(ch, method, properties, body):
    print(&quot; [x] %r&quot; % body)

# 配置消费者
channel.basic_consume(queue=queue_name,
                      auto_ack=True,
                      on_message_callback=callback)

# 正式开始监听队列
channel.start_consuming()
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Step 1：连接至RabbitMQ服务器&lt;/h6&gt;
&lt;h6&gt;Step 2：新建交换机&lt;/h6&gt;
&lt;h6&gt;Step 3：新建队列&lt;/h6&gt;
&lt;h6&gt;Step 4：将队列绑定到交换机&lt;/h6&gt;
&lt;pre&gt;&lt;code&gt;# 将队列绑定到交换机
channel.queue_bind(exchange=&apos;logs&apos;,queue=queue_name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;queue_bind()&lt;/code&gt; 函数用于将消息队列 Queue 与交换机 Exchange 绑定，其定义如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def queue_bind(
    queue,
    exchange,
    routing_key: Any | None = None,
    arguments: Any | None = None
) -&amp;gt; Any
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;queue_bind()&lt;/code&gt; 函数至少接收两个参数：队列名 &lt;code&gt;queue&lt;/code&gt; 和 交换机名 &lt;code&gt;exchange&lt;/code&gt;。&lt;strong&gt;当绑定的交换机类型为 &lt;code&gt;fanout&lt;/code&gt; 时，无需 &lt;code&gt;routing_key&lt;/code&gt; 参数；当交换机类型为 &lt;code&gt;direct&lt;/code&gt; 或 &lt;code&gt;topic&lt;/code&gt; 时，必须要提供 &lt;code&gt;routing_key&lt;/code&gt;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里表示将刚才声明的消息队列与 &lt;code&gt;logs&lt;/code&gt; 交换机绑定。&lt;/p&gt;
&lt;h6&gt;Step 5：配置消费者&lt;/h6&gt;
&lt;h6&gt;Step 6：正式监听队列并接收消息&lt;/h6&gt;
&lt;h4&gt;关键字模式&lt;/h4&gt;
&lt;p&gt;关键字模式下，交换机 Exchange 按照消息的 &lt;code&gt;routing_key&lt;/code&gt; 将其发送到具有相同 Binding Key 的消息队列 Queue 中，如下所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%A8%A1%E5%BC%8F-%E5%85%B3%E9%94%AE%E5%AD%97%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;交换机模式-关键字模式&quot; /&gt;&lt;/p&gt;
&lt;p&gt;相比发布 / 订阅模式，&lt;strong&gt;在关键字模式下：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;声明交换机时需要将交换机类型 &lt;code&gt;exchange_type&lt;/code&gt; 设定为 &lt;code&gt;direct&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产者在发送消息时需要将 &lt;code&gt;exchange&lt;/code&gt; 设置为交换机名称，并设置一个 &lt;code&gt;routing_key&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消费者在使用 &lt;code&gt;queue_bind()&lt;/code&gt; 函数将消息队列 Queue 绑定到交换机 Exchange 时，需要提供 &lt;code&gt;routing_key&lt;/code&gt; 参数。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以下分别是完整的生产者程序和消费者程序示例。&lt;/p&gt;
&lt;h5&gt;生产者&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(&apos;localhost&apos;))
channel = connection.channel()

# 声明一个&apos;logs&apos;交换机，设置类型为&apos;direct&apos;
channel.exchange_declare(exchange=&apos;logs&apos;,exchange_type=&apos;direct&apos;)

# 发送消息到&apos;logs&apos;交换机
channel.basic_publish(exchange=&apos;logs&apos;,
                      routing_key=&apos;info&apos;,
                      body=&apos;Hello World!&apos;)

connection.close()	# 关闭连接
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;消费者&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(&apos;localhost&apos;))
channel = connection.channel()

# 声明一个‘logs2’交换机，设置类型为‘direct’
channel.exchange_declare(exchange=&apos;logs&apos;,exchange_type=&apos;direct&apos;)

# 创建一个临时队列
result = channel.queue_declare(&quot;&quot;,exclusive=True)
queue_name = result.method.queue

# 将队列绑定到交换机，同时提供routing_key参数
channel.queue_bind(exchange=&apos;logs&apos;,queue=queue_name, routing_key=&apos;info&apos;)

# 定义callback函数，用于在收到消息后处理消息
def callback(ch, method, properties, body):
    print(&quot; [x] %r&quot; % body)

# 配置消费者
channel.basic_consume(queue=queue_name,
                      auto_ack=True,
                      on_message_callback=callback)

channel.start_consuming()	# 正式开始监听队列
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;通配符模式&lt;/h4&gt;
&lt;p&gt;在通配符模式下，交换机 Exchange 接收到消息后，将消息发送到 Binding Key 与消息的 Routing Key 模式匹配的消息队列 Queue 中，示意图如下所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BA%A4%E6%8D%A2%E6%9C%BA%E6%A8%A1%E5%BC%8F-%E9%80%9A%E9%85%8D%E7%AC%A6%E6%A8%A1%E5%BC%8F.png&quot; alt=&quot;交换机模式-通配符模式&quot; /&gt;&lt;/p&gt;
&lt;p&gt;相比关键字模式，&lt;strong&gt;在通配符模式下：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;声明交换机时需要将交换机类型 &lt;code&gt;exchange_type&lt;/code&gt; 设定为 &lt;code&gt;topic&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产者在发送消息时，所设置的 &lt;code&gt;routing_key&lt;/code&gt; 应采用 &lt;code&gt;A.B&lt;/code&gt; 这类多段格式（其中 &lt;code&gt;A&lt;/code&gt; 和 &lt;code&gt;B&lt;/code&gt; 为任意单词，且不包含通配符 &lt;code&gt;#&lt;/code&gt; 或 &lt;code&gt;*&lt;/code&gt;）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;消费者在使用 &lt;code&gt;queue_bind()&lt;/code&gt; 函数将消息队列 Queue 绑定到交换机 Exchange 时，提供的 &lt;code&gt;routing_key&lt;/code&gt; 参数同样为 &lt;code&gt;A.B&lt;/code&gt; 格式，但允许使用通配符&lt;code&gt;*&lt;/code&gt; 匹配一个单词或 &lt;code&gt;#&lt;/code&gt; 匹配零个或多个单词&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以下分别是完整的生产者程序和消费者程序示例。&lt;/p&gt;
&lt;h5&gt;生产者&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(&apos;localhost&apos;))
channel = connection.channel()

# 声明一个&apos;logs&apos;交换机，设置类型为&apos;topic&apos;
channel.exchange_declare(exchange=&apos;logs&apos;,exchange_type=&apos;topic&apos;)

# 发送消息到&apos;logs&apos;交换机
for sender in [&apos;system&apos;, &apos;user&apos;]:
    for level in [&apos;error&apos;, &apos;info&apos;, &apos;warning&apos;]:
        key = f&quot;{sender}.{level}&quot;
        channel.basic_publish(exchange=&apos;logs&apos;,
                              routing_key=key,
                              body=f&quot;[{sender}]: {level}&quot;)

connection.close()	# 关闭连接
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;消费者&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;import pika

# 连接至RabbitMQ服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(&apos;localhost&apos;))
channel = connection.channel()

# 声明一个&apos;logs&apos;交换机，设置类型为&apos;topic&apos;
channel.exchange_declare(exchange=&apos;logs3&apos;,exchange_type=&apos;topic&apos;)

# 创建一个临时队列
result = channel.queue_declare(&quot;&quot;,exclusive=True)
queue_name = result.method.queue

# 将队列绑定到交换机
channel.queue_bind(exchange=&apos;logs&apos;,
                   queue=queue_name,
                   routing_key=&quot;system.*&quot;) # 绑定所有以system.为开头且仅含两个关键字的路由键

# 定义callback函数，用于在收到消息后处理消息
def callback(ch, method, properties, body):
    print(&quot; [x] %r&quot; % body)

# 配置消费者
channel.basic_consume(queue=queue_name,
                      auto_ack=True,
                      on_message_callback=callback)

channel.start_consuming()	# 正式开始监听队列
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自此，RabbitMQ 的内容已全部结束，已经能满足生产中的绝大部分需求。如果需要更多高级用法，请查阅 RabbitMQ 官网中的文档。&lt;/p&gt;
</content:encoded></item><item><title>Jetson 配置指南</title><link>https://miafetta.github.io/posts/jetson-%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</link><guid isPermaLink="true">https://miafetta.github.io/posts/jetson-%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</guid><description>为 Jetson 安装系统、部署 Docker 并安装 YOLO</description><pubDate>Mon, 27 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本指南中所使用的 Jetson 型号为 NVIDIA Jetson Nano (B01) 4GB，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/Jetson-Nano-(B01)-4GB.png&quot; alt=&quot;Jetson Nano (B01) 4GB&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;系统烧录&lt;/h2&gt;
&lt;h3&gt;下载安装SD卡格式化工具 SD Card Formatter&lt;/h3&gt;
&lt;p&gt;SD Card Formatter 是由 SD 协会官方推出的一款专业开源工具。其界面简洁易用，提供稳定可靠的格式化功能，全面支持 FAT32、exFAT 和 NTFS 文件系统。该工具能够高效修复 SD 卡中潜在的错误，保障数据安全，同时优化存储设备的读写性能。&lt;/p&gt;
&lt;p&gt;以 Windows 系统为例，可以从 &lt;a href=&quot;https://www.sdcard.org/downloads/formatter/sd-memory-card-formatter-for-windows-download/&quot;&gt;SD Association&lt;/a&gt; 获取 SD Card Formatter 的最新版本。如图所示，打开网页后下拉到最底部，选择“Accept”后开始下载。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BB%8E%E5%AE%98%E7%BD%91%E4%B8%8A%E4%B8%8B%E8%BD%BDSD-Card-Formatter.jpg&quot; alt=&quot;从官网上下载SD Card Formatter&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下载完成后得到 &lt;code&gt;SDCardFormatterv5_WinEN.zip&lt;/code&gt; 压缩文件。解压后在 &lt;code&gt;SDCardFormatterv5_WinEN&lt;/code&gt; 文件夹中运行名为 &lt;code&gt;SD Card Formatter 5.0.3 Setup EN.exe&lt;/code&gt; 的安装程序，安装过程如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(1).png&quot; alt=&quot;SD Card Formatter的安装(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(2).png&quot; alt=&quot;SD Card Formatter的安装(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(3).png&quot; alt=&quot;SD Card Formatter的安装(3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(4).png&quot; alt=&quot;SD Card Formatter的安装(4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(5).png&quot; alt=&quot;SD Card Formatter的安装(5)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(6).png&quot; alt=&quot;SD Card Formatter的安装(6)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(7).png&quot; alt=&quot;SD Card Formatter的安装(7)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/SD-Card-Formatter%E7%9A%84%E5%AE%89%E8%A3%85(8).png&quot; alt=&quot;SD Card Formatter的安装(8)&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;下载安装系统镜像烧录软件 balenaEtcher&lt;/h3&gt;
&lt;p&gt;BalenaEtcher 是一款免费开源的跨平台镜像烧录工具，专为快速、安全地将操作系统镜像写入 USB 驱动器、SD 卡等可移动设备而设计。该工具拥有直观的三步操作流程，支持 Windows、macOS 和 Linux 三大操作系统，是烧录系统镜像的优选工具。&lt;/p&gt;
&lt;p&gt;以 Windows 系统为例，可以从 &lt;a href=&quot;https://etcher.balena.io/&quot;&gt;balenaEtcher&lt;/a&gt; 获取最新版本，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BB%8E%E5%AE%98%E7%BD%91%E4%B8%8A%E4%B8%8B%E8%BD%BDbalenaEtcher(1).png&quot; alt=&quot;从官网上下载balenaEtcher(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BB%8E%E5%AE%98%E7%BD%91%E4%B8%8A%E4%B8%8B%E8%BD%BDbalenaEtcher(2).png&quot; alt=&quot;从官网上下载balenaEtcher(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;balenaEtcher 的安装过程为无界面式设计。用户只需双击运行下载的 .exe 可执行文件，桌面即会出现一个如图所示的 balenaEtcher 图标窗口，期间软件将自动完成安装与配置，并在准备就绪后直接启动主程序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/balenaEtcher%E7%9A%84%E5%AE%89%E8%A3%85.png&quot; alt=&quot;balenaEtcher的安装&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;烧录系统镜像&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;下载 SD 卡镜像文件&lt;/p&gt;
&lt;p&gt;下载 &lt;a href=&quot;https://developer.nvidia.com/jetson-nano-sd-card-image&quot;&gt;Jetson Nano 开发者套件 SD 卡镜像文件&lt;/a&gt;，并记下其在电脑中的保存位置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;格式化 SD 卡&lt;/p&gt;
&lt;p&gt;将 microSD 卡插入到 SD 卡读卡器中，然后把读卡器连接至电脑，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E8%AF%BB%E5%8F%96SD%E5%8D%A1.jpg&quot; alt=&quot;读取SD卡&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行 SD Card Formatter，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(1).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择需要格式化的 SD 卡，之后选择“Quick Format”，将“Volume label”留空，点击“Format”开始格式化，并在弹出的警告对话框中选择“是”，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(2).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(3).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(4).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;等待格式化完毕后，在弹出的对话框中选择“确定”，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(5).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(5)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8SD-Card-Formatter%E6%A0%BC%E5%BC%8F%E5%8C%96SD%E5%8D%A1(6).png&quot; alt=&quot;使用SD Card Formatter格式化SD卡(6)&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将镜像写入 SD 卡&lt;/p&gt;
&lt;p&gt;以管理员身份运行 balenaEtcher，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(1).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击“从文件烧录”并选择之前下载的镜像文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(2).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(3).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击“选择目标磁盘”，选择格式化后的 SD 卡并点击“选定”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(4).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(5).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(5)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(6).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(6)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(7).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(7)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击“现在烧录！”，开始写入镜像。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(8).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(8)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(9).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(9)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(10).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(10)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E4%BD%BF%E7%94%A8balenaEtcher%E5%B0%86%E9%95%9C%E5%83%8F%E5%86%99%E5%85%A5SD%E5%8D%A1(11).png&quot; alt=&quot;使用balenaEtcher将镜像写入SD卡(11)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时 SD 卡已准备就绪。Windows 系统可能会提示无法读取 SD 卡，属于正常现象。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;配置 Jetson 系统&lt;/h2&gt;
&lt;h3&gt;首次启动前准备&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;准备一个 microSD 储存卡，并烧录好系统。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将 SD 卡插入 Jetson Nano 的 SD 卡插槽中，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E6%8F%92%E5%85%A5SD%E5%8D%A1.png&quot; alt=&quot;插入SD卡&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;用 HDMI 接口连接 Jetson Nano 和显示器，用 USB 接口连接键盘与鼠标。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在 DC 接口处连接直流电源。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;首次启动系统&lt;/h3&gt;
&lt;p&gt;连接电源后，Jetson Nano会立即启动。首先将显示如图所示的 NIVIDIA 图标，之后经历如图所示的一系列自检等过程，直到启动如图所示的系统设置窗口。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(1).png&quot; alt=&quot;第一次启动系统(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(2).png&quot; alt=&quot;第一次启动系统(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(3).png&quot; alt=&quot;第一次启动系统(3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;勾选“I accept the terms of these licenses”，并点击“Continue”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(4).png&quot; alt=&quot;第一次启动系统(4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(5).png&quot; alt=&quot;第一次启动系统(5)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在语言设置中，下拉到底部，找到并选择“中文(简体)”，然后点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(6).png&quot; alt=&quot;第一次启动系统(6)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(7).png&quot; alt=&quot;第一次启动系统(7)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在键盘布局设置中保持默认设置，点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(8).png&quot; alt=&quot;第一次启动系统(8)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在无线网络设置中，选择“连接到这个网络”，找到并选择自己的 Wi-Fi，点击“连接”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(9).png&quot; alt=&quot;第一次启动系统(9)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(10).png&quot; alt=&quot;第一次启动系统(10)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在弹出的窗口中输入 Wi-Fi 密码，点击“Connect”，稍等连接完毕后，点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(11).png&quot; alt=&quot;第一次启动系统(11)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(12).png&quot; alt=&quot;第一次启动系统(12)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(13).png&quot; alt=&quot;第一次启动系统(13)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;地区设置保持为默认“Shanghai”，并点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(14).png&quot; alt=&quot;第一次启动系统(14)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在用户设置页面，依次设置姓名、主机名、用户名、密码，并选择“自动登录”。设置完毕后，点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(15).png&quot; alt=&quot;第一次启动系统(15)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(16).png&quot; alt=&quot;第一次启动系统(16)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在 APP 分区大小设置中，保持其为默认设置（即 &lt;code&gt;Maximum accepted size&lt;/code&gt; 的大小），点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(17).png&quot; alt=&quot;第一次启动系统(17)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在功率模式设置中，保持默认设置，点击“继续”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(18).png&quot; alt=&quot;第一次启动系统(18)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;稍后系统会自动完成设置，如图所示。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(19).png&quot; alt=&quot;第一次启动系统(19)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自动设置完毕后进入桌面，显示如图界面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(20).png&quot; alt=&quot;第一次启动系统(20)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;首次启动后，建议运行 &lt;code&gt;sudo reboot&lt;/code&gt; 命令重启 Jetson Nano，重新进入系统后看到如图所示桌面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%90%AF%E5%8A%A8%E7%B3%BB%E7%BB%9F(21).png&quot; alt=&quot;第一次启动系统(21)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自此，首次启动过程完成。&lt;/p&gt;
&lt;h3&gt;系统本地化设置&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;apt 软件源设置&lt;/p&gt;
&lt;p&gt;由于默认 apt 软件源的服务器在国外，下载速度缓慢，于是使用清华大学开源软件镜像站提供的 Ubuntu Ports 软件仓库替换系统默认的 apt 软件源。&lt;/p&gt;
&lt;p&gt;首先打开 Terminal，将原先的 apt 软件源备份。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后使用如下命令，修改 apt 软件源。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo tee /etc/apt/sources.list &amp;lt;&amp;lt;-&apos;EOF&apos;
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse
deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后运行“sudo apt update”以应用更改。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;系统语言设置&lt;/p&gt;
&lt;p&gt;虽然在首次启动系统时已经修改了系统语言，但是系统大部分界面仍为英文。我们可以通过 Ubuntu 提供的图形化界面修改系统语言为中文。&lt;/p&gt;
&lt;p&gt;双击左侧任务栏中的“System Settings”，打开系统设置页面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(1).png&quot; alt=&quot;系统语言设置(1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击“Language Support”，打开语言支持。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(2).png&quot; alt=&quot;系统语言设置(2)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在语言支持界面中提示“语言支持没有安装完整”，点击“安装”。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(3).png&quot; alt=&quot;系统语言设置(3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;输入用户密码，点击“Authenticate”，等待语言支持安装完毕后，点击“Close”关闭窗口。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(4).png&quot; alt=&quot;系统语言设置(4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(5).png&quot; alt=&quot;系统语言设置(5)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(6).png&quot; alt=&quot;系统语言设置(6)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;重启系统后，可以看到系统界面已全部变为中文。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./media/%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80%E8%AE%BE%E7%BD%AE(7).png&quot; alt=&quot;系统语言设置(7)&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;安装并配置 Docker 容器&lt;/h2&gt;
&lt;h3&gt;安装 Docker&lt;/h3&gt;
&lt;p&gt;首先运行升级所有系统软件包。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 Jetson Nano 开发者套件中已默认安装好 Docker，可以通过以下命令验证 Docker 是否成功安装并更新到最新版本。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo docker version
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;使用国内镜像加速 Docker&lt;/h3&gt;
&lt;p&gt;运行如下命令，添加国内镜像，并重启 Docker 服务。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json &amp;lt;&amp;lt;-&apos;EOF&apos;
{
    &quot;registry-mirrors&quot;: [
        &quot;https://docker.1ms.run&quot;,
        &quot;https://dockercf.jsdelivr.fyi/&quot;,
        &quot;https://docker.m.daocloud.io&quot;
    ]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;将当前用户加入 docker 用户组&lt;/h3&gt;
&lt;p&gt;在使用 docker 命令前，每次都需要添加“sudo”前缀。通过将当前用户加入 docker 用户组，可以让当前用户直接运行 docker 命令。&lt;/p&gt;
&lt;p&gt;首先创建 docker 用户组。通常在安装 Docker 时，docker 用户组会自动创建，但也可以运行以下命令来创建或确认：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo groupadd docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果已存在 docker 用户组则会提示：groupadd：“docker”组已存在。&lt;/p&gt;
&lt;p&gt;然后将当前用户添加到 docker 用户组中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo usermod -aG docker $USER

# 也可以使用下面的命令
# sudo usermod -aG docker $(whoami)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注销并重新登录当前用户后生效。&lt;/p&gt;
&lt;h3&gt;验证 Docker 的安装与配置&lt;/h3&gt;
&lt;p&gt;运行如下命令。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo docker run --rm hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;终端中输出如下信息，说明 Docker 的安装与配置过程完成。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Unable to find image &apos;hello-world:latest&apos; locally
latest: Pulling from library/hello-world
198f93fd5094: Pull complete 
Digest: sha256:a0dfb02aac212703bfcb339d77d47ec32c8706ff250850ecc0e19c8737b18567
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub.
    (arm64v8)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;在容器中安装 YOLO&lt;/h2&gt;
&lt;h3&gt;配置 NVIDIA 运行时&lt;/h3&gt;
&lt;p&gt;运行 YOLO11 需要 NVIDIA 运行时，而 Docker 内默认不含 NVIDIA 运行时，需要额外配置。&lt;/p&gt;
&lt;p&gt;运行如下命令，编辑或创建 Docker 的配置文件（以下配置文件包含镜像和 NIVIDIA 运行时）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json &amp;lt;&amp;lt;-&apos;EOF&apos;
{
    &quot;registry-mirrors&quot;: [
        &quot;https://docker.1ms.run&quot;,
        &quot;https://dockercf.jsdelivr.fyi/&quot;,
        &quot;https://docker.m.daocloud.io&quot;
    ],
    &quot;runtimes&quot;: {
        &quot;nvidia&quot;: {
            &quot;path&quot;: &quot;nvidia-container-runtime&quot;,
            &quot;runtimeArgs&quot;: []
        }
    },
	&quot;default-runtime&quot;: &quot;nvidia&quot;
}
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启 Docker 服务以应用编辑好的配置。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;安装 YOLO11&lt;/h3&gt;
&lt;p&gt;本项目所使用的 NVIDIA Jetson Nano (B01) 4GB 仅支持 JetPack 4。查阅 &lt;a href=&quot;https://docs.ultralytics.com/zh/guides/nvidia-jetson/#jetpack-support-based-on-jetson-device&quot;&gt;快速入门指南：Ultralytics YOLO11 与 NVIDIA Jetson&lt;/a&gt; 可以得知，可以直接运行如下命令以安装 YOLO11。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t=ultralytics/ultralytics:latest-jetson-jetpack4
sudo docker pull $t &amp;amp;&amp;amp; sudo docker run -it --ipc=host --runtime=nvidia $t
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等待安装完成后，自动进入 Docker 命令行，运行如下命令验证 YOLO 的安装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -c &quot;import torch; print(f&apos;PyTorch CUDA available: {torch.cuda.is_available()}&apos;)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;若提示 &lt;code&gt;PyTorch CUDA available: True&lt;/code&gt;，说明 YOLO 已成功安装。&lt;/p&gt;
&lt;p&gt;为了便于使用，也可以用 &lt;code&gt;docker ps -a&lt;/code&gt; 查阅容器名称，并重命名容器。&lt;/p&gt;
</content:encoded></item></channel></rss>