从 Azure 存储获取数据
如何从 Azure 存储获取聚合数据。
引言
我正在进行一个 POC,并尝试弄清楚如何通过 REST 调用从 Azure 存储获取聚合数据。我的经理让我使用 LINQ 获取聚合数据。我感到震惊。我怎么能应用 LINQ 查询来从 Azure 存储获取数据?REST 通过 Web 服务提供响应。我不想卷入无聊的争论,所以我决定着手研究。经过大量研究,我发现可以通过在表上使用过滤器来优化从 Azure 存储的数据访问。
让我们从头开始。
解决方案
第 1 步: 我创建了一个简单的基于 Windows 窗体的应用程序用于 UI。它看起来如下
我将窗体分为 3 个部分:数据输入、验证到 Azure 和结果。
第 2 步: 提交按钮不仅会通过 REST 验证 URI 和时间,还会解析 REST 响应中的结果。
但在深入研究代码之前,让我们看看我到底在做什么。
我在 Azure 存储中有一个示例表,在我的测试表中,有两个属性:Key 和 Value。假设我们有 key = "CPU" 和 value = 10,key = "Memory" 和 value = 90,依此类推(相似的 key)...当我通过 REST 调用时,它以 XML 形式响应 CPU 和 Memory 的所有数据,但我想在通过 REST 请求时从 Azure 存储获取聚合数据。我不想先将所有数据获取到本地,然后再执行聚合。我想要响应,例如 Key = "CPU" 和 Value = 1450(对应于 CPU 的所有值的聚合总和)。(这就是我必须做的,最终我发现目前这不可能,因为 Azure 不允许我们通过 REST 操作服务器数据。所以,我将这个概念整合为一项伟大的 Windows Azure 想法。)
唯一的解决方案是通过过滤表来减少大型表响应的开销。
第 3 步: 现在,我们如何通过 REST 调用 Azure 存储?有关 REST 的更多信息,请访问此链接。
我在这里不描述 REST 架构。所有存储服务都可以通过 REST API 访问。可以在 Windows Azure 中运行的服务内访问存储服务,或者可以直接通过 Internet 从任何能够发送 HTTP/HTTPS 请求并接收 HTTP/HTTPS 响应的应用程序进行访问。您可以通过 REST 访问三个服务(Blob 服务、队列服务和表服务)。对于我来说,我使用的是表服务。
表服务以表的形式提供结构化存储。表服务支持符合 ADO.NET Data Services REST API 的 REST API。开发人员还可以使用 ADO.NET Data Services 的 .NET 客户端库来访问表服务。在存储帐户内的表服务中,开发人员可以创建命名的表。表将数据存储为实体。实体是命名属性及其值的集合,类似于行。表会进行分区以支持跨存储节点的负载均衡。每个表的第一项属性是分区键,它指定实体所属的分区。第二个属性是行键,它标识给定分区内的实体。分区键和行键的组合形成一个主键,该主键在表中唯一标识每个实体。
第 4 步:理论讨论够了,让我们深入代码。
首先声明输入字段(在我的基于窗体的应用程序中)
private string _Account;
private string _Table;
private string _Secret;
private List<string> result;
private List<string> value;
public string key = string.Empty;
_Account
用于获取用户 Azure 存储帐户的输入。_Table
用于获取已存在于 Azure 存储帐户中的示例表的输入。_Secret
密钥用于身份验证目的。这是一个重要的字段,因为我们将进一步使用此密钥对 REST 调用中的标头进行签名。
第 5 步: 输入由提交按钮验证。
private void btnSubmit_Click(object sender, EventArgs e)
{
_Secret = tbSharedKey.Text;
_Account = tbAccountName.Text;
_Table = tbTblName.Text;
string xml = string.Empty;
ListTables(key); // this function will show entitis of table
}
第 6 步:现在我们准备进入 REST。我们将调用 REST API 以从 Azure 存储获取表数据。
表服务 API 符合 ADO.NET Data Services 提供的 REST API。在表服务中,可以执行不同的 REST API 操作(查询表、创建表、合并实体、删除表、插入实体、更新实体和删除实体)。我在这里使用的是查询实体。有关其他操作的详细信息,您可以使用此链接。
可以按如下方式构造查询实体请求。将 *myaccount* 替换为您的存储帐户名称,将 *Tables* 替换为您用作输入的示例表:*http://myaccount.table.core.windows.net/Tables*。
此 URI 用于创建 GET 格式的请求以及请求标头。因此,我的 URI 将如下所示
uri = @"https://" + _Account + ".table.core.windows.net/" + Resource;
HttpWebRequest request= (HttpWebRequest)HttpWebRequest.Create(uri);
request.Method = "GET";
request.ContentLength = 0;
但是我们需要添加一个标头来调用 REST API。请求标头参数显示在表中
请求标头 |
描述 |
Authorization |
必需。指定身份验证方案、帐户名称和签名。有关更多信息,请参阅身份验证方案。 |
Date 或 *x-ms-date* |
必需。指定请求的协调世界时 (UTC)。有关更多信息,请参阅身份验证方案。 |
x-ms-version |
可选。指定此请求使用的操作版本。有关更多信息,请参阅存储服务版本控制。 |
因此,让我们在标头中添加必需的参数
request.Headers.Add("x-ms-date", DateTime.UtcNow.ToString("R",
System.Globalization.CultureInfo.InvariantCulture));
请注意:“x-ms-date”指定 UTC 格式的日期(必需)。
现在,我将在代码中对标头进行签名以进行身份验证。
// Add the Authorization header to the request
request.Headers.Add("Authorization", authH);
授权代码是
string authH = "SharedKey " + _Account + ":" +
System.Convert.ToBase64String(hasher.ComputeHash(
System.Text.Encoding.UTF8.GetBytes(signature)));
签名采用一个定义明确的格式,请记住这一点。
// Now sign the request
string signature = "GET\n";
// Content-MD5
signature += "\n";
// Content-Type
signature += "\n";
// Date
signature += request.Headers["x-ms-date"] + "\n";
// Canonicalized Resource
// remove the query string
int q = Resource.IndexOf("?");
if (q > 0) Resource = Resource.Substring(0, q);
// Format is /{0}/{1} where 0 is name
// of the account and 1 is resources URI path
signature += "/" + _Account + "/" + Resource;
现在,使用请求从 Azure 存储获取响应。
HttpWebResponse response = (HttpWebResponse)request.GetResponse()
您现在可以将此响应保存到 StreamReader
对象中。REST 默认将以 XML 格式响应,但您也可以获取 ATOM(类似于 XML)格式的数据。
XML 响应正文如下
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://myaccount.tables.core.windows.net/"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
<title type="text">Customers</title>
<id>http://myaccount.tables.core.windows.net/Customers</id>
<updated>2008-10-01T15:26:13Z</updated>
<link rel="self" title="Customers" href="Customers" />
<entry m:etag="W/"datetime'2008-10-01T15%3A26%3A04.6812774Z'"">
<id>http://myaccount.tables.core.windows.net/Customers(
PartitionKey='Customer03',RowKey='')</id>
<title type="text"></title>
<updated>2008-10-01T15:26:13Z</updated>
<author>
<name />
</author>
<link rel="edit" title="Customers"
href="Customers (PartitionKey='Customer03',RowKey='')" />
<category term="myaccount.Customers"
scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:PartitionKey>Customer03</d:PartitionKey>
<d:RowKey></d:RowKey>
<d:Timestamp m:type="Edm.DateTime">2008-10-01T15:26:04.6812774Z</d:Timestamp>
<d:Address>123 Lakeview Blvd, Redmond WA 98052</d:Address>
<d:CustomerSince m:type="Edm.DateTime">2008-10-01T15:25:05.2852025Z</d:CustomerSince>
<d:Discount m:type="Edm.Double">10</d:Discount>
<d:Rating m:type="Edm.Int32">3</d:Rating>
</m:properties>
</content>
</entry>
</feed>
这就是我的部分。一旦您在本地端获取了响应数据,您就可以对其进行处理并显示数据。
但是等等,我还没有为您提供问题的解决方案。如果 Azure 存储中有大量数据,那么它将在表数据的响应中产生开销。
到目前为止,REST API 还没有允许通过任何机制获取聚合数据,但是您可以使用特定的 URI 来过滤表数据。过滤将有助于以过滤后的形式获取数据。也就是说,如果我想在本地获取任何特定数据,那么我就不需要从 Azure 存储表请求所有数据。Azure 存储为 URI 提供了查询语法。
例如:要返回单个命名表,请按如下方式指定表:*http://myaccount.table.core.windows.net/Tables('MyTable')*。
对我而言,我使用了过滤器查询语法:*http://myaccount.table.core.windows.net/Customers()?$filter=LastName%20eq%20'Smith'%20and%20FirstName%20eq%20'John'*。
要获取有关更多查询语法的详细信息,请使用此链接。
这是我在代码中应用过滤器的方法
if (Key == "")
{
uri = @"https://" + _Account + ".table.core.windows.net/" + Resource;
}
else
{
uri = @"https://" + _Account + ".table.core.windows.net/" +
Resource +"?$filter=Key%20eq%20"+"'"+Key+"'";
}
很简单!!嗯,还有一件事我想讨论:我如何读取 XML 并将其解析到相关的答案部分。
XDocument xdoc = XDocument.Parse(xml);
doc.LoadXml(xml);
XmlNodeList nodes = doc.GetElementsByTagName("d:Key");// Response XML attrib
List<string> la = new List<string>();
foreach (XmlNode n in nodes)
{
la.Add(n.InnerText);
}
我将列表绑定到数据源。如果存在重复的键,如我在问题陈述中前面提到的,那么我将读取列表并删除列表中的重复项,然后绑定到组合框。这又是另一个有趣的主题,我的室友建议使用哈希集,但我发现删除列表中的重复项更有趣。
static List<string> removeDuplicates(List<string> la)
{
Dictionary<string, int> uniqueStore = new Dictionary<string, int>();
List<string> finalList = new List<string>();
foreach (string currValue in la)
{
if (!uniqueStore.ContainsKey(currValue))
{
uniqueStore.Add(currValue, 0);
finalList.Add(currValue);
}
}
return finalList;
}
最后,我将这个列表绑定并删除列表中的重复项。
结论
我的问题仍然没有解决,但我从过滤器查询语法中获得了一些帮助。这不足以解决我的问题,但我期待着 Azure 存储中的一些新功能。