Windows Azure

在 Windows Azure 上为消费者设备构建可缩放性很高的平台

Bruno Terkaly
Ricardo Villalobos

下载代码示例

本文主要介绍可缩放性和互操作性,体系结构中需要这两种特性来支持当今热门移动平台(用户可能超过百万)的多样性。图 1 描绘了这种多样性,对于当下的开发人员来说,这是一种常见且具有挑战性的场景。为移动设备提供基于 Web 的服务是一项令人畏缩的任务,因为它需要多种不同的工具、语言和 IDE。在这种多样性的背后是对弹性缩放的需求,因为需要面对提供的众多 Web 服务和 TB 级的数据。

图 1 各种不同的移动技术对于开发人员是一项挑战

应用程序类型 平台 开发环境 语言
移动 Windows Phone Visual Studio C#
移动 Android Eclipse Java
移动 iOS Xcode Objective-C
基于云的 Web 服务器 Windows Azure Visual Studio C#

开发人员需要在两个不同的维度上缩放他们的 Web 应用程序。第一个维度是计算,它仅压缩到主机供应商提供的 Web 服务实例的数量,以响应移动 Web 请求。第二个维度是可缩放的数据: 某些云平台通过专用存储设备提供可缩放的数据,以允许开发人员缩放数百万移动用户的 TB 级数据,然后在多台服务器上轻松进行分区,从而获得 PB 容量快速的性能、冗余和支持。

要尽量对各种各样客户端通信提供支持,互操作方案是必不可少的。从数据格式到网络协议,每一个环节都需要仔细考虑。解决方案必须能最小化自定义编码,并最大可能地使用开放标准。

在本文中,我们使用 Windows Azure(Microsoft 云平台)中托管的 RESTful Web 服务解决互操作性挑战和弹性缩放问题。

图 2 描绘了基于 RESTful Web 服务的参考体系结构。RESTful 体系结构具有互操作性,因为它们与 HTTP/1.x 一同开发并对大量的客户端提供统一的通信。可以替代 REST 的体系结构是 SOAP。我们不选择使用 SOAP 是因为它更大、数据负载更慢且更加复杂。

A Solution Based on Open Standards
图 2 基于开放标准的解决方案

使用 Windows Azure 便于根据需要增加和减少规模。仅仅通过使用 Windows Azure 门户或管理 API 更改数字(即“实例计数”),几乎就可以毫不费力地缩放 RESTful Web 服务以满足任何层级的需要。

我们的实现使用 JSON(而非 XML)作为数据格式,因为它紧凑并广泛受到支持。XML 存在负载较大的难题。

尽管许多供应商为 RESTful Web 服务提供云托管解决方案,但是 Windows Azure 具有很多优势。对于初学人员来说,你可以在位于亚洲、欧洲和北美的六个高自动化数据中心之间进行选择,包括 24 个内容传送网络 (CDN),以便以低延迟和低数据局部性连接用户变为可能。

Windows Azure 提供了一组存储和计算选项,另外还有功能强大的开发人员工具。各种各样的存储机制都可用,从二进制大型对象 (BLOB) 到关系存储。Windows Azure 还提供身份管理系统、安全邮件和混合云/本地连接功能。

开始使用

本文的剩余内容将体系结构和实现分为四个部分:

  1. 使用 Windows Azure 门户设置帐户。
  2. 创建 Windows Azure 云项目,并编写一些代码定义 RESTful Web 服务。
  3. 使用 Windows Azure 门户将云项目部署到帐户。
  4. 为以下内容构建移动应用程序: Windows Phone、Android 和 iOS (iPhone/iPad)。

让我们一起来看看下面这些步骤。第一个步骤在 Windows Azure 门户中执行,如果你有订阅,可以从 windows.azure.com 进行访问。(有关详细信息,请访问 azure.com。)

第 1 部分: 在 Windows Azure 门户中设置 Web 服务

Windows Azure 门户中的两个重要选项为: 新建托管服务和新建存储帐户。

图 3 展示了设置“托管服务”的工作流。该过程会生成一个 URL,代表部署 RESTful Web 服务的 Microsoft 数据中心的端点。Windows Phone、Android 和 iOS 应用程序开发人员将需要该 URL 与服务进行通信。

Provisioning the Windows Azure RESTful Web Service
图 3 设置 Windows Azure RESTful Web 服务

完成上述所有操作的工作流很简单:

  1. 登录 Windows Azure 门户。
  2. 选择“新建托管服务”。指定帐户名、URL 和区域(数据中心的位置)。
  3. 存储 Windows Azure 门户生成的 URL;将在构建 RESTful Web 服务和移动客户端时与帐户名一起使用该 URL。帐户名在第 3 部分还要用到。

注意: 本文中的示例使用帐户名“fastmotorcycleservice”以及 URL“http://fastmotorcycleservice.cloudapp.net”。

在 Windows Azure 门户中的第二个任务是创建存储帐户。图 4 展示了该过程,包括 Windows Azure 表的名称和位置。现在还可以从六个数据中心内进行选择。最好在同一个数据中心托管 Web 服务和数据,以降低成本和提高性能。

Provisioning the Windows Azure Storage Account
图 4 设置 Windows Azure 存储帐户

工作流与之前介绍的“托管服务”相似:

  1. 登录 Windows Azure 门户。
  2. 创建新的存储帐户并提供帐户名和区域。
  3. 存储 Windows Azure 门户生成的访问键并提供帐户名;在构建 RESTful Web 服务时将需要这两项。

第 1 部分完成后,所需的 Windows Azure 门户信息可用于编写 RESTful Web 服务以及 Windows Phone、Android 和 iOS 应用程序。

第 2 部分: 构建 Windows Azure 托管的 RESTful Web 服务

在 Visual Studio 中构建 RESTful Web 服务很简单。在“开始”|“所有程序”|“Microsoft Visual Studio 2010”中,右键单击 Microsoft Visual Studio 2010 快捷方式并选择“以管理员身份运行”,以便作为管理员打开 Visual Studio。从“文件”菜单中选择“新建”|“项目”。

在“新建项目”对话框中,展开“已安装的模板”列表中的首选语言并选择“云”。选择 Windows Azure 项目模板,将项目名称设置为 FastMotorcycleProject,并将位置设置为方便的任何地点。

下面的网址提供了详细介绍这些步骤的视频:bit.ly/VideoAzureRestfulService

解决方案资源管理器如图 5 所示。

Creating a New Windows Azure Project
图 5 创建新的 Windows Azure 项目

图 6 显示本文未涉及的一些基本步骤(但在参考视频中有介绍)。

图 6 本文未涉及的基本步骤

视频中介绍的任务 备注
添加 ASP.NET Web 角色 将用于托管 RESTful Web 服务
添加 DataConnectionString 将包括帐户名和访问键
添加某些基本启动代码以初始化数据 将代码添加到 global.asax.cs 以读取 DataConnectionString

这些步骤在所有 Windows Azure 项目中很常见。例如,使用 Web 角色托管 RESTful Web 服务是一种标准做法。需要 DataConnectionString 以访问之前在 Windows Azure 门户中定义的存储帐户。Visual Studio 项目中需要启动代码从配置文件读取帐户名和访问键,以用于存储帐户。

完成最初的步骤后,可以使用 Visual Studio 中的 WCF 服务模板添加 RESTful Web 服务。

要添加 WCF 服务,请右键单击 FastMotorcycleProject_WebRole 文件夹,选择“添加”|“新建项目”对话框,并将类名称设置为 FastMotorcycleService。

随即生成 FastMotorcycleService.svc.cs。使用图 7 中所示的代码取代类的整个代码。

让此操作生效的关键在于知道如何映射不同的 URI 和 RESTful 方法的谓词。为此,必须将 WebGet 和 WebInvoke 属性添加到图 7 的代码中。

图 7 FastMotorcycleListService.svc.cs

[ServiceContract]
public class FastMotorcycleListService
{
  private FastMotorcycleListDataProvider _data;
  
  public FastMotorcycleListService()
  {
    _data = new FastMotorcycleListDataProvider();
  }

  [OperationContract]
  [WebGet(UriTemplate = "/list/{owner}", ResponseFormat = 
    WebMessageFormat.Json)]
  public List<string> GetItems(string owner)
  {
    return _data.GetItems(owner);
  }

  [OperationContract]
  [WebInvoke(UriTemplate = "/list/{owner}", Method = "POST",
    RequestFormat = WebMessageFormat.Json)]
  public void AddItem(string owner, string item)
  { 
    _data.AddItem(owner, item);
  }

  [OperationContract]
  [WebInvoke(UriTemplate = "/list/{owner}/{item}", Method = "DELETE")]
  public void DeleteItem(string owner, string item)
  {
    _data.DeleteItem(owner, item);
  }
}

这些属性告诉框架方法应该响应 HTTP GET 请求。 默认情况下,WebInvoke 映射到 HTTP POST。 同样在默认情况下,URI 由方法的名称(已添加到端点的基 URI)确定。 某些专家或 REST 纯粹主义者可能会说我们的方法名称不应是动词,而应是名词。

图 8 所示的 WCF REST 编程模型允许为每个方法自定义 URI,具体操作是使用可在 WebInvoke 或 WebGet 属性上通过 UriTemplate 属性设置的模板。 该模型将在以下列表中介绍,其数字与图 8 中的数字对应:

Workflow for Mobile Application Requesting RESTful Data
图 8 移动应用程序请求 RESTful 数据的工作流

  1. 移动应用程序使用标准 HTTP 发送消息请求,请求中包括一个 HTTP 谓词和一个 URL。
  2. RESTful Web 服务截获移动应用程序消息请求(数据请求)并调用 GetItems,传递“Bruno”作为参数。 GetItems 使用 LINQ 查询来查询数据,并使用“Bruno”作为 where 子句的一部分。
  3. 仅从 Windows Azure 表服务中返回 PartitionKey 等于“Bruno”的记录。
  4. 数据会转换为 JSON 格式(自动)并返回给移动设备。
  5. 数据可用于移动应用程序。 数据用于填充 ListBox 并呈现给移动应用程序用户。

下面我们讨论的三个类是帮助程序对象,需要用它们来与 Windows Azure 表服务进行交互。 Fast­MotorcycleListDataProvider、FastMotorcycleListItem 和 FastMotorcycleList 是从图 9 的代码中提取存储和 Windows Azure 表特定 API 详细信息的类,允许应用程序使用 Windows Azure 表服务执行创建、读取、更新和删除 (CRUD) 操作。

在 Visual Studio 中,添加一个名为 FastMotorcycleListDataProvider.cs 的新类模块。 使用图 9 中的代码替换该代码。

图 9 FastMotorcycleListDataProvider、FastMotorcycleListItem 和 FastMotorcycleList 类

public class FastMotorcycleListDataProvider
{
  private FastMotorcycleList _list;

  public FastMotorcycleListDataProvider()
  {
    string configValue = RoleEnvironment.GetConfigurationSettingValue(
      "DataConnectionString");
    var account = CloudStorageAccount.Parse(configValue);

    _list = new FastMotorcycleList(account.TableEndpoint.ToString(),
                                   account.Credentials);
  }
 
  public List<string> GetItems(string owner)
  {
    var results = from entity in _list.Items
                  where entity.PartitionKey == owner
                  select entity;

    var list = new List<string>();
    foreach (var item in results)
    {
      list.Add(item.RowKey);
    }

    return list;
  }
 
  public void AddItem(string owner, string item)
  {
    _list.AddObject("FastBikes", new FastMotorcycleListItem(owner, item));
    _list.SaveChanges();
  }

  public void DeleteItem(string owner, string item)
  {
    var entity = (from i in _list.Items
                  where i.PartitionKey == owner
                  && i.RowKey == item
                  select i).Single();

    _list.DeleteObject(entity);
    _list.SaveChanges();
  }
}

 
public class FastMotorcycleListItem : TableServiceEntity
{
  public FastMotorcycleListItem()
  {
  }
 
  public FastMotorcycleListItem(string partitionKey, string rowKey)
    : base(partitionKey, rowKey)
  {
  }
}
 
public class FastMotorcycleList : TableServiceContext
{
  public FastMotorcycleList(string baseAddress,
    StorageCredentials storageCredentials)
    : base(baseAddress, storageCredentials)
  {
  }
 
  public DataServiceQuery<FastMotorcycleListItem> Items
  {
    get
    {
      return this.CreateQuery<FastMotorcycleListItem>("FastBikes");
    }
  }
}

第 3 部分: 部署 RESTful Web 服务

这是 Windows Azure 可以真正炫耀的领域之一。 部署 100 个 RESTful Web 服务实例如同部署一个实例一样简单。 请注意下面的步骤列表:

  1. 在 Visual Studio 中,右键单击 FastMotorcycleProject 并选择“打包”。
  2. 使用浏览器返回门户并选择“托管服务、存储帐户和 CDN”。
  3. 在顶部窗格中,选择“托管服务”。
  4. 在中间窗格中,选择你之前创建的托管服务。
  5. 右键单击并选择“新建生产部署”,上载文件(FastMotorcycleProject.cspkg 和 ServiceConfiguration.Cloud.cscfg);这些文件是在第一步中生成的。

第 4 部分: 从移动应用程序使用 RESTful Web 服务

现在我们将讨论从各种移动应用程序使用 RESTful Web 服务。 本部分旨在重点介绍这种方法提供的互操作性。

JSONKit (github.com/johnezang/JSONKit) 使得从 iOS 设备与 RESTful Web 服务的交互更为简单。 只需几行代码,就可以完成以下操作:调用 RESTful Web 服务,下载 JSON 格式化的数据,将数据转换为更好用的格式并将转换的数据附加到表视图控件,再由 iPhone 或 iPad 应用程序使用(参见图 10)。

图 10 解析 JSON 数据的 Objective-C 代码

NSString *username = @"Bruno"; // Gets passed to the RESTful Web Service
  
NSString *serviceUri = "http://your_hosted_service_name.cloudapp.
net/"+
  "FastMotorcycleListService.svc/list/";
// Build the service URI (will point to our RESTful Web service
NSString *url = [NSString stringWithFormat:@"%@%@", serviceUri, username];
      
// Retrieve the data in the form of a JSON array
NSData *json = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
     
// Convert from JSON array to NSArray
// This allows us to populate the table view more easily
NSArray *itemArray = [json objectFromJSONData];
     
// Assign the array to the TableView
// fastbikes is the name of our TableView control
self.fastbikes = [[NSMutableArray alloc] initWithArray:itemArray];

开发 Android 涉及到 Java 编程语言,该语言已经问世很长时间,并可在本机解析 JSON 数据。 图 11 显示了一个示例。 Windows Phone SDK 包括本机支持以调用 RESTful Web 服务和处理 JSON 格式化的数据。 SDK 使得使用 DataContractJsonSerializer 处理 JSON 数据变得简单。 图 12 显示了一个示例。 最后,如果你想要了解开发 Android 和 iOS 的功能更强大的工具包,请访问 Microsoft 认可的以下链接: github.com/microsoft-dpe

图 11 解析 JSON 数据的 Android 代码

    // HttpClient used to talk to Web service
    HttpClient httpclient = new DefaultHttpClient();
                  
    String url = 
      "http://your_hosted_service_name.cloudapp.
    net/"+
      "FastMotorcycleListService.svc/list/Bruno";
    // This will be the array we need to convert
    // We get the data from the Web service
    JSONArray listItems = null;
    String jason = null;
                  
    // Set up the RESTful call to 'GET' the data
    HttpGet request_http_get = new HttpGet(url);
      
                  
    // Read the JSON data and assign it to ListView
    try 
    {
      // Fill a response object using a request
      HttpResponse response_http_get = httpclient.execute(request_http_get);
                
      // Length represents the number of data items returned
      // by RESTful Web service
      long length = response_http_get.getEntity().getContentLength();
    
      // "entity" ends up being the data coming back from Web server
      HttpEntity entity = response_http_get.getEntity();
     
      // Read the bytes, one byte at a time
      InputStream stream = entity.getContent();
                     
      // Allocate a series of bytes
      byte[] buffer = new byte[(int) length];
                    
      // Read bytes from RESTful Web service
      // After this loop, we end up with something like -> 
      // ["busa","gxr1000","ninja250"]
      for (int i = 0; i < length; i++) 
      {
        buffer[i] = (byte) stream.read();
      }
      // Create an array of strings
      jason = new String(buffer);
      // Convert to JSON array for Android ListBox
      // listItems ends up being a three-element JSON array (see "busa")
      listItems = new JSONArray(jason);
      } 
      catch (Exception e) 
      {
        System.out.println(e);
      }

图 12 解析 JSON 数据的 C# 代码

private void LoadList()
{
  string uri =   
    @"http://your_hosted_service_name.cloudapp.
net/"+
    "FastMotorcycleListService.svc/list/Bruno";
  var webRequest = (HttpWebRequest)WebRequest.Create(uri);
  webRequest.Method = "GET";
 
  try
  {
    webRequest.BeginGetResponse(new AsyncCallback((result) =>
    {
      var webResponse = 
        (HttpWebResponse)webRequest.EndGetResponse(result);
  
      if (webResponse.StatusCode == HttpStatusCode.OK)
      {
        var jsonDeserializer = 
          new DataContractJsonSerializer(typeof(List<string>));
        List<string> items = 
          (List<string>)jsonDeserializer.ReadObject(
          webResponse.GetResponseStream());                      

        shoppingListBox.Dispatcher.BeginInvoke(new Action(() =>
        {
          shoppingListBox.Items.Clear();
          foreach (var item in items)
          {
            shoppingListBox.Items.Add(item);
          }
        }));
      }

    }), null);
  }
  catch
  {
     // Ignored
  }
}

访问设备的整个家族

由于 Windows Azure 托管的 RESTful Web 服务基于 HTTP,因此支持该协议的任何客户端应用程序都可以与它进行通信。 这为开发人员打开了设备大门,众多设备可任意挑选,因为大多数设备都支持 HTTP 协议。 尽管我们在本文中讨论的是移动平台,但是诸如 jQuery 的 JavaScript 实现也可以使用 RESTful Web 服务。 无论移动平台针对 UI 多样性采取什么途径,构建简单、开放且基于 HTTP Web 服务的体系结构总是有意义的。

Bruno Terkaly 是 Microsoft 的开发推广人员。 他的知识深度来源于多年来相关领域以及使用大量平台、语言、框架、SDK、库和 API 编写代码的经验。 他不辞辛苦,就有关构建基于云的应用程序(特别是使用 Windows Azure 平台)编写代码、发布博客并给予现场演示。

Ricardo Villalobos 是一名资深的软件设计师,具有 15 年为供应链管理行业设计和创建应用程序的经验。 他持有多个不同的 Microsoft 证书,并获得了达拉斯大学供应链管理专业的 MBA 学位。他于 2010 年进入 Microsoft 并成为一名 Windows Azure 架构推广人员。

衷心感谢以下技术专家对本文的审阅:Reza Alizadeh 和 Wade Wegner