你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

向 Azure 服务总线主题发送消息,并从该主题的订阅接收消息 (TypeScript)

在本教程中,将完成以下步骤:

  1. 使用 Azure 门户创建服务总线命名空间。
  2. 使用 Azure 门户创建服务总线主题。
  3. 使用 Azure 门户创建该主题的服务总线订阅。
  4. 编写 TypeScript ESM 应用程序,以使用 @azure/service-bus 包来执行以下操作:
    • 将一组消息发送到主题。
    • 从订阅接收这些消息。

注意

本快速入门分步说明将一批消息发送到某个服务总线主题并从该主题的订阅接收这些消息这一简单场景。 可在 GitHub 上的 Azure SDK for JavaScript 存储库中找到预生成的 Azure 服务总线 JavaScript 和 TypeScript 示例。

先决条件

若要通过你自己的 Azure 帐户来使用本快速入门,你需要:

  • 安装 Azure CLI,它提供向开发人员计算机进行无密码身份验证的功能。
  • 在终端或命令提示符处通过 az login 使用 Azure 帐户登录。
  • 将适当的角色添加到资源时,请使用同一帐户。
  • 在同样的终端或命令提示符中运行代码。
  • 记下服务总线命名空间的主题名称和订阅。 需要在代码中使用它。

注意

本教程将演练可以使用 Node.js 复制和运行的示例。 有关如何创建 Node.js 应用程序的说明,请参阅创建 Node.js 应用程序并将其部署到 Azure 网站使用 Windows PowerShell 创建 Node.js 云服务

在 Azure 门户中创建命名空间

若要开始在 Azure 中使用服务总线消息实体,必须先使用在 Azure 中唯一的名称创建一个命名空间。 命名空间提供了用于应用程序中的服务总线资源(队列、主题等)的范围容器。

创建命名空间:

  1. 登录 Azure 门户

  2. 导航到“所有服务”

  3. 在左侧的导航栏中,从类别列表中选择“集成”,将鼠标悬停在“服务总线”上,然后选择“服务总线”磁贴上的 + 按钮。

    图像显示“创建资源”、“集成”以及菜单中的“服务总线”选择。

  4. 在“创建命名空间”页的“基本信息”标记中,执行以下步骤 :

    1. 对于“订阅”,请选择要在其中创建命名空间的 Azure 订阅。

    2. 对于“资源组”,请选择该命名空间驻留到的现有资源组,或创建一个新资源组。

    3. 输入命名空间的名称。 命名空间名称应遵循以下命名约定:

      • 该名称在 Azure 中必须唯一。 系统会立即检查该名称是否可用。
      • 名称长度最少为 6 个字符,最多为 50 个字符。
      • 名称只能包含字母、数字、连字符“-”。
      • 名称必须以字母开头,并以字母或数字结尾。
      • 名称不以“-sb”或“-mgmt”结尾。
    4. 对于“位置”,请选择托管该命名空间的区域。

    5. 对于“定价层”,请选择命名空间的定价层(“基本”、“标准”或“高级”)。 对于本快速入门,请选择“标准”。

      重要

      若要使用主题和订阅,请选择“标准”或“高级”。 基本定价层不支持主题/订阅。

      如果选择了“高级”定价层,请指定“消息传送单元”数 。 高级层在 CPU 和内存级别提供资源隔离,使每个工作负荷在隔离的环境中运行。 此资源容器称为消息传送单元。 高级命名空间至少具有一个消息传送单元。 可为每个服务总线高级命名空间选择 1、2、4、8 或 16 个消息传送单元。 有关详细信息,请参阅服务总线高级消息传送

    6. 在页面底部选择“查看 + 创建”。

      图像显示“创建命名空间”页

    7. 在“查看 + 创建”页上,查看设置,然后选择“创建” 。

  5. 资源部署成功后,在部署页上选择“转到资源”。

    图像显示“部署成功”页,其中包括“转到资源”链接。

  6. 将会看到服务总线命名空间的主页。

    图像显示已创建的服务总线命名空间的主页。

使用 Azure 门户创建主题

  1. 在“服务总线命名空间”页面上,选择左侧菜单中的“主题”

  2. 在工具栏中选择“+ 主题”。

  3. 输入主题名称。 将其他选项保留默认值。

  4. 选择“创建”。

    图像显示了“创建主题”页。

创建主题的订阅

  1. 选择在上一部分创建的主题

    图像显示主题列表中选择的主题。

  2. 在“服务总线主题”页面上,选择工具栏上的“+ 订阅” 。

    图像显示“添加订阅”按钮。

  3. 在“创建订阅”页上执行以下步骤:

    1. 对于订阅名称,输入“S1” 。

    2. 对于“最大交付数”,输入“3” 。

    3. 然后,选择“创建”以创建订阅。

      图像显示了“创建订阅”页。

向 Azure 验证应用

本快速入门介绍了连接到 Azure 服务总线的两种方法:无密码连接字符串方法

第一个选项展示如何使用 Microsoft Entra ID 中的安全主体和基于角色的访问控制 (RBAC) 连接到服务总线命名空间。 无需担心代码、配置文件或安全存储(如 Azure Key Vault)中存在硬编码的连接字符串。

第二个选项展示如何使用连接字符串连接到服务总线命名空间。 如果不熟悉 Azure,你可能会感觉连接字符串选项更易于使用。 建议在实际应用程序和生产环境中使用无密码选项。 有关详细信息,请参阅身份验证和授权。 还可以在概述页上阅读有关无密码身份验证的详细信息。

将角色分配到 Microsoft Entra 用户

在本地开发时,请确保连接到 Azure 服务总线的用户帐户具有正确的权限。 你需要拥有 Azure 服务总线数据所有者角色才能发送和接收消息。 若要为自己分配此角色,需要具有“用户访问管理员”角色,或者具有包含 Microsoft.Authorization/roleAssignments/write 操作的其他角色。 可使用 Azure 门户、Azure CLI 或 Azure PowerShell 向用户分配 Azure RBAC 角色。 可在范围概述页上详细了解角色分配的可用范围。

以下示例将 Azure Service Bus Data Owner 角色分配给用户帐户,该角色提供对 Azure 服务总线资源的完全访问权限。 在实际方案中,遵循最小特权原则,仅向用户提供更安全的生产环境所需的最小权限。

适用于 Azure 服务总线的 Azure 内置角色

对于 Azure 服务总线,通过 Azure 门户和 Azure 资源管理 API 对命名空间和所有相关资源的管理已使用 Azure RBAC 模型进行了保护。 Azure 提供以下 Azure 内置角色,用于授予对服务总线命名空间的访问权限:

如果要创建自定义角色,请参阅执行服务总线操作所需的权限

将 Microsoft Entra 用户添加到“Azure 服务总线所有者”角色

将 Microsoft Entra 用户名添加到服务总线命名空间级别的 Azure 服务总线数据所有者角色。 它将允许在用户帐户上下文中运行的应用将消息发送到队列或主题,并从队列或主题的订阅中接收消息。

重要

在大多数情况下,角色分配在 Azure 中传播需要一两分钟。 在极少数情况下,最多可能需要 8 分钟。 如果在首次运行代码时收到身份验证错误,请稍等片刻再试。

  1. 如果未在 Azure 门户中打开“服务总线命名空间”页,请使用主搜索栏或左侧导航找到你的服务总线命名空间。

  2. 在概述页面上,从左侧菜单中选择“访问控制(IAM)”。

  3. 在“访问控制 (IAM)”页上,选择“角色分配”选项卡。

  4. 从顶部菜单中选择“+ 添加”,然后从出现的下拉菜单中选择“添加角色分配”。

    显示如何分配角色的屏幕截图。

  5. 使用搜索框将结果筛选为所需角色。 对于此示例,请搜索 Azure Service Bus Data Owner 并选择匹配的结果。 然后选择“下一步” 。

  6. 在“访问权限分配对象”下,选择“用户、组或服务主体”,然后选择“+ 选择成员”。

  7. 在对话框中,搜索 Microsoft Entra ID 用户名(通常是 user@domain 电子邮件地址),然后选中对话框底部的“选择”。

  8. 选择“查看 + 分配”转到最后一页,然后再次选择“查看 + 分配”完成该过程。

配置项目

  1. 创建项目文件夹以包含快速入门文件。

  2. 在项目文件夹中的终端中,初始化 Node.js 项目。

    npm init -y
    
  3. 打开 package.json 项目文件夹中的文件,并添加属性以配置 ESM。 在 version 属性后面添加此属性:

    "type":"module",
    
  4. package.json 文件中,编辑 scripts 属性以编译 TypeScript 文件。 添加 build 脚本。

    "scripts": {
        "build": "tsc"
    }
    
  5. 在项目文件中创建 tsconfig.json 以配置 TypeScript ESM 内部版本并将以下内容复制到文件中:

    {
      "compilerOptions": {
        "module": "NodeNext",
        "target": "ESNext",
        "moduleResolution": "NodeNext",
    
        "outDir": "dist",
        "rootDir": "./src",
    
        "esModuleInterop": true,
        "skipLibCheck": true
      },
      "include": ["src/**/*"],
      "exclude": ["node_modules", "dist"]
    }
    
  6. 在项目中创建 src 文件夹。 此位置用于放置在此快速入门中创建的 TypeScript 文件。

使用 Node 包管理器 (npm) 安装该包

  1. 若要安装服务总线所需的 npm 包,请打开路径中包含 npm 的命令提示符,将目录更改为要包含示例的文件夹,然后运行此命令。

  2. 安装以下包:

    npm install @azure/service-bus @azure/identity
    

将消息发送到主题

下面的示例代码演示如何将一批消息发送到服务总线主题。 请参阅代码注释以了解详细信息。

必须已使用 Azure CLI 的 az login 进行登录,以便本地计算机提供此代码中所需的无密码身份验证。

  1. 打开你喜好的编辑器,例如 Visual Studio Code

  2. src 文件夹中,创建名为 sendtotopic.ts 的文件,然后将以下代码粘贴到该文件中。 此代码向主题发送一条消息。

    重要

    无密码凭据随 DefaultAzureCredential 一起提供。

    import {
        ServiceBusClient,
        ServiceBusSender,
        ServiceBusMessageBatch,
      } from "@azure/service-bus";
    import { DefaultAzureCredential } from "@azure/identity";
    
    // Replace `<SERVICE-BUS-NAMESPACE>` with your namespace
    const fullyQualifiedNamespace = "<SERVICE-BUS-NAMESPACE>.servicebus.windows.net";
    
    // Passwordless credential
    const credential = new DefaultAzureCredential();
    
    const topicName = "<TOPIC NAME>";
    
    const messages = [
        { body: "Albert Einstein" },
        { body: "Werner Heisenberg" },
        { body: "Marie Curie" },
        { body: "Steven Hawking" },
        { body: "Isaac Newton" },
        { body: "Niels Bohr" },
        { body: "Michael Faraday" },
        { body: "Galileo Galilei" },
        { body: "Johannes Kepler" },
        { body: "Nikolaus Kopernikus" }
     ];
    
     async function main() {
        // create a Service Bus client using the passwordless authentication to the Service Bus namespace
        const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);
    
        // createSender() can also be used to create a sender for a queue.
        const sender:ServiceBusSender = serviceBusClient.createSender(topicName);
    
        try {
            // Tries to send all messages in a single batch.
            // Will fail if the messages cannot fit in a batch.
            // await sender.sendMessages(messages);
    
            // create a batch object
            let serviceBusMessageBatch:ServiceBusMessageBatch = await sender.createMessageBatch();
            for (let i = 0; i < messages.length; i++) {
                // for each message in the array
    
                // try to add the message to the batch
                if (!serviceBusMessageBatch.tryAddMessage(messages[i])) {
                    // if it fails to add the message to the current batch
                    // send the current batch as it is full
                    await sender.sendMessages(serviceBusMessageBatch);
    
                    // then, create a new batch
                    serviceBusMessageBatch = await sender.createMessageBatch();
    
                    // now, add the message failed to be added to the previous batch to this batch
                    if (!serviceBusMessageBatch.tryAddMessage(messages[i])) {
                        // if it still can't be added to the batch, the message is probably too big to fit in a batch
                        throw new Error("Message too big to fit in a batch");
                    }
                }
            }
    
            // Send the last created batch of messages to the topic
            await sender.sendMessages(serviceBusMessageBatch);
    
            console.log(`Sent a batch of messages to the topic: ${topicName}`);
    
            // Close the sender
            await sender.close();
        } finally {
            await serviceBusClient.close();
        }
    }
    
    // call the main function
    main().catch((err) => {
        console.log("Error occurred: ", err);
        process.exit(1);
     });
    
  3. <SERVICE BUS NAMESPACE CONNECTION STRING> 替换为服务总线命名空间的连接字符串。

  4. <TOPIC NAME> 替换为主题名称。

  5. 然后,在命令提示符中运行该命令以执行此文件。

    npm run build
    node dist/sendtotopic.js
    
  6. 你会看到以下输出。

    Sent a batch of messages to the topic: mytopic
    

从订阅接收消息

必须已使用 Azure CLI 的 az login 进行登录,以便本地计算机提供此代码中所需的无密码身份验证。

  1. 打开你喜好的编辑器,例如 Visual Studio Code

  2. src 文件夹中,创建名为 receivefromsubscription.ts 的文件,然后将以下代码粘贴到该文件中。 请参阅代码注释以了解详细信息。

    import {
        delay,
        ProcessErrorArgs,
        ServiceBusClient,
        ServiceBusMessage,
        ServiceBusReceiver,
      } from "@azure/service-bus";
    import { DefaultAzureCredential } from "@azure/identity";
    
    // Replace `<SERVICE-BUS-NAMESPACE>` with your namespace
    const fullyQualifiedNamespace = "<SERVICE-BUS-NAMESPACE>.servicebus.windows.net";
    
    // Passwordless credential
    const credential = new DefaultAzureCredential();
    
    const topicName = "<TOPIC NAME>";
    const subscriptionName = "<SUBSCRIPTION NAME>";
    
     async function main() {
        // create a Service Bus client using the passwordless authentication to the Service Bus namespace
        const serviceBusClient = new ServiceBusClient(fullyQualifiedNamespace, credential);
    
        // createReceiver() can also be used to create a receiver for a queue.
        const serviceBusReceiver: ServiceBusReceiver = serviceBusClient.createReceiver(topicName, subscriptionName);
    
      // function to handle messages
      const myMessageHandler = async (
        messageReceived: ServiceBusMessage,
      ): Promise<void> => {
        console.log(`Received message: ${messageReceived.body}`);
      };
    
      // function to handle any errors
      const myErrorHandler = async (error: ProcessErrorArgs): Promise<void> => {
        console.log(error);
      };
    
      // subscribe and specify the message and error handlers
      serviceBusReceiver.subscribe({
        processMessage: myMessageHandler,
        processError: myErrorHandler,
      });
    
        // Waiting long enough before closing the sender to send messages
        await delay(5000);
    
        await serviceBusReceiver.close();
        await serviceBusReceiver.close();
    }
    
    // call the main function
    main().catch((err) => {
        console.log("Error occurred: ", err);
        process.exit(1);
     });
    
  3. <SERVICE BUS NAMESPACE CONNECTION STRING> 替换为命名空间的连接字符串。

  4. <TOPIC NAME> 替换为主题名称。

  5. <SUBSCRIPTION NAME> 替换为主题的订阅名称。

  6. 然后,在命令提示符中运行该命令以执行此文件。

    npm run build
    node dist/receivefromsubscription.js
    

你会看到以下输出。

Received message: Albert Einstein
Received message: Werner Heisenberg
Received message: Marie Curie
Received message: Stephen Hawking
Received message: Isaac Newton
Received message: Niels Bohr
Received message: Michael Faraday
Received message: Galileo Galilei
Received message: Johannes Kepler
Received message: Nikolaus Kopernikus

在 Azure 门户中,导航到你的服务总线命名空间,切换到底部窗格中的“主题”,然后选择你的主题,以查看你的主题的“服务总线主题”页面。 在此页上,“消息”图表中应显示 10 条传入消息和 10 条传出消息。

Azure 门户的屏幕截图,其中显示了传入和传出消息。

如果你下次只运行发送应用,那么,在“服务总线主题”页上,除 10 条传出消息外,你还会看到 20 条传入消息(10 条新消息)。

Azure 门户的屏幕截图,其中显示了更新的主题页。

在此页上,如果在底部窗格中选择订阅,则会转到“服务总线订阅”页。 可以在此页上查看活动消息计数、死信消息计数等。 在此示例中,还有 10 条活动消息未被接收器接收。

Azure 门户的屏幕截图,其中显示了活动消息计数。

故障排除

如果在运行有关所需声明的无密码版本 TypeScript 代码时收到错误,请确保通过 Azure CLI 命令 az login 登录,并将相应的角色应用到 Azure 用户帐户。

清理资源

导航到 Azure 门户中的服务总线命名空间,然后在 Azure 门户上选择“删除”以删除命名空间以及其中的主题。

后续步骤

请参阅以下文档和示例: