通过 .NET 开始使用 Azure 队列存储

Tip

查看 Azure 存储代码示例存储库

若要获取能够下载和运行且易于使用的端到端 Azure 存储代码,请查看我们提供的 Azure 存储示例列表。

概述

Azure 队列存储用于在应用程序组件之间进行云消息传送。 在设计应用程序以实现可伸缩性时,通常要将各个应用程序组件分离,使其可以独立地进行伸缩。 队列存储提供的异步消息传送适用于在应用程序组件之间进行通信,无论这些应用程序组件是运行在云中、桌面上、本地服务器上还是移动设备上。 队列存储还支持管理异步任务以及构建过程工作流。

关于本教程

本教程演示如何针对使用 Azure 队列存储一些常见情形编写 .NET 代码。 涉及的方案包括创建和删除队列、添加、读取和删除队列消息。

估计完成时间: 45 分钟

先决条件:

什么是队列存储?

Azure 队列存储是一项可存储大量消息的服务,用户可以通过经验证的呼叫,使用 HTTP 或 HTTPS 从世界任何地方访问这些消息。 一条队列消息的大小最多可为 64 KB,一个队列中可以包含数百万条消息,直至达到存储帐户的总容量限值。

队列存储的常见用途包括:

  • 创建积压工作以进行异步处理
  • 将消息从 Azure Web 角色传递到 Azure 辅助角色

队列服务概念

队列服务包含以下组件:

队列 1

  • URL 格式: 可使用以下 URL 格式对队列进行寻址:
    http://<storage account>.queue.core.chinacloudapi.cn/<queue>

    可使用以下 URL 访问示意图中的某个队列:

    http://myaccount.queue.core.chinacloudapi.cn/images-to-download

  • 存储帐户: 对 Azure 存储进行的所有访问都要通过存储帐户完成。 有关存储帐户容量的详细信息,请参阅 Azure 存储可伸缩性和性能目标

  • 队列: 一个队列包含一组消息。 所有消息必须位于相应的队列中。 请注意,队列名称必须全部小写。 有关命名队列的详细信息,请参阅 命名队列和元数据
  • 消息: 一条消息(不管采用何种格式)的最大大小为 64 KB。 消息可以保留在队列中的最长时间为 7 天。

创建 Azure 存储帐户

创建第一个 Azure 存储帐户的最简单方法是使用 Azure 门户。 若要了解更多信息,请参阅 创建存储帐户

还可使用 Azure PowerShellAzure CLI适用于 .NET 的 Azure 存储资源提供程序创建 Azure 存储帐户。

如果暂时不想在 Azure 中创建存储帐户,也可以使用 Azure 存储模拟器在本地环境中运行和测试代码。 有关详细信息,请参阅 使用 Azure 存储模拟器进行开发和测试

设置开发环境

接下来在 Visual Studio 中设置开发环境,即可试用本指南中的代码示例。

创建 Windows 控制台应用程序项目

在 Visual Studio 中创建新的 Windows 控制台应用程序。 以下步骤演示了如何在 Visual Studio 2017 中创建控制台应用程序。 在其他版本的 Visual Studio 中,这些步骤是类似的。

  1. 选择“文件” > “新建” > “项目”。
  2. 选择“已安装” > “模板” > “Visual C#” > “Windows 经典桌面”。
  3. 选择“控制台应用(.NET Framework)”。
  4. 在“名称”字段中输入应用程序的名称。
  5. 选择“确定” 。

Visual Studio 中“新建项目”对话框的屏幕截图

本教程中的所有代码示例都可以添加到控制台应用程序的 Program.cs 文件的 Main() 方法。

可以在任意类型的 .NET 应用程序(包括 Azure 云服务或 Web 应用,以及桌面和移动应用程序)中使用 Azure 存储客户端库。 为简单起见,我们在本指南中使用控制台应用程序。

使用 NuGet 安装所需包

为完成此教程,需要在项目中引用两个包:

可以使用 NuGet 获取这两个包。 执行以下步骤:

  1. 在“解决方案资源管理器”中,右键单击项目并选择“管理 NuGet 包”。
  2. 在线搜索“WindowsAzure.Storage”,并选择“安装”以安装存储客户端库和依赖项。
  3. 在线搜索“WindowsAzure.ConfigurationManager”,并选择“安装”以安装 Azure Configuration Manager。

Note

用于 .NET 的 Azure SDK中也包含存储客户端库包。 但是我们建议同时从 NuGet 安装存储客户端库,以确保始终使用客户端库的最新版本。

适用于 .NET 的存储客户端库中的 ODataLib 依赖项通过 NuGet(而非 WCF 数据服务)上提供的 ODataLib 包来解析。 ODataLib 库可直接下载或者通过 NuGet 由代码项目引用。 存储空间客户端库使用的具体 ODataLib 包是 ODataEdmSpatial。 尽管这些库由 Azure 表存储类使用,但是用存储空间客户端库进行编程时,它们是必需的依赖项。

确定目标环境

可从两个环境中选择用于运行本指南中示例的环境:

  • 可针对云中的 Azure 存储帐户运行代码。
  • 可针对 Azure 存储模拟器运行代码。 存储模拟器是模拟云中 Azure 存储帐户的本地环境。 应用程序处于开发阶段时,可以选择使用模拟器免费测试和调试代码。 模拟器使用已知帐户和密钥。 有关详细信息,请参阅使用 Azure 存储模拟器进行开发和测试

如果以云中的存储帐户为目标,请从 Azure 门户复制存储帐户的主访问密钥。 有关详细信息,请参阅访问密钥

Note

可以指向存储模拟器以避免引发与 Azure 存储有关的任何费用。 但是,如果您确实选择指向云中的 Azure 存储帐户,则执行此教程的费用会忽略不计。

配置存储连接字符串

用于 .NET 的 Azure 存储客户端库支持使用存储连接字符串来配置终结点和用于访问存储服务的凭据。 维护存储连接字符串的最佳方法在配置文件中。

有关连接字符串的详细信息,请参阅配置 Azure 存储的连接字符串

Note

存储帐户密钥类似于存储帐户的根密码。 始终要小心保护存储帐户密钥。 避免将其分发给其他用户、对其进行硬编码或将其保存在其他人可以访问的纯文本文件中。 如果认为密钥可能已泄漏,请使用 Azure 门户重新生成密钥。

若要配置连接字符串,请从 Visual Studio 中的解决方案资源管理器打开 app.config 文件。 添加 <appSettings> 元素的内容,如下所示。 将 account-name 替换为存储帐户名称,将 account-key 替换为存储帐户密钥:

<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
    </startup>
    <appSettings>
        <add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key;EndpointSuffix=core.chinacloudapi.cn" />
    </appSettings>
</configuration>

例如,配置设置看起来类似于:

<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=storagesample;AccountKey=GMuzNHjlB3S9itqZJHHCnRkrokLkcSyW7yK9BRbGp0ENePunLPwBgpxV1Z/pVo9zpem/2xSHXkMqTHHLcx8XRA==;EndpointSuffix=core.chinacloudapi.cn" />

若要以存储模拟器为目标,可使用映射到已知帐户名称和密钥的快捷方式。 在这种情况下,连接字符串设置如下所示:

<add key="StorageConnectionString" value="UseDevelopmentStorage=true;" />

添加 using 指令

将以下 using 指令添加到 Program.cs 文件顶部:

using Microsoft.Azure; // Namespace for CloudConfigurationManager
using Microsoft.WindowsAzure.Storage; // Namespace for CloudStorageAccount
using Microsoft.WindowsAzure.Storage.Queue; // Namespace for Queue storage types

从 Azure 门户复制凭据

此示例代码需要对存储帐户访问进行授权。 若要授权,请以连接字符串的形式向应用程序提供存储帐户凭据。 若要查看存储帐户凭据,请执行以下操作:

  1. 导航到 Azure 门户
  2. 找到自己的存储帐户。
  3. 在存储帐户概述的“设置”部分,选择“访问密钥”。 此时会显示帐户访问密钥,以及每个密钥的完整连接字符串。
  4. 找到“密钥 1”下面的“连接字符串”值,单击“复制”按钮复制该连接字符串。 下一步需将此连接字符串值添加到某个环境变量。

    显示如何从 Azure 门户复制连接字符串的屏幕截图

解析连接字符串

适用于 .NET 的 Microsoft Configuration Manager 库 提供用于分析配置文件中连接字符串的类。 CloudConfigurationManager 类分析配置设置,而不考虑客户端应用程序是在台式计算机、移动设备、Azure 虚拟机还是 Azure 云服务中运行。

若要引用 CloudConfigurationManager 包,请添加以下 using 指令:

using Microsoft.Azure; //Namespace for CloudConfigurationManager

以下示例演示如何从配置文件中检索连接字符串:

// Parse the connection string and return a reference to the storage account.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

不一定非要使用 Azure 配置管理器。 也可以使用 API,例如 .NET Framework 的 ConfigurationManager 类。

创建队列服务客户端

使用 CloudQueueClient 类可以检索存储在队列存储中的队列。 下面是创建服务客户端的一种方法:

CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

现在,已准备好编写从队列存储读取数据并将数据写入队列存储的代码。

创建队列

此示例演示如何创建队列(如果队列已经不存在):

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a container.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Create the queue if it doesn't already exist
queue.CreateIfNotExists();

在队列中插入消息

要将消息插入现有队列,请先创建一个新的 CloudQueueMessage。 接下来,调用 AddMessage 方法。 可从字符串(UTF-8 格式)或字节数组创建 CloudQueueMessage。 以下代码创建队列(如果该队列不存在)并插入消息“Hello, World”:

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Create the queue if it doesn't already exist.
queue.CreateIfNotExists();

// Create a message and add it to the queue.
CloudQueueMessage message = new CloudQueueMessage("Hello, World");
queue.AddMessage(message);

扫视下一条消息

通过调用 PeekMessage 方法,可以速览队列前面的消息,而不必从队列中将其删除。

// Retrieve storage account from connection string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Peek at the next message
CloudQueueMessage peekedMessage = queue.PeekMessage();

// Display message.
Console.WriteLine(peekedMessage.AsString);

更改已排队消息的内容

可以更改队列中现有消息的内容。 如果消息表示工作任务,可使用此功能来更新该工作任务的状态。 以下代码使用新内容更新队列消息,并将可见性超时设置为再延长 60 秒。 这会保存与消息关联的工作的状态,并额外为客户端提供一分钟的时间来继续处理消息。 可使用此方法跟踪队列消息上的多步骤工作流,即使处理步骤因硬件或软件故障而失败,也无需从头开始操作。 通常同时保留重试计数,当消息重试次数超过 n 时再删除该消息。 这可避免每次处理某条消息时都触发应用程序错误。

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Get the message from the queue and update the message contents.
CloudQueueMessage message = queue.GetMessage();
message.SetMessageContent("Updated contents.");
queue.UpdateMessage(message,
    TimeSpan.FromSeconds(60.0),  // Make it invisible for another 60 seconds.
    MessageUpdateFields.Content | MessageUpdateFields.Visibility);

取消对下一条消息的排队

代码通过两个步骤来取消对队列中某条消息的排队。 调用 GetMessage时,你会获取队列中的下一条消息。 从 GetMessage 返回的消息变得对从此队列读取消息的任何其他代码不可见。 默认情况下,此消息持续 30 秒不可见。 要从队列中删除消息,还必须调用 DeleteMessage。 此删除消息的两步过程可确保,如果代码因硬件或软件故障而无法处理消息,则代码的其他实例可以获取相同消息并重试。 代码在处理消息后会立即调用 DeleteMessage

// Retrieve storage account from connection string
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Get the next message
CloudQueueMessage retrievedMessage = queue.GetMessage();

//Process the message in less than 30 seconds, and then delete the message
queue.DeleteMessage(retrievedMessage);

将 Async-Await 模式与公用队列存储 API 配合使用

此示例演示如何将 Async-Await 模式和公用队列存储 API 配合使用。 示例调用每个给定方法的异步版本,如每个方法的 Async 后缀所示。 使用异步方法时,async-await 模式暂停本地执行,直到调用完成。 此行为允许当前的线程执行其他工作,这有助于避免性能瓶颈并提高应用程序的整体响应能力。 有关在 .NET 中使用 Async-Await 模式的详细信息,请参阅 Async 和 Await(C# 和 Visual Basic)

// Create the queue if it doesn't already exist
if(await queue.CreateIfNotExistsAsync())
{
    Console.WriteLine("Queue '{0}' Created", queue.Name);
}
else
{
    Console.WriteLine("Queue '{0}' Exists", queue.Name);
}

// Create a message to put in the queue
CloudQueueMessage cloudQueueMessage = new CloudQueueMessage("My message");

// Async enqueue the message
await queue.AddMessageAsync(cloudQueueMessage);
Console.WriteLine("Message added");

// Async dequeue the message
CloudQueueMessage retrievedMessage = await queue.GetMessageAsync();
Console.WriteLine("Retrieved message with content '{0}'", retrievedMessage.AsString);

// Async delete the message
await queue.DeleteMessageAsync(retrievedMessage);
Console.WriteLine("Deleted message");

使用其他方法取消对消息的排队

可通过两种方式自定义队列中消息的检索。 首先,可获取一批消息(最多 32 条)。 其次,可以设置更长或更短的不可见超时时间,从而允许代码使用更多或更少时间来完全处理每个消息。 以下代码示例使用 GetMessages 方法在一次调用中获取 20 条消息。 然后,它使用 foreach 循环处理每条消息。 它还将每条消息的不可见超时时间设置为 5 分钟。 请注意,5 分钟超时时间对于所有消息都是同时开始的,因此在调用 GetMessages5 分钟后,尚未删除的任何消息都会再次变得可见。

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

foreach (CloudQueueMessage message in queue.GetMessages(20, TimeSpan.FromMinutes(5)))
{
    // Process all messages in less than 5 minutes, deleting each message after processing.
    queue.DeleteMessage(message);
}

获取队列长度

可以获取队列中消息的估计数。 使用 FetchAttributes 方法可请求队列服务检索队列属性,包括消息计数。 ApproximateMessageCount 属性返回 FetchAttributes 方法检索到的最后一个值,而不会调用队列服务。

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Fetch the queue attributes.
queue.FetchAttributes();

// Retrieve the cached approximate message count.
int? cachedMessageCount = queue.ApproximateMessageCount;

// Display number of messages.
Console.WriteLine("Number of messages in queue: " + cachedMessageCount);

删除队列

若要删除队列及其包含的所有消息,请对队列对象调用 Delete 方法。

// Retrieve storage account from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
    CloudConfigurationManager.GetSetting("StorageConnectionString"));

// Create the queue client.
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();

// Retrieve a reference to a queue.
CloudQueue queue = queueClient.GetQueueReference("myqueue");

// Delete the queue.
queue.Delete();

后续步骤

现在,了解了有关队列存储的基础知识,可单击下面的链接来了解更复杂的存储任务。