使用 AMQ C++ 客户端


Red Hat AMQ Clients 2.11

用于 AMQ Clients 2.11

摘要

本指南论述了如何安装和配置客户端,运行实践示例,并将您的客户端与其他 AMQ 组件一起使用。

使开源包含更多

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。我们从这四个术语开始:master、slave、黑名单和白名单。由于此项工作十分艰巨,这些更改将在即将推出的几个发行版本中逐步实施。有关更多详情,请参阅我们的首席技术官 Chris Wright 提供的消息

第 1 章 概述

AMQ C++ 是用于开发消息传递应用程序的库。它允许您编写发送和接收 AMQP 消息的 C++ 应用程序。

AMQ C++ 是 AMQ 客户端的一部分,这是支持多种语言和平台的一系列消息传递库。有关客户端的概述,请参阅 AMQ 客户端概述。有关此发行版本的详情,请参考 AMQ Clients 2.11 发行注记

AMQ C++ 基于 Apache Qpid 中的 Proton API。有关详细的 API 文档,请参阅 AMQ C++ API 参考

1.1. 主要特性

  • 简化了与现有应用程序集成的事件驱动的 API
  • 用于安全通信的 SSL/TLS
  • 灵活的 SASL 身份验证
  • 自动重新连接和故障转移
  • AMQP 和语言原生数据类型之间的无缝转换
  • 访问 AMQP 1.0 的所有特性和功能

1.2. 支持的标准和协议

AMQ C++ 支持以下行业认可的标准和网络协议:

1.3. 支持的配置

有关 AMQ C++ 支持的配置 的当前信息,请参阅红帽客户门户网站上的 Red Hat AMQ 支持的配置。

1.4. 术语和概念

本节介绍核心 API 实体,并描述它们如何一起工作。

Expand
表 1.1. API 术语
实体描述

Container

连接的顶级容器。

连接

网络上两个对等点间的通信的频道。它包含会话。

会话

发送和接收消息的上下文。它包含发送者和接收器。

sender

将信息发送到目标的频道。它有一个目标。

receiver

从源接收消息的频道。它有一个源。

用于消息的命名源点。

目标

消息的命名目的地。

消息

特定于应用程序的信息片段。

交付

消息传输。

AMQ C++ 发送并 接收消息。通过 发送方 和接收器在连接的对等点之间传输消息。发件人和接收器通过会话 建立。会话通过 连接建立。连接在两个唯一标识 的容器 之间建立。虽然连接可以有多个会话,但通常不需要这样做。API 允许您忽略会话,除非您需要它们。

发送对等点会创建发送邮件的发送者。发件人具有在远程对等点上标识队列或主题 的目标。接收对等点会创建一个接收消息的接收器。接收器有一个 ,用于标识远程对等点上的队列或主题。

消息发送称为发送。消息是发送的内容,包括标头和注解等所有元数据。交付是与该内容传输相关联的协议交换。

要指示发送已经完成,发送是发送,可以是发送,也可以是接收方。当另一端了解了它时,它将不再传达该交付。接收器也可以指示它接受或拒绝该消息。

1.5. 文档惯例

sudo 命令

在本文档中,sudo 用于任何需要 root 特权的命令。使用 sudo 时请谨慎操作,因为任何更改都可能会影响整个系统。有关 sudo 的更多信息,请参阅使用 sudo 命令

文件路径

在本文档中,所有文件路径都对 Linux、UNIX 和类似操作系统(例如 /home/andrea)有效。在 Microsoft Windows 上,您必须使用对应的 Windows 路径(例如 C:\Users\andrea)。

变量文本

本文档包含代码块,其中的变量必须替换为特定于您的环境的值。变量文本以箭头括号括起,样式为方便的 monospace。例如,使用以下命令将 < project-dir&gt; 替换为环境的值:

$ cd <project-dir>
Copy to Clipboard Toggle word wrap

第 2 章 安装

本章介绍了在您的环境中安装 AMQ C++ 的步骤。

2.1. 先决条件

  • 您必须具有访问 AMQ 发行文件和软件仓库 的订阅
  • 要在 Red Hat Enterprise Linux 上安装软件包,您必须 注册您的系统
  • 要在 Red Hat Enterprise Linux 上使用 AMQ C++ 构建程序,您必须安装 gcc-c++、 cmake,并 制作 软件包。
  • 要在 Microsoft Windows 上使用 AMQ C++ 构建程序,您必须安装 Visual Studio。

2.2. 在 Red Hat Enterprise Linux 上安装

流程

  1. 使用 subscription-manager 命令订阅所需的软件包存储库。将 <version > 替换为主发行流的 2,或将 2.11 替换用于长期支持发行流。如有必要,将 < variant> 替换为您的 Red Hat Enterprise Linux 变体的值(如 serverworkstation)。

    Red Hat Enterprise Linux 7

    $ sudo subscription-manager repos --enable=amq-clients-<version>-for-rhel-7-<variant>-rpms
    Copy to Clipboard Toggle word wrap

    Red Hat Enterprise Linux 8

    $ sudo subscription-manager repos --enable=amq-clients-<version>-for-rhel-8-x86_64-rpms
    Copy to Clipboard Toggle word wrap

  2. 使用 yum 命令安装 qpid-proton-cpp-develqpid-proton-cpp-docs 软件包。

    $ sudo yum install qpid-proton-cpp-devel qpid-proton-cpp-docs
    Copy to Clipboard Toggle word wrap

有关使用软件包的详情请参考 附录 B, 使用 Red Hat Enterprise Linux 软件包

2.3. 在 Microsoft Windows 上安装

流程

  1. 打开浏览器并登录红帽客户门户网站 产品下载页面,网址为 access.redhat.com/downloads
  2. 找到 INTEGRATION AND AUTOMATION 类别中的 Red Hat AMQ Clients 条目。
  3. Red Hat AMQ Clients。此时会打开 Software Downloads 页面。
  4. 下载 AMQ Clients 2.11.0 C++ .zip 文件。
  5. 将文件内容提取到您选择的目录中,方法是右键单击 zip 文件并选择 Extract All

当您提取 .zip 文件的内容时,会创建一个名为 amq-clients-2.11.0-cpp-win 的目录。这是安装的顶级目录,在整个文档中被称为 < install-dir& gt;。

第 3 章 开始使用

本章介绍了设置环境并运行简单的消息传递程序的步骤。

3.1. 先决条件

  • 您必须为您的环境 完成安装过程
  • 您必须有一个 AMQP 1.0 消息代理侦听接口 localhost 和端口 5672 上的连接。它必须启用匿名访问权限。如需更多信息,请参阅 启动代理
  • 您必须有一个名为 example 的队列。如需更多信息,请参阅创建队列

3.2. 在 Red Hat Enterprise Linux 上运行 Hello World

Hello World 示例创建了一个与代理的连接,发送一条消息,其中包含到 示例 队列的问候消息,然后接收它。成功时,它会将收到的消息输出到控制台。

流程

  1. 将示例复制到您选择的位置。

    $ cp -r /usr/share/proton/examples/cpp cpp-examples
    Copy to Clipboard Toggle word wrap
  2. 创建构建目录并更改到该目录:

    $ mkdir cpp-examples/bld
    $ cd cpp-examples/bld
    Copy to Clipboard Toggle word wrap
  3. 使用 cmake 配置构建,并使用 make 编译示例。

    $ cmake ..
    $ make
    Copy to Clipboard Toggle word wrap
  4. 运行 helloworld 程序。

    $ ./helloworld
    Hello World!
    Copy to Clipboard Toggle word wrap

第 4 章 例子

本章演示了通过示例程序使用 AMQ C++。

如需了解更多示例,请参阅 AMQ C++ 示例套件和 Qpid Proton C++ 示例

注意

本指南中介绍的代码使用 C++11 功能。AMQ C++ 也与 C++03 兼容,但代码需要次要修改。

4.1. 发送消息

此客户端程序使用 < connection-url > 连接到服务器,为目标 <address& gt; 创建发件人,发送一条消息,包含 <message-body >、关闭连接并退出。

示例:发送消息

#include <proton/connection.hpp>
#include <proton/container.hpp>
#include <proton/message.hpp>
#include <proton/messaging_handler.hpp>
#include <proton/sender.hpp>
#include <proton/target.hpp>

#include <iostream>
#include <string>

struct send_handler : public proton::messaging_handler {
    std::string conn_url_ {};
    std::string address_ {};
    std::string message_body_ {};

    void on_container_start(proton::container& cont) override {
        cont.connect(conn_url_);

        // To connect with a user and password:
        //
        // proton::connection_options opts {};
        // opts.user("<user>");
        // opts.password("<password>");
        //
        // cont.connect(conn_url_, opts);
    }

    void on_connection_open(proton::connection& conn) override {
        conn.open_sender(address_);
    }

    void on_sender_open(proton::sender& snd) override {
        std::cout << "SEND: Opened sender for target address '"
                  << snd.target().address() << "'\n";
    }

    void on_sendable(proton::sender& snd) override {
        proton::message msg {message_body_};
        snd.send(msg);

        std::cout << "SEND: Sent message '" << msg.body() << "'\n";

        snd.close();
        snd.connection().close();
    }
};

int main(int argc, char** argv) {
    if (argc != 4) {
        std::cerr << "Usage: send <connection-url> <address> <message-body>\n";
        return 1;
    }

    send_handler handler {};
    handler.conn_url_ = argv[1];
    handler.address_ = argv[2];
    handler.message_body_ = argv[3];

    proton::container cont {handler};

    try {
        cont.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << "\n";
        return 1;
    }

    return 0;
}
Copy to Clipboard Toggle word wrap

运行示例

要运行示例程序,请将其复制到本地文件,对其进行编译并从命令行执行。如需更多信息,请参阅 第 3 章 开始使用

$ g++ send.cpp -o send -std=c++11 -lstdc++ -lqpid-proton-cpp
$ ./send amqp://localhost queue1 hello
Copy to Clipboard Toggle word wrap

4.2. 接收信息

此客户端程序使用 < connection-url > 连接到服务器,为源 <address& gt; 创建一个接收器,并接收信息直到终止或到达 < count> 信息。

示例:接收信息

#include <proton/connection.hpp>
#include <proton/container.hpp>
#include <proton/delivery.hpp>
#include <proton/message.hpp>
#include <proton/messaging_handler.hpp>
#include <proton/receiver.hpp>
#include <proton/source.hpp>

#include <iostream>
#include <string>

struct receive_handler : public proton::messaging_handler {
    std::string conn_url_ {};
    std::string address_ {};
    int desired_ {0};
    int received_ {0};

    void on_container_start(proton::container& cont) override {
        cont.connect(conn_url_);

        // To connect with a user and password:
        //
        // proton::connection_options opts {};
        // opts.user("<user>");
        // opts.password("<password>");
        //
        // cont.connect(conn_url_, opts);
    }

    void on_connection_open(proton::connection& conn) override {
        conn.open_receiver(address_);
    }

    void on_receiver_open(proton::receiver& rcv) override {
        std::cout << "RECEIVE: Opened receiver for source address '"
                  << rcv.source().address() << "'\n";
    }

    void on_message(proton::delivery& dlv, proton::message& msg) override {
        std::cout << "RECEIVE: Received message '" << msg.body() << "'\n";

        received_++;

        if (received_ == desired_) {
            dlv.receiver().close();
            dlv.connection().close();
        }
    }
};

int main(int argc, char** argv) {
    if (argc != 3 && argc != 4) {
        std::cerr << "Usage: receive <connection-url> <address> [<message-count>]\n";
        return 1;
    }

    receive_handler handler {};
    handler.conn_url_ = argv[1];
    handler.address_ = argv[2];

    if (argc == 4) {
        handler.desired_ = std::stoi(argv[3]);
    }

    proton::container cont {handler};

    try {
        cont.run();
    } catch (const std::exception& e) {
        std::cerr << e.what() << "\n";
        return 1;
    }

    return 0;
}
Copy to Clipboard Toggle word wrap

运行示例

要运行示例程序,请将其复制到本地文件,对其进行编译并从命令行执行。如需更多信息,请参阅 第 3 章 开始使用

$ g++ receive.cpp -o receive -std=c++11 -lstdc++ -lqpid-proton-cpp
$ ./receive amqp://localhost queue1
Copy to Clipboard Toggle word wrap

第 5 章 使用 API

如需更多信息,请参阅 AMQ C++ API 参考AMQ C++ 示例套件

5.1. 处理消息传递事件

AMQ C++ 是一个异步事件驱动的 API。为了定义应用如何处理事件,用户在 messaging_handler 类上实施回调方法。然后,这些方法被称为网络活动或计时器触发新事件。

示例:处理消息传递事件

struct example_handler : public proton::messaging_handler {
    void on_container_start(proton::container& cont) override {
        std::cout << "The container has started\n";
    }

    void on_sendable(proton::sender& snd) override {
        std::cout << "A message can be sent\n";
    }

    void on_message(proton::delivery& dlv, proton::message& msg) override {
        std::cout << "A message is received\n";
    }
};
Copy to Clipboard Toggle word wrap

这些只是几个常见情况事件。完整集合记录在 API 参考 中。

5.2. 创建容器

容器是顶级 API 对象。它是创建连接的入口点,它负责运行主事件循环。它通常由全局事件处理程序构建。

示例:创建容器

int main() {
    example_handler handler {};
    proton::container cont {handler};
    cont.run();
}
Copy to Clipboard Toggle word wrap

5.3. 设置容器身份

每个容器实例具有唯一的身份,称为容器 ID。当 AMQ C++ 进行连接时,它会将容器 ID 发送到远程对等点。要设置容器 ID,请将其传递给 proton::container 构造器。

示例:设置容器身份

proton::container cont {handler, "job-processor-3"};
Copy to Clipboard Toggle word wrap

如果用户未设置 ID,则当容器被决定时,库会生成一个 UUID。

第 6 章 网络连接

6.1. 连接 URL

连接 URL 对用于建立新连接的信息进行编码。

连接 URL 语法

scheme://host[:port]
Copy to Clipboard Toggle word wrap

  • scheme - 连接传输,用于未加密的 TCP amqp 或使用 SSL/TLS 加密的 TCP 的 amqp。
  • Host - 远程网络主机。该值可以是主机名或数字 IP 地址。IPv6 地址必须包括在方括号中。
  • port - 远程网络端口。这个值是可选的。对于 amqp 方案,默认值是 5672,amqps 方案的 5671。

连接 URL 示例

amqps://example.com
amqps://example.net:56720
amqp://127.0.0.1
amqp://[::1]:2000
Copy to Clipboard Toggle word wrap

6.2. 创建出站连接

要连接到远程服务器,请使用 连接 URL 调用 container::connect () 方法。这通常在 messaging_handler::on_container_start () 方法内完成。

示例:创建传出连接

class example_handler : public proton::messaging_handler {
    void on_container_start(proton::container& cont) override {
        cont.connect("amqp://example.com");
    }

    void on_connection_open(proton::connection& conn) override {
        std::cout << "The connection is open\n";
    }
};
Copy to Clipboard Toggle word wrap

有关创建安全连接的详情,请参考 第 7 章 安全性

6.3. 配置重新连接

重新连接可让客户端从丢失的连接中恢复。它用于确保分布式系统中的组件在临时网络或组件故障后重新建立通信。

AMQ C++ 默认禁用重新连接。要启用它,请将 reconnect connection 选项设置为 reconnect_options 类的实例。

示例:启用重新连接

proton::connection_options opts {};
proton::reconnect_options ropts {};

opts.reconnect(ropts);

container.connect("amqp://example.com", opts);
Copy to Clipboard Toggle word wrap

启用重新连接后,如果连接丢失或连接尝试失败,客户端会在短暂的延迟后重试。每次新尝试都会增加延迟。

要控制连接尝试之间的延迟,请设置 delay、 delay _multipliermax_delay 选项。所有持续时间都以毫秒为单位指定。

要限制重新连接尝试次数,请设置 max_attempts 选项。将它设置为 0 可移除任何限制。

示例:配置重新连接

proton::connection_options opts {};
proton::reconnect_options ropts {};

ropts.delay(proton::duration(10));
ropts.delay_multiplier(2.0);
ropts.max_delay(proton::duration::FOREVER);
ropts.max_attempts(0);

opts.reconnect(ropts);

container.connect("amqp://example.com", opts);
Copy to Clipboard Toggle word wrap

6.4. 配置故障转移

AMQ C++ 允许您配置多个连接端点。如果连接到一个失败,客户端会尝试连接到列表中的下一个。如果列表耗尽,该过程会启动。

要指定备用连接端点,请将 failover_urls reconnect 选项设置为连接 URL 列表。

示例:配置故障切换

std::vector<std::string> failover_urls = {
    "amqp://backup1.example.com",
    "amqp://backup2.example.com"
};

proton::connection_options opts {};
proton::reconnect_options ropts {};

opts.reconnect(ropts);
ropts.failover_urls(failover_urls);

container.connect("amqp://primary.example.com", opts);
Copy to Clipboard Toggle word wrap

6.5. 接受进入的连接

AMQ C++ 可以接受入站网络连接,使您能够构建自定义消息传递服务器。

要开始侦听连接,请使用 proton::container::listen () 方法,以及包含要侦听的本地主机地址和端口的 URL。

示例:接受传入的连接

class example_handler : public proton::messaging_handler {
    void on_container_start(proton::container& cont) override {
        cont.listen("0.0.0.0");
    }

    void on_connection_open(proton::connection& conn) override {
        std::cout << "New incoming connection\n";
    }
};
Copy to Clipboard Toggle word wrap

特殊的 IP 地址 0.0.0.0 侦听所有可用的 IPv4 接口。要侦听所有 IPv6 接口,请使用 [::0]

如需更多信息,请参阅 服务器 receive.cpp 示例

第 7 章 安全性

7.1. 使用 SSL/TLS 保护连接

AMQ C++ 使用 SSL/TLS 来加密客户端和服务器之间的通信。

要使用 SSL/TLS 连接到远程服务器,请设置 ssl_client_options 连接选项,并使用带有 amqps 方案的连接 URL。ssl_client_options 构造器使用 CA 证书的文件名、目录或数据库 ID。

示例:启用 SSL/TLS

proton::ssl_client_options sopts {"/etc/pki/ca-trust"};
proton::connection_options opts {};

opts.ssl_client_options(sopts);

container.connect("amqps://example.com", opts);
Copy to Clipboard Toggle word wrap

7.2. 使用用户和密码连接

AMQ C++ 可以使用用户和密码验证连接。

要指定用于身份验证的凭证,请在 connect 方法上设置 用户和密码 选项。

示例:使用用户和密码连接

proton::connection_options opts {};

opts.user("alice");
opts.password("secret");

container.connect("amqps://example.com", opts);
Copy to Clipboard Toggle word wrap

7.3. 配置 SASL 身份验证

AMQ C++ 使用 SASL 协议来执行身份验证。SASL 可以使用多种不同的 验证机制。当两个网络对等连接时,它们交换其允许的机制,并选择了这两者允许的最强机制。

注意

客户端使用 Cyrus SASL 执行身份验证。Cyrus SASL 使用插件来支持特定的 SASL 机制。在使用特定的 SASL 机制前,必须安装相关的插件。例如,您需要 cyrus-sasl-plain 插件才能使用 SASL PLAIN 身份验证。

要查看 Red Hat Enterprise Linux 中的 Cyrus SASL 插件列表,请使用 yum search cyrus-sasl 命令。要安装 Cyrus SASL 插件,请使用 yum install PLUG-IN 命令。

默认情况下,AMQ C++ 允许本地 SASL 库配置支持的所有机制。要限制允许的机制,从而控制可以协商哪些机制,请使用 sasl_allowed_mechs 连接选项。这个选项接受包含空格分隔的机制名称列表的字符串。

示例:配置 SASL 身份验证

proton::connection_options opts {};

opts.sasl_allowed_mechs("ANONYMOUS");

container.connect("amqps://example.com", opts);
Copy to Clipboard Toggle word wrap

这个示例强制连接使用 ANONYMOUS 机制进行身份验证,即使我们连接的服务器可以提供其他选项。有效机制包括 ANONYMOUS,PLAIN,SCRAM-SHA-256,SCRAM-SHA-1,GSSAPI, 和 EXTERNAL

AMQ C++ 默认启用 SASL。要禁用它,将 sasl_enabled 连接选项设置为 false。

示例:禁用 SASL

proton::connection_options opts {};

opts.sasl_enabled(false);

container.connect("amqps://example.com", opts);
Copy to Clipboard Toggle word wrap

7.4. 使用 Kerberos 进行身份验证

Kerberos 是一种网络协议,用于根据加密票据的交换集中管理身份验证。如需更多信息,请参阅使用 Kerberos

  1. 在您的操作系统中配置 Kerberos。请参阅 配置 Kerberos 在 Red Hat Enterprise Linux 中设置 Kerberos。
  2. 在客户端应用程序中启用 GSSAPI SASL 机制。

    proton::connection_options opts {};
    
    opts.sasl_allowed_mechs("GSSAPI");
    
    container.connect("amqps://example.com", opts);
    Copy to Clipboard Toggle word wrap
  3. 使用 kinit 命令验证您的用户凭证并存储生成的 Kerberos 票据。

    $ kinit USER@REALM
    Copy to Clipboard Toggle word wrap
  4. 运行客户端程序。

第 8 章 发件人和接收器

客户端使用发送方和接收器链接来代表传递消息的频道。发件人和接收器是单向的,消息来源的源结尾和消息目的地的目标结尾。

源和目标通常指向消息代理上的队列或主题。源也用于代表订阅。

8.1. 根据需要创建队列和主题

有些消息服务器支持按需创建队列和主题。附加发送方或接收器时,服务器使用发送者目标地址或接收器源地址来创建名称与地址匹配的队列或主题。

邮件服务器通常默认为创建队列(用于一对一消息发送)或主题(用于一对多消息发送)。客户端可以通过在源或目标上设置 队列或主题 功能来指示首选情况。

要选择队列或主题语义,请按照以下步骤执行:

  1. 配置您的消息服务器,以自动创建队列和主题。这通常是默认配置。
  2. 在发送者目标或接收器源上设置 队列或主题 功能,如下例所示。

示例:发送到按需创建的队列

void on_container_start(proton::container& cont) override {
    proton::connection conn = cont.connect("amqp://example.com");
    proton::sender_options opts {};
    proton::target_options topts {};

    topts.capabilities(std::vector<proton::symbol> { "queue" });
    opts.target(topts);

    conn.open_sender("jobs", opts);
}
Copy to Clipboard Toggle word wrap

示例:从按需创建的主题接收

void on_container_start(proton::container& cont) override {
    proton::connection conn = cont.connect("amqp://example.com");
    proton::receiver_options opts {};
    proton::source_options sopts {};

    sopts.capabilities(std::vector<proton::symbol> { "topic" });
    opts.source(sopts);

    conn.open_receiver("notifications", opts);
}
Copy to Clipboard Toggle word wrap

如需了解更多详细信息,请参阅以下示例:

8.2. 创建持久订阅

持久化订阅是远程服务器上的一个状态,代表一个消息接收器。通常,当客户端关闭时,消息接收方会被丢弃。但是,由于持久订阅是持久的,客户端可以从它们分离,之后再重新连接。当客户端重新附加时,任何在分离时收到的消息都可用。

持久化订阅通过组合客户端容器 ID 和接收器名称来组成订阅 ID 来唯一标识。它们必须具有稳定的值,以便可以恢复订阅。

要创建持久订阅,请按照以下步骤执行:

  1. 将连接容器 ID 设置为 stable 值,如 client-1

    proton::container cont {handler, "client-1"};
    Copy to Clipboard Toggle word wrap
  2. 使用稳定名称(如 sub-1 )创建一个接收器,并通过设置 persistence _mode 和 expiry_policy 选项来为持久性 配置接收器源:

    void on_container_start(proton::container& cont) override {
        proton::connection conn = cont.connect("amqp://example.com");
        proton::receiver_options opts {};
        proton::source_options sopts {};
    
        opts.name("sub-1");
        sopts.durability_mode(proton::source::UNSETTLED_STATE);
        sopts.expiry_policy(proton::source::NEVER);
    
        opts.source(sopts);
    
        conn.open_receiver("notifications", opts);
    }
    Copy to Clipboard Toggle word wrap

要从订阅分离,请使用 proton::receiver::detach () 方法。要终止订阅,请使用 proton::receiver::close () 方法。

如需更多信息,请参阅 durable-subscribe.cpp 示例

8.3. 创建共享订阅

共享订阅是远程服务器中代表一个或多个消息接收器的状态。由于它是共享的,多个客户端可以从同一消息流使用。

客户端通过在接收器源上设置 共享功能来配置共享 订阅。

共享订阅通过组合客户端容器 ID 和接收器名称来组成订阅 ID 来唯一标识。它们必须具有稳定的值,以便多个客户端进程可以找到相同的订阅。如果除了 共享 外设置了 全局 功能,则仅接收方名称用于标识订阅。

要创建持久订阅,请按照以下步骤执行:

  1. 将连接容器 ID 设置为 stable 值,如 client-1

    proton::container cont {handler, "client-1"};
    Copy to Clipboard Toggle word wrap
  2. 使用稳定名称(如 sub-1 )创建一个接收器,并通过设置共享功能来配置用于 共享的 接收器源:

    void on_container_start(proton::container& cont) override {
        proton::connection conn = cont.connect("amqp://example.com");
        proton::receiver_options opts {};
        proton::source_options sopts {};
    
        opts.name("sub-1");
        sopts.capabilities(std::vector<proton::symbol> { "shared" });
    
        opts.source(sopts);
    
        conn.open_receiver("notifications", opts);
    }
    Copy to Clipboard Toggle word wrap

要从订阅分离,请使用 proton::receiver::detach () 方法。要终止订阅,请使用 proton::receiver::close () 方法。

如需更多信息,请参阅 shared-subscribe.cpp 示例

第 9 章 消息交付

9.1. 发送消息

若要发送消息,可覆盖 on_sendable 事件处理程序,并调用 sender::send () 方法。当 proton::sender 有足够信的信用来发送至少一条消息时, 发送的事件将触发。

示例:发送消息

struct example_handler : public proton::messaging_handler {
    void on_container_start(proton::container& cont) override {
        proton::connection conn = cont.connect("amqp://example.com");
        conn.open_sender("jobs");
    }

    void on_sendable(proton::sender& snd) override {
        proton::message msg {"job-1"};
        snd.send(msg);
    }
};
Copy to Clipboard Toggle word wrap

9.2. 跟踪发送的消息

发送消息后,发件人可以保留对代表传输的 tracker 对象的引用。接收器接受或拒绝发送的每个消息。发件人会通知每个跟踪发送的结果。

要监控发送的消息的结果,请覆盖 on_tracker_accepton_tracker_reject 事件处理程序,并将交付状态更新映射到从 send () 返回的 tracker。

示例:跟踪发送的消息

void on_sendable(proton::sender& snd) override {
    proton::message msg {"job-1"};
    proton::tracker trk = snd.send(msg);
}

void on_tracker_accept(proton::tracker& trk) override {
    std::cout << "Delivery for " << trk << " is accepted\n";
}

void on_tracker_reject(proton::tracker& trk) override {
    std::cout << "Delivery for " << trk << " is rejected\n";
}
Copy to Clipboard Toggle word wrap

tracker 对象具有一个 tag () 方法,用于访问每个发送的唯一标识符。delivery 标签可用于存储在连接失败后重新发送的消息。

9.3. 接收信息

要接收消息,请创建一个接收器并覆盖 on_message 事件处理程序。

示例:接收信息

struct example_handler : public proton::messaging_handler {
    void on_container_start(proton::container& cont) override {
        proton::connection conn = cont.connect("amqp://example.com");
        conn.open_receiver("jobs");
    }

    void on_message(proton::delivery& dlv, proton::message& msg) override {
        std::cout << "Received message '" << msg.body() << "'\n";
    }
};
Copy to Clipboard Toggle word wrap

delivery 对象具有一个 tag () 方法,用于访问每个发送的唯一标识符。

9.4. 确认收到的消息

要显式接受或拒绝发送,请使用 on_message 事件处理程序中的 delivery::accept ()delivery::reject () 方法。

示例:确认收到的信息

void on_message(proton::delivery& dlv, proton::message& msg) override {
    try {
        process_message(msg);
        dlv.accept();
    } catch (std::exception& e) {
        dlv.reject();
    }
}
Copy to Clipboard Toggle word wrap

默认情况下,如果您没有明确确认发送,则库会在 on_message 返回后接受它。要禁用此行为,将 auto_accept receiver 选项设置为 false。

第 10 章 错误处理

AMQ C++ 中的错误可以通过两种不同的方式处理:

  • 捕获例外
  • 覆盖事件处理功能以截获 AMQP 协议或连接错误

捕获例外是最基本但最精细的方法来处理错误。如果没有在处理程序函数中使用覆盖处理错误,则会抛出异常。

10.1. 捕获例外

如果没有在事件处理函数中使用覆盖处理错误,则 容器运行 方法会抛出异常。

AMQ C++ 丢弃从 proton::error 类继承的所有例外,后者又从 std::runtime_errorstd::exception 类继承。

以下示例演示了如何捕获 AMQ C++ 中抛出的异常:

示例:特定于 API 的异常处理

try {
    // Something that might throw an exception
} catch (proton::error& e) {
    // Handle Proton-specific problems here
} catch (std::exception& e) {
    // Handle more general problems here
}
Copy to Clipboard Toggle word wrap

如果您不需要特定于 API 的异常处理,您只需要捕获 std::exception,因为 proton::error 会继承它。

10.2. 处理连接和协议错误

您可以通过覆盖以下 messaging_handler 方法来处理协议级错误:

  • on_transport_error(proton::transport&)
  • on_connection_error(proton::connection&)
  • on_session_error(proton::session&)
  • on_receiver_error(proton::receiver&)
  • on_sender_error(proton::sender&)

每当事件发生特定对象时,这些事件处理例程都会调用。在调用错误处理程序后,也会调用相应的关闭处理程序。

如果没有覆盖多个更具体的错误处理程序,则调用默认错误处理程序:

  • on_error(proton::error_condition&)
注意

由于在出现任何错误时调用关闭处理程序,因此仅需要在错误处理程序中处理错误本身。资源清理可以通过关闭的处理程序来管理。如果没有特定于特定对象的错误处理,则通常使用常规 on_error 处理程序,且没有更具体的处理程序。

注意

当启用重新连接并且远程服务器关闭具有 amqp:connection:forced 条件的连接时,客户端不会将其视为错误,因此不会触发 on_connection_error 处理程序。相反,客户端会开始重新连接过程。

第 11 章 日志记录

11.1. 启用协议日志记录

客户端可以将 AMQP 协议帧记录到控制台。诊断问题时,这些数据通常至关重要。

要启用协议日志记录,请将 PN_TRACE_FRM 环境变量设置为 1:

示例:启用协议日志记录

$ export PN_TRACE_FRM=1
$ <your-client-program>
Copy to Clipboard Toggle word wrap

要禁用协议日志记录,请取消设置 PN_TRACE_FRM 环境变量。

第 12 章 线程和调度

AMQ C++ 支持使用 C++11 及之后的版本的完整多线程。旧版本 C++ 可能会有有限的多线程。请参阅 第 12.6 节 “使用旧版本的 C++”

12.1. 线程模型

容器 对象可以同时处理多个连接。当 AMQP 事件在连接上发生时,容器会调用 messaging_handler 回调函数。任何一个连接的回调都会按顺序化(不同时调用),但不同连接的回调可以安全地并行执行。

您可以使用 handler connection 选项,将处理程序分配给 container::connect ()listen_handler::on_accept () 中的连接。建议为每个连接创建一个单独的处理程序,以便处理程序不需要锁定或其他同步来保护它不受库线程的并发使用。如果有任何非library 线程同时使用处理程序,则需要同步。

12.2. thread-safety 规则

连接会话发送者接收器跟踪器 和交付 对象不是线程安全状态,受以下规则约束:

  1. 您必须只使用它们来自 messaging_handler 回调或 work_queue 函数。
  2. 对于另一个连接,不要使用属于来自回调的一个连接的对象。
  3. 您可以在成员变量中存储 AMQ C++ 对象,以便在后续回调中使用,只要您遵守规则 2。

message 对象是一个值类型,其线程限制与标准 C++ 内置类型相同。它不能同时修改。

12.3. 工作队列

work_queue 接口提供了一种安全的方法,可在不同的连接处理程序之间或非library 线程和连接处理程序之间进行通信。

  • 每个连接都有一个关联的 work_queue
  • 工作队列是 thread-safe (C++11 或更高版本)。任何线程都可以添加工作。
  • work 项是一个 std::function,绑定参数则称为事件回调。

当库调用工作函数时,它被安全序列化,以便您可以像事件回调一样对待工作功能,并安全地访问其中存储的处理器和 AMQ C++ 对象。

12.4. wake primitive

connection::wake () 方法通过触发 on_connection_wake () 回调,允许任何线程在连接上提示活动。这是 连接 上唯一的 thread-safe 方法。

wake () 是一个轻量级、低级别的原语,用于在线程之间进行信号。

  • 它不执行任何代码或数据,这与 work_queue 不同。
  • wake () 的多个调用可能会合并到一个 on_connection_wake () 中。
  • 可能会出现对 on_connection_wake () 的调用,而无需任何应用程序调用 wake (),因为库在内部使用 wake ()

wake () 的语义与 std::condition_variable::notify_one () 类似。会有一个唤醒,但必须有一些共享应用程序状态来确定发生唤醒的原因,以及要对其执行什么操作。

工作队列在很多实例中更易于使用,但是如果您已经拥有自己的外部线程安全队列,并且需要高效的方法来唤醒连接来检查它们的数据,则 wake () 可能很有用。

12.5. 调度延迟工作

AMQ C++ 能够延迟后执行代码。您可以使用它来实现应用程序中的基于时间的行为,如定期调度的工作或超时。

要延迟固定时间的工作,请使用 schedule 方法设置延迟并注册定义工作的功能。

示例:在延迟后发送消息

void on_sender_open(proton::sender& snd) override {
    proton::duration interval {5 * proton::duration::SECOND};
    snd.work_queue().schedule(interval, [=] { send(snd); });
}

void send(proton::sender snd) {
    if (snd.credit() > 0) {
        proton::message msg {"hello"};
        snd.send(msg);
    }
}
Copy to Clipboard Toggle word wrap

本例在发件人的工作队列中使用 schedule 方法,以将其建立为工作的执行上下文。

12.6. 使用旧版本的 C++

在 C++11 之前,在 C++ 中不支持线程。您可以将 AMQ C++ 与线程一起使用,但有以下限制。

  • 容器不创建线程。它仅使用调用 container::run () 的单个线程。
  • AMQ C++ 库类都不是 thread-safe,包括 containerwork_queue。您需要一个外部锁定才能在多个线程中使用 容器。唯一的例外是 connection::wake ()。即使在较旧的 C++ 中,它是线程安全。

container::schedule ()work_queue API 接受 C++11 lambda 函数来定义工作单元。如果您使用不支持 lambda 的 C++ 版本,则必须使用 make_work () 函数。

第 13 章 基于文件的配置

AMQ C++ 可以读取用于从名为 connect.json 的本地文件中建立连接的配置选项。这可让您在部署时在应用程序中配置连接。

当应用调用容器连接方法时,库会尝试读取文件,而无需提供任何连接选项。

13.1. 文件位置

如果设置,AMQ C++ 将使用 MESSAGING_CONNECT_FILE 环境变量的值来定位配置文件。

如果没有设置 MESSAGING_CONNECT_FILE,AMQ C++ 会在以下位置搜索名为 connect.json 的文件,并按照所示的顺序搜索。它在第一次遇到的匹配项时停止。

对于 Linux:

  1. $PWD/connect.json,其中 $PWD 是客户端进程的当前工作目录
  2. $HOME/.config/messaging/connect.json,其中 $HOME 是当前用户主目录
  3. /etc/messaging/connect.json

在 Windows 上:

  1. %CD%/connect.json,其中 %cd% 是客户端进程的当前工作目录

如果没有找到 connect.json 文件,则库为所有选项使用默认值。

13.2. 文件格式

connect.json 文件包含 JSON 数据,具有对 JavaScript 注释的额外支持。

所有配置属性都是可选的,或者具有默认值,因此一个简单的示例只需要提供几个详情:

示例:一个简单的 connect.json 文件

{
    "host": "example.com",
    "user": "alice",
    "password": "secret"
}
Copy to Clipboard Toggle word wrap

SASL 和 SSL/TLS 选项嵌套在 "sasl""tls" 命名空间下:

示例:带有 SASL 和 SSL/TLS 选项的 connect.json 文件

{
    "host": "example.com",
    "user": "ortega",
    "password": "secret",
    "sasl": {
        "mechanisms": ["SCRAM-SHA-1", "SCRAM-SHA-256"]
    },
    "tls": {
        "cert": "/home/ortega/cert.pem",
        "key": "/home/ortega/key.pem"
    }
}
Copy to Clipboard Toggle word wrap

13.3. 配置选项

选项键包含一个点(.),代表嵌套在命名空间内的属性。

Expand
表 13.1. connect.json中的配置选项
值类型默认值描述

scheme

string

"amqps"

"AMQP" 用于明文或 SSL/TLS "amqps"

主机

string

"localhost"

远程主机的主机名或 IP 地址

port

字符串或数字

"amqps"

端口号或端口字面

user

string

进行身份验证的用户名

密码

string

进行身份验证的密码

sasl.mechanisms

list 或 string

none (系统默认设置)

启用 SASL 机制的 JSON 列表。裸机字符串代表一种机制。如果未指定,客户端将使用系统提供的默认机制。

sasl.allow_insecure

布尔值

false

启用发送明文密码的机制

tls.cert

string

客户端证书的文件名或数据库 ID

tls.key

string

客户端证书的私钥的文件名或数据库 ID

tls.ca

string

CA 证书的文件名、目录或数据库 ID

tls.verify

布尔值

true

需要具有匹配主机名的有效服务器证书

第 14 章 互操作性

本章讨论了如何将 AMQ C++ 与其他 AMQ 组件结合使用。有关 AMQ 组件的兼容性概述,请参阅 产品简介

14.1. 与其他 AMQP 客户端交互

AMQP 消息使用 AMQP 类型系统 组成。这种通用格式是以不同语言的 AMQP 客户端能够相互互操作的原因之一。

发送消息时,AMQ C++ 会自动将语言原生类型转换为 AMQP 编码数据。收到信息时,反向转换就发生。

注意

有关 AMQP 类型的更多信息,请访问由 Apache Qpid 项目维护 的交互式类型参考

Expand
表 14.1. AMQP 类型
AMQP 类型描述

null

一个空值

布尔值

true 或 false 值

char

单个 Unicode 字符

string

一系列 Unicode 字符

二进制

一个字节序列

byte

签名的 8 位整数

short

签名的 16 位整数

int

签名的 32 位整数

long

签名的 64 位整数

ubyte

未签名的 8 位整数

ushort

未签名的 16 位整数

uint

未签名的 32 位整数

ulong

未签名的 64 位整数

浮点值

32 位浮点号

double

64 位浮点号

数组

单个类型的值的序列

list

变量类型的序列值

map

从不同键到值的映射

uuid

通用唯一标识符

符号

来自受限域的 7 位 ASCII 字符串

timestamp

一个绝对时间点

Expand
表 14.2. 在编码前和解码后 AMQ C++ 类型
AMQP 类型编码前的 AMQ C++ 类型解码后 AMQ C++ 类型

null

nullptr

nullptr

布尔值

bool

bool

char

wchar_t

wchar_t

string

std::string

std::string

二进制

proton::binary

proton::binary

byte

int8_t

int8_t

short

int16_t

int16_t

int

int32_t

int32_t

long

int64_t

int64_t

ubyte

uint8_t

uint8_t

ushort

uint16_t

uint16_t

uint

uint32_t

uint32_t

ulong

uint64_t

uint64_t

浮点值

浮点值

浮点值

double

double

double

list

std::vector

std::vector

map

std::map

std::map

uuid

proton::uuid

proton::uuid

符号

proton::symbol

proton::symbol

timestamp

proton::timestamp

proton::timestamp

Expand
表 14.3. AMQ C++ 和其他 AMQ 客户端类型(2 为 1)
编码前的 AMQ C++ 类型AMQ JavaScript 类型AMQ .NET 类型

nullptr

null

null

bool

布尔值

system.Boolean

wchar_t

number

system.Char

std::string

string

system.String

proton::binary

string

System.Byte[]

int8_t

number

system.SByte

int16_t

number

System.Int16

int32_t

number

System.Int32

int64_t

number

System.Int64

uint8_t

number

system.Byte

uint16_t

number

System.UInt16

uint32_t

number

System.UInt32

uint64_t

number

System.UInt64

浮点值

number

system.Single

double

number

system.Double

std::vector

Array

Amqp.List

std::map

object

Amqp.Map

proton::uuid

number

system.Guid

proton::symbol

string

Amqp.Symbol

proton::timestamp

number

System.DateTime

Expand
表 14.4. AMQ C++ 和其他 AMQ 客户端类型(2 为 2)
编码前的 AMQ C++ 类型AMQ Python 类型AMQ Ruby 类型

nullptr

nil

bool

bool

true, false

wchar_t

unicode

字符串

std::string

unicode

字符串

proton::binary

bytes

字符串

int8_t

int

整数

int16_t

int

整数

int32_t

long

整数

int64_t

long

整数

uint8_t

long

整数

uint16_t

long

整数

uint32_t

long

整数

uint64_t

long

整数

浮点值

浮点值

浮点值

double

浮点值

浮点值

std::vector

list

Array

std::map

dict

hash

proton::uuid

-

-

proton::symbol

str

符号

proton::timestamp

long

Time

14.2. 使用 AMQ JMS 进行交互

AMQP 定义标准映射到 JMS 消息传递模型。本节讨论该映射的各个方面。如需更多信息,请参阅 AMQ JMS Interoperability 章节。

JMS 消息类型

AMQ C++ 提供单个消息类型,其正文类型可能会有所不同。相反,JMS API 使用不同的消息类型来代表不同类型的数据。下表标明特定的正文类型如何映射到 JMS 消息类型。

若要更明确地控制生成的 JMS 消息类型,您可以设置 x-opt-jms-msg-type 消息注释。如需更多信息,请参阅 AMQ JMS Interoperability 章节。

Expand
表 14.5. AMQ C++ 和 JMS 消息类型
AMQ C++ 正文类型JMS 消息类型

std::string

TextMessage

nullptr

TextMessage

proton::binary

BytesMessage

任何其他类型

ObjectMessage

14.3. 连接到 AMQ Broker

AMQ Broker 旨在与 AMQP 1.0 客户端互操作。检查以下内容以确保为 AMQP 消息传递配置了代理:

附录 A. 使用您的订阅

AMQ 通过软件订阅提供。要管理您的订阅,请访问红帽客户门户中的帐户。

A.1. 访问您的帐户

流程

  1. 转至 access.redhat.com
  2. 如果您还没有帐户,请创建一个帐户。
  3. 登录到您的帐户。

A.2. 激活订阅

流程

  1. 转至 access.redhat.com
  2. 导航到 My Subscriptions
  3. 导航到 激活订阅 并输入您的 16 位激活号。

A.3. 下载发行文件

要访问 .zip、.tar.gz 和其他发布文件,请使用客户门户查找要下载的相关文件。如果您使用 RPM 软件包或 Red Hat Maven 存储库,则不需要这一步。

流程

  1. 打开浏览器并登录红帽客户门户网站 产品下载页面,网址为 access.redhat.com/downloads
  2. 查找 INTEGRATION 目录中的红帽 AMQ 条目。
  3. 选择所需的 AMQ 产品。此时会打开 Software Downloads 页面。
  4. 单击组件的 Download 链接。

A.4. 为系统注册软件包

要在 Red Hat Enterprise Linux 上安装此产品的 RPM 软件包,必须注册您的系统。如果您使用下载的发行文件,则不需要这一步。

流程

  1. 转至 access.redhat.com
  2. 进入 Registration Assistant
  3. 选择您的操作系统版本,再继续到下一页。
  4. 使用您的系统终端中列出的命令完成注册。

有关注册您的系统的更多信息,请参阅以下资源之一:

附录 B. 使用 Red Hat Enterprise Linux 软件包

这部分论述了如何使用作为 Red Hat Enterprise Linux 的 RPM 软件包提供的软件。

要确保此产品的 RPM 软件包可用,您必须首先 注册您的系统

B.1. 概述

库或服务器等组件通常关联有多个软件包。您不必安装它们。您只能安装您需要的用户。

主软件包通常具有最简单的名称,而不带有其他限定符。这个软件包提供了在程序运行时使用组件所需的所有接口。

名称以 -devel 结尾的软件包包含 C 和 C++ 库的标头。在编译时,需要它们来构建依赖于此软件包的程序。

名称以 -docs 结尾的软件包包含组件的文档和示例程序。

有关使用 RPM 软件包的更多信息,请参阅以下资源之一:

B.2. 搜索软件包

要搜索软件包,请使用 yum search 命令。搜索结果包括软件包名称,您可以在本节中列出的其他命令中用作 & lt;package > 的值。

$ yum search <keyword>...
Copy to Clipboard Toggle word wrap

B.3. 安装软件包

要安装软件包,请使用 yum install 命令。

$ sudo yum install <package>...
Copy to Clipboard Toggle word wrap

B.4. 查询软件包信息

要列出系统中安装的软件包,请使用 rpm -qa 命令。

$ rpm -qa
Copy to Clipboard Toggle word wrap

要获取有关特定软件包的信息,请使用 rpm -qi 命令。

$ rpm -qi <package>
Copy to Clipboard Toggle word wrap

要列出与软件包关联的所有文件,请使用 rpm -ql 命令。

$ rpm -ql <package>
Copy to Clipboard Toggle word wrap

附录 C. 使用带有示例的 AMQ Broker

AMQ C++ 示例需要一个正在运行的消息代理,其中包含名为 example 的队列。使用以下步骤安装和启动代理并定义队列。

C.1. 安装代理

按照 AMQ Broker 入门 中的说明 来安装代理 并创建代理实例。启用匿名访问。

以下流程将代理实例的位置称为 < broker-instance-dir>

C.2. 启动代理

流程

  1. 使用 artemis run 命令启动代理。

    $ <broker-instance-dir>/bin/artemis run
    Copy to Clipboard Toggle word wrap
  2. 检查控制台输出,以查看启动期间记录的所有关键错误。代理日志服务器 现在在服务器就绪时处于实时 状态。

    $ example-broker/bin/artemis run
               __  __  ____    ____            _
         /\   |  \/  |/ __ \  |  _ \          | |
        /  \  | \  / | |  | | | |_) |_ __ ___ | | _____ _ __
       / /\ \ | |\/| | |  | | |  _ <| '__/ _ \| |/ / _ \ '__|
      / ____ \| |  | | |__| | | |_) | | | (_) |   <  __/ |
     /_/    \_\_|  |_|\___\_\ |____/|_|  \___/|_|\_\___|_|
    
     Red Hat AMQ <version>
    
    2020-06-03 12:12:11,807 INFO  [org.apache.activemq.artemis.integration.bootstrap] AMQ101000: Starting ActiveMQ Artemis Server
    ...
    2020-06-03 12:12:12,336 INFO  [org.apache.activemq.artemis.core.server] AMQ221007: Server is now live
    ...
    Copy to Clipboard Toggle word wrap

C.3. 创建队列

在新终端中,使用 artemis queue 命令创建名为 example 的队列

$ <broker-instance-dir>/bin/artemis queue create --name examples --address examples --auto-create-address --anycast
Copy to Clipboard Toggle word wrap

系统将提示您回答一系列 yes 或没有问题。全部答案为 N

创建队列后,代理就可以与示例程序一起使用。

C.4. 停止代理

运行完示例后,请使用 artemis stop 命令来停止代理。

$ <broker-instance-dir>/bin/artemis stop
Copy to Clipboard Toggle word wrap

更新于 2023-09-26

法律通告

Copyright © 2023 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.
返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat