使用 Azure 服务总线队列创建 .NET 多层应用程序

使用 Visual Studio 和免费的 Azure SDK for .NET,可以轻松针对 Azure 进行开发。 本教程指导完成创建使用在本地环境中运行的多个 Azure 资源的应用程序的步骤。

你将了解以下内容:

  • 如何通过单个下载和安装来使计算机能够进行 Azure 开发。
  • 如何使用 Visual Studio 针对 Azure 进行开发。
  • 如何使用 Web 角色和辅助角色在 Azure 中创建多层应用程序。
  • 如何使用服务总线队列在各层之间进行通信。

注意

若要完成本教程,需要一个 Azure 帐户。 可以注册免费试用版

在本教程中,你将生成多层应用程序并在 Azure 云服务中运行它。 前端为 ASP.NET MVC Web 角色,后端为使用服务总线队列的辅助角色。 可以创建与前端相同的多层应用程序,作为将部署到 Azure 网站而不是云服务的 Web 项目。 还可以试用 .NET 本地/云混合应用程序教程。

以下屏幕截图显示已完成的应用程序。

Application's Submit page

方案概述:角色间通信

若要提交处理命令,以 Web 角色运行的前端 UI 组件必须与以辅助角色运行的中间层逻辑进行交互。 此示例使用服务总线消息传送在各层之间进行通信。

在 Web 层和中间层之间使用服务总线消息传送将分离这两个组件。 与直接消息传送(即 TCP 或 HTTP)不同,Web 层不会直接连接到中间层,而是将工作单元作为消息推送到服务总线,服务总线以可靠方式保留这些工作单元,直到中间层准备好使用和处理它们。

服务总线提供了两个实体以支持中转消息传送:队列和主题。 通过队列,发送到队列的每个消息均由一个接收方使用。 主题支持发布/订阅模式,在该模式中,每个已发布消息都将提供给在该主题中注册的订阅。 每个订阅都以逻辑方式保留自己的消息队列。 此外,还可以使用筛选规则配置订阅,这些规则可将传递给订阅队列的消息集限制为符合筛选条件的消息集。 以下示例使用服务总线队列。

Diagram showing the communication between the Web Role, Service Bus, and the Worker Role.

与直接消息传送相比,此通信机制具有多项优势:

  • 暂时分离。 使用异步消息传送模式时,创建方和使用方不需要在同一时间联机。 服务总线可靠地存储消息,直到使用方准备好接收它们。 这会允许分布式应用程序的组件断开连接,例如,为进行维护而自动断开,或因组件故障断开连接,而不会影响系统的整体性能。 此外,使用方应用程序可能只需在一天的特定时段内联机。

  • 负载调节。 在许多应用程序中,系统负载随时间而变化,而每个工作单元所需的处理时间通常为常量。 使用队列在消息创建者与使用者之间中继意味着,只需将使用方应用程序(辅助)预配为适应平均负载而非最大负载。 队列深度将随传入负载的变化而加大和减小。 这会直接根据为应用程序加载提供服务所需的基础结构的数目来节省成本。

  • 负载均衡。 随着负载增加,可添加更多的工作进程以从队列中读取。 每条消息仅由一个辅助进程处理。 另外,可通过此基于拉取的负载均衡来以最合理的方式使用辅助计算机,即使这些辅助计算机具有不同的处理能力(因为它们以其最大速率拉取消息)也是如此。 此模式通常称为 使用者竞争 模式。

    Diagram showing the communication between the Web Role, the Service Bus, and two Worker Roles.

以下各节讨论了实现此体系结构的代码。

先决条件

在本教程中,我们将使用 Microsoft Entra 身份验证来创建 ServiceBusClientServiceBusAdministrationClient 对象。 你还将使用 DefaultAzureCredential,要使用的话,需要执行以下步骤以在开发环境中本地测试应用程序。

  1. 在 Microsoft Entra ID 中注册应用程序
  2. 将应用程序添加到 Service Bus Data Owner 角色
  3. 设置 AZURE-CLIENT-IDAZURE-TENANT-IDAZURE-CLIENT-SECRET 环境变量。 请参阅这篇文章,了解相关说明。

有关服务总线内置角色的列表,请参阅 服务总线的 Azure 内置角色

创建命名空间

第一步是创建命名空间 并获取该命名空间的共享访问签名 (SAS) 密钥。 命名空间为每个通过服务总线公开的应用程序提供应用程序边界。 创建命名空间后,系统将生成一个 SAS 密钥。 命名空间名称与 SAS 密钥的组合为服务总线提供了用于验证应用程序访问权限的凭据。

在 Azure 门户中创建命名空间

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

创建命名空间:

  1. 登录 Azure 门户

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

    Image showing selection of Create a resource, Integration, and then Service Bus in the menu.

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

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

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

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

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

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

      重要

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

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

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

      Image showing the Create a namespace page

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

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

    Image showing the deployment succeeded page with the Go to resource link.

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

    Image showing the home page of the Service Bus namespace created.

获取到命名空间的连接字符串(Azure 门户)

创建新的命名空间会自动生成一个初始共享访问签名 (SAS) 策略,该策略包含主密钥和辅助密钥以及主要连接字符串和辅助连接字符串,每个连接字符串都授予对命名空间所有方面的完全控制权。 请参阅服务总线身份验证和授权,了解如何创建规则来对普通发送者和接收者的权限进行更多限制。

客户端可以使用连接字符串连接到服务总线命名空间。 若要复制命名空间的主要连接字符串,请执行以下步骤:

  1. 在“服务总线命名空间”页中的左侧菜单上,选择“共享访问策略” 。

  2. 在“共享访问策略”页,选择“RootManageSharedAccessKey” 。

  3. 在“策略: RootManageSharedAccessKey”窗口中,选择“主连接字符串”旁边的“复制”按钮,将连接字符串复制到剪贴板供以后使用。 将此值粘贴到记事本或其他某个临时位置。

    Screenshot shows an S A S policy called RootManageSharedAccessKey, which includes keys and connection strings.

    可使用此页面复制主密钥、辅助密钥、主连接字符串和辅助连接字符串。

创建 Web 角色

在本部分中,会生成应用程序的前端。 首先,将创建应用程序显示的各种页面。 之后,将添加代码,以便将项目提交到服务总线队列并显示有关队列的状态信息。

创建项目

  1. 使用管理员特权启动 Visual Studio:右键单击“Visual Studio”程序图标,并选择“以管理员身份运行”。 Azure 计算模拟器(本文后面会讨论)要求使用管理员权限启动 Visual Studio。

    在 Visual Studio 中的“文件”菜单上,选择“新建”,然后选择“项目”。

  2. 在“模板”页上执行以下步骤:

    1. 选择“C#”作为编程语言。

    2. 选择“云”作为项目类型。

    3. 选择“Azure 云服务”。

    4. 选择下一步

      Screenshot of the New Project dialog box with Cloud selected and Azure Cloud Service Visual C# highlighted and outlined in red.

  3. 将项目命名为 MultiTierApp,选择项目的位置,然后选择“创建”。

    Specify project name.

  4. 在“角色”页上,双击“ASP.NET Web 角色”,然后选择“确定”。

    Select Web Role

  5. 将鼠标指针停留在“Azure 云服务解决方案”下的“WebRole1”上,选择铅笔图标,并将 Web 角色重命名为“FrontendWebRole”。 然后选择“确定” 。 (请确保输入“Frontend”而不是“FrontEnd”,此处为小写“e”。)

    Screenshot of the New Azure Cloud Service dialog box with the solution renamed to FrontendWebRole.

  6. 在“新建 ASP.NET Web 应用程序”对话框中,选择“MVC”,然后选择“创建”。

    Screenshot of the New ASP.NET Project dialog box with MVC highlighted and outlined in red and the Change Authentication option outlined in red.

  7. 在“解决方案资源管理器”的“FrontendWebRole”项目中,右键单击“引用”,并选择“管理 NuGet 包”。

  8. 选择“浏览”选项卡,然后搜索 Azure.Messaging.ServiceBus。 搜索 Azure.Messaging.ServiceBus 包,选择“安装”,并接受使用条款。

    Screenshot of the Manage NuGet Packages dialog box with the Azure.Messaging.ServiceBus highlighted and the Install option outlined in red.

    请注意,现已引用所需的客户端程序集并已添加部分新代码文件。

  9. 按照相同的步骤将 Azure.Identity NuGet 包添加到项目。

  10. 在“解决方案资源管理器”中,展开“FronendWebRole”,右键单击“模型”,选择“添加”,然后选择“类”。 在“名称” 框中,键入名称“OnlineOrder.cs” 。 然后选择“添加” 。

为 Web 角色编写代码

在本部分,将创建应用程序显示的各种页面。

  1. 在 Visual Studio 的 OnlineOrder.cs 文件中将现有命名空间定义替换为以下代码:

    namespace FrontendWebRole.Models
    {
        public class OnlineOrder
        {
            public string Customer { get; set; }
            public string Product { get; set; }
        }
    }
    
  2. 在“解决方案资源管理器” 中,双击“Controllers\HomeController.cs” 。 在文件顶部添加以下 using 语句以包括针对你刚创建的模型以及服务总线的命名空间。

     using FrontendWebRole.Models;
     using Azure.Messaging.ServiceBus;    
    
  3. 仍在 Visual Studio 的 HomeController.cs 文件中,将现有命名空间定义替换为以下代码。 此代码包含用于处理将项提交到队列这一任务的方法。

    namespace FrontendWebRole.Controllers
    {
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                // Simply redirect to Submit, since Submit will serve as the
                // front page of this application.
                return RedirectToAction("Submit");
            }
    
            public ActionResult About()
            {
                return View();
            }
    
            // GET: /Home/Submit.
            // Controller method for a view you will create for the submission
            // form.
            public ActionResult Submit()
            {
                // Will put code for displaying queue message count here.
    
                return View();
            }
    
            // POST: /Home/Submit.
            // Controller method for handling submissions from the submission
            // form.
            [HttpPost]
            // Attribute to help prevent cross-site scripting attacks and
            // cross-site request forgery.  
            [ValidateAntiForgeryToken]
            public ActionResult Submit(OnlineOrder order)
            {
                if (ModelState.IsValid)
                {
                    // Will put code for submitting to queue here.
    
                    return RedirectToAction("Submit");
                }
                else
                {
                    return View(order);
                }
            }
        }
    }
    
  4. 在“生成”菜单中,选择“生成解决方案”以测试工作的准确性。

  5. 现在,为前面创建的 Submit() 方法创建视图。 在 HomeController.cs 文件中的 Submit() 方法(不带任何参数的 Submit() 的重载函数)中右键单击,然后选择“添加视图” 。

  6. 在“添加已搭建基架的新项”对话框中,选择“添加”。

  7. 在“添加视图”对话框中,执行以下步骤:

    1. 在“模板” 列表中,选择“创建” 。

    2. 在“模型类” 列表中,选择“OnlineOrder” 类。

    3. 选择添加

      A screenshot of the Add View dialog box with the Template and Model class drop-down lists outlined in red.

  8. 现在,请更改应用程序的显示名称。 在“解决方案资源管理器”中,双击“Views\Shared\_Layout.cshtml”文件以在 Visual Studio 编辑器中将其打开。

  9. 将每一处 My ASP.NET Application 替换为 Northwind Traders Products

  10. 删除“Home” 、“About” 和“Contact” 链接。 删除突出显示的代码:

    Screenshot of the code with three lines of H T M L Action Link code highlighted.

  11. 最后,修改提交页以包含有关队列的一些信息。 在“解决方案资源管理器” 中,双击“Views\Home\Submit.cshtml” 文件以在 Visual Studio 编辑器中将其打开。 <h2>Submit</h2>后面添加以下行。 ViewBag.MessageCount 当前为空。 稍后将填充它。

    <p>Current number of orders in queue waiting to be processed: @ViewBag.MessageCount</p>
    
  12. 现在,已实现 UI。 可以按 F5 运行应用程序并确认其按预期方式运行。

    Screenshot of the application's Submit page.

编写用于将项提交到 Service Bus 队列的代码

现在,将添加用于将项提交到队列的代码。 首先,将创建一个包含服务总线队列连接信息的类。 然后,将从 Global.aspx.cs 初始化连接。 最后,将更新你之前在 HomeController.cs 中创建的提交代码以便实际将项提交到服务总线队列。

  1. 在“解决方案资源管理器” 中,右键单击“FrontendWebRole” (右键单击项目而不是角色)。 依次选择“添加”、“类”。

  2. 将类命名为 QueueConnector.cs。 选择“添加”,以创建此类。

  3. 现在,将添加可封装连接信息并初始化服务总线队列连接的代码。 将 QueueConnector.cs 的全部内容替换为下面的代码,并输入 your Service Bus namespace(命名空间名称)和 yourKey(之前从 Azure 门户中获取的主要密钥)的值。

     using System;
     using System.Collections.Generic;
     using System.Linq;
     using System.Web;
     using System.Threading.Tasks;
     using Azure.Messaging.ServiceBus;
     using Azure.Messaging.ServiceBus.Administration;
    
    namespace FrontendWebRole
    {
         public static class QueueConnector
         {
             // object to send messages to a Service Bus queue
             internal static ServiceBusSender SBSender;
    
             // object to create a queue and get runtime properties (like message count) of queue
             internal static ServiceBusAdministrationClient SBAdminClient;
    
             // Fully qualified Service Bus namespace
             private const string FullyQualifiedNamespace = "<SERVICE BUS NAMESPACE NAME>.servicebus.chinacloudapi.cn";
    
             // The name of your queue.
             internal const string QueueName = "OrdersQueue";
    
             public static async Task Initialize()
             {
                 // Create a Service Bus client that you can use to send or receive messages
                 ServiceBusClient SBClient = new ServiceBusClient(FullyQualifiedNamespace, new DefaultAzureCredential());
    
                 // Create a Service Bus admin client to create queue if it doesn't exist or to get message count
                 SBAdminClient = new ServiceBusAdministrationClient(FullyQualifiedNamespace, new DefaultAzureCredential());
    
                 // create the OrdersQueue if it doesn't exist already
                 if (!(await SBAdminClient.QueueExistsAsync(QueueName)))
                 {
                     await SBAdminClient.CreateQueueAsync(QueueName);
                 }
    
                 // create a sender for the queue 
                 SBSender = SBClient.CreateSender(QueueName);    
             }
         }    
    }
    
  4. 现在,请确保 Initialize 方法会被调用。 在“解决方案资源管理器” 中,双击“Global.asax\Global.asax.cs” 。

  5. Application_Start 方法的末尾添加以下代码行。

     FrontendWebRole.QueueConnector.Initialize().Wait();
    
  6. 最后,更新之前创建的 Web 代码以便将项提交到队列。 在“解决方案资源管理器” 中,双击“Controllers\HomeController.cs” 。

  7. 更新 Submit() 方法(不包含任何参数的重载),如下所示,获取队列的消息计数。

         public ActionResult Submit()
         {
             QueueRuntimeProperties properties = QueueConnector.adminClient.GetQueueRuntimePropertiesAsync(QueueConnector.queueName).Result;
             ViewBag.MessageCount = properties.ActiveMessageCount;
    
             return View();
         }
    
  8. 更新 Submit(OnlineOrder order) 方法(包含一个参数的重载),如下所示,将订单信息提交到队列。

         public ActionResult Submit(OnlineOrder order)
         {
             if (ModelState.IsValid)
             {
                 // create a message 
                 var message = new ServiceBusMessage(new BinaryData(order));
    
                 // send the message to the queue
                 QueueConnector.sbSender.SendMessageAsync(message);
    
                 return RedirectToAction("Submit");
             }
             else
             {
                 return View(order);
             }
         }
    
  9. 现在,可以重新运行应用程序。 每提交订单时,消息计数都会增大。

    Screenshot of the application's Submit page with the message count incremented to 1.

创建辅助角色

现在,将创建用于处理订单提交的辅助角色。 此示例使用“服务总线队列的辅助角色” Visual Studio 项目模板。 已从门户中获取所需的凭据。

  1. 确保已将 Visual Studio 连接到 Azure 帐户。

  2. 在 Visual Studio 的“解决方案资源管理器”中,右键单击“MultiTierApp”项目下的“角色”文件夹。

  3. 选择“添加”,然后选择“新建辅助角色项目”。 此时会显示“添加新角色项目” 对话框。

    Screenshot of the Solution Explorer pane with the New Worker Role Project option and Add option highlighted.

  4. 在“添加新角色项目”对话框中,选择“辅助角色”。 请勿选择“具有服务总线队列的辅助角色”,因为此选项会生成使用旧版服务总线 SDK 的代码。

    Screenshot of the Ad New Role Project dialog box with the Worker Role with Service Bus Queue option highlighted and outlined in red.

  5. 在“名称” 框中,将项目命名为“OrderProcessingRole” 。 然后选择“添加” 。

  6. 在“解决方案资源管理器”中,右键单击 OrderProcessingRole 项目,然后选择“管理 NuGet 包”。

  7. 选择“浏览”选项卡,然后搜索 Azure.Messaging.ServiceBus。 搜索 Azure.Messaging.ServiceBus 包,选择“安装”,并接受使用条款。

    Screenshot of the Manage NuGet Packages dialog box with the Azure.Messaging.ServiceBus highlighted and the Install option outlined in red.

  8. 按照相同的步骤将 Azure.Identity NuGet 包添加到项目。

  9. 从队列中处理订单时,创建一个 OnlineOrder 类来表示这些订单。 可以重用已创建的类。 在“解决方案资源管理器” 中,右键单击“OrderProcessingRole” 类(右键单击类图标,而不是角色)。 选择“添加”,然后选择“现有项”。

  10. 浏览到 FrontendWebRole\Models 的子文件夹,然后双击“OnlineOrder.cs” 以将其添加到此项目中。

  11. 将以下 using 语句添加到 OrderProcessingRole 项目中的 WorkerRole.cs 文件。

    using FrontendWebRole.Models;
    using Azure.Messaging.ServiceBus;
    using Azure.Messaging.ServiceBus.Administration; 
    
  12. 在 WorkerRole.cs 中,添加以下属性。

    重要

    对于命名空间,请使用在满足先决条件的过程中记下的连接字符串。

        // Fully qualified Service Bus namespace
        private const string FullyQualifiedNamespace = "<SERVICE BUS NAMESPACE NAME>.servicebus.chinacloudapi.cn";
    
        // The name of your queue.
        private const string QueueName = "OrdersQueue";
    
        // Service Bus Receiver object to receive messages message the specific queue
        private ServiceBusReceiver SBReceiver;
    
    
  13. 更新 OnStart 方法以创建 ServiceBusClient 对象,然后更新 ServiceBusReceiver 对象以接收来自 OrdersQueue 的消息。

        public override bool OnStart()
        {
            // Create a Service Bus client that you can use to send or receive messages
            ServiceBusClient SBClient = new ServiceBusClient(FullyQualifiedNamespace, new DefaultAzureCredential());
    
            CreateQueue(QueueName).Wait();
    
            // create a receiver that we can use to receive the message
            SBReceiver = SBClient.CreateReceiver(QueueName);
    
            return base.OnStart();
        }
        private async Task CreateQueue(string queueName)
        {
            // Create a Service Bus admin client to create queue if it doesn't exist or to get message count
            ServiceBusAdministrationClient SBAdminClient = new ServiceBusAdministrationClient(FullyQualifiedNamespace, new DefaultAzureCredential());
    
            // create the OrdersQueue if it doesn't exist already
            if (!(await SBAdminClient.QueueExistsAsync(queueName)))
            {
                await SBAdminClient.CreateQueueAsync(queueName);
            }
        }
    
  14. 更新 RunAsync 方法以包含用于接收消息的代码。

        private async Task RunAsync(CancellationToken cancellationToken)
        {
            // TODO: Replace the following with your own logic.
            while (!cancellationToken.IsCancellationRequested)
            {
                // receive message from the queue
                ServiceBusReceivedMessage receivedMessage = await SBReceiver.ReceiveMessageAsync();
    
                if (receivedMessage != null)
                {
                    Trace.WriteLine("Processing", receivedMessage.SequenceNumber.ToString());
    
                    // view the message as an OnlineOrder
                    OnlineOrder order = receivedMessage.Body.ToObjectFromJson<OnlineOrder>();
                    Trace.WriteLine(order.Customer + ": " + order.Product, "ProcessingMessage");
    
                    // complete message so that it's removed from the queue
                    await SBReceiver.CompleteMessageAsync(receivedMessage);
                }
            }
        }
    
  15. 已完成应用程序。 可以测试整个应用程序,方法是右键单击“解决方案资源管理器”中的 MultiTierApp 项目,选择“设置为启动项目” ,然后按 F5。 消息计数不会递增,因为辅助角色会处理队列中的项并将其标记为完成。 可以通过查看 Azure 计算模拟器 UI 来查看辅助角色的跟踪输出。 可通过右击任务栏的通知区域中的模拟器图标并选择“显示计算模拟器 UI” 来执行此操作。

    Screenshot of what appears when you select the emulator icon. Show Compute Emulator UI is in the list of options.

    Screenshot of the Azure Compute Emulator (Express) dialog box.

后续步骤

若要了解有关 Service Bus 的详细信息,请参阅以下资源: