大数据

使用 ASP.NET 管道实现无需 Hadoop 的 MapReduce

Doug Duerner
Yeon Chang Chang

下载代码示例(VB)

您是否希望将 MapReduce 在大数据方面的强大功能添加到您的智能手机应用,或在您的平板电脑或其他小型设备上增加丰富的数据分析功能,但认为这很困难?

您是否希望可以快速且轻松地将现有的单节点应用程序转换为分布式系统,而无需重新构建整个应用程序?

这些问题促使我们开始冒险创建一个非常容易设置和使用的 REST 式 MapReduce 组件。

诸如 Hadoop 之类的产品擅长处理大数据。我们创建了一种解决方案,通过牺牲一些这样的功能以实现简易性和灵活性,从而使大数据应用程序的开发更为简单。这样一来,您不必是专家也可在短时间内让工作系统正常启动并运转。网格的简易与 Hadoop 设置的复杂以及我们解决方案的敏捷与 Hadoop 群集的“行动迟缓”之间的对比,很有吸引力。

简而言之,我们创建了一个非常简单的基础结构,它可以使用 MapReduce 在“网格”节点上执行计算密集型的处理操作,或者,在这些节点上执行数据收集操作,这些操作的结果相互关联并整合为一个返回到客户端的最终结果。

背景

IIS Web 服务器(包含其 ASP.NET 管道)已被证明是高度可伸缩的企业级 Web 服务器。但这些技术并不限于只是提供网页和托管网站。您不能将其用作可通过 HTTP 访问的通用管道机制,这实际上没有任何技术原因。ASP.NET 管道步骤按顺序执行(在前一个步骤未完成之前不会执行下一个步骤),但是每个步骤都可以以异步方式并行执行。可以配置 IIS Web 服务器以运行多个提供 HTTP 请求的 ASP.NET 管道(多个 w3wp.exes)。

将 ASP.NET 管道用作通用管道(只在通过 HTTP 访问时发生)而不是提供网页和托管网站似乎有点背离正统,但是 ASP.NET 管道(具有异步管道步骤)实际上非常类似于微处理器中的 CPU 指令流水线处理 (bit.ly/1DifFvO),而具有多个 w3wp.exe 文件(每个 w3wp.exe 中都有一个 ASP.NET 管道)的能力非常类似于微处理器中的超标量设计 (bit.ly/1zMr6KD)。这些相似之处再加上经验证的可伸缩性,使得“可将 IIS Web 服务器和 ASP.NET 管道用于任何需要流水线处理功能的场合”成为一个引人注目的命题。

有许多产品已经做到了 REST 式 MapReduce(Hadoop、Infinispan、Riak、CouchDB、MongoDB 等等),但我们的研究发现,这些产品非常难于设置或需要专门的专业知识。

我们希望简单地使用我们已启动并正在运行的现有 Windows IIS 服务器;使用我们已写入的现有数据 API 方法;按需为我们的 UI 屏幕获取数据;以及在数分钟内启动并运行整个分布式 MapReduce 系统(所有这些只需分布式系统或 MapReduce 系统设计和体系结构方面有限的知识)。这样一来,您只需最少的工作量或知识就可在您的服务器或云上快速、轻松地将现有的小规模应用程序转换为更大的分布式系统。或者,如果您想要向现有的智能手机应用添加丰富的数据分析,也无需太费劲。

这一 REST 式 MapReduce 组件采用的是栓接式安装,无需重新编写现有的应用程序,如果您的目标只是将基本的分布式功能添加到已具有广泛的公共数据 API 的现有应用程序,那么它是一个最佳的候选选择。可以快速、轻松地模拟分布式计算模式,例如“分散-收集”,如图 1 所示。

模拟分布式计算的模式,例如“分散-收集”
图 1 模拟分布式计算的模式,例如“分散-收集”

本文附带的示例项目通过提供一个简单的基本基础结构演示了这一设计理念,您可以此为起点继续将其展开。这个设计之所以吸引人不在于它比其他产品优秀,而是它的简单易用。这只是现今大型 MapReduce 企业系统的一个易于使用的设计替代方法。该设计决不是成熟的企业 MapReduce 产品(如 Hadoop)的替代品,我们也没有暗示其中甚至近乎包含了所有领先产品的功能。

MapReduce

简单来说,MapReduce 是一种聚合大型数据存储的方法。Map 步骤在多个分布式处理服务器节点上执行。它通常会在各个分布式服务器节点上执行任务以从数据节点检索数据,并可在数据仍在分布式的服务器节点上时根据需要进行转换或预处理。Reduce 步骤在一个或多个最终处理服务器节点上执行,并使用许多不同的组合算法将来自 Map 步骤的所有结果合并到一个最终结果集。

在业务对象 API 的上下文中,Map 步骤执行业务对象 API 方法以获取数据,而 Reduce 步骤将所有 Map 步骤结果集合并为一个最终结果集(例如,按主键进行联合,或进行聚合,如按组求和),并将该最终结果集返回至发出请求的客户端。

MapReduce 的主要优点之一是,它允许您“向外扩展”而不是“向上扩展”。换言之,若要扩展一个主服务器节点,您只需持续添加更多的普通服务器节点即可,而无需购买更好的硬件。向外扩展通常是更便宜、更灵活的选择,因为它使用的是普通的商用硬件,而向上扩展通常更加昂贵,因为其复杂程度的加深往往会导致硬件的成本以指数级增加。

需要注意的一个有趣现象是,MapReduce 擅长处理海量数据(Internet 规模级)以及部分为结构化或非结构化的数据(如日志文件和二进制 blob)。与此相反,SQL 关系数据库擅长处理已规范化的带架构的结构化数据,至少在关系数据库的开销无法处理大量数据之前均可处理。

图 2 高度概述了 MapReduce 过程,并比较了简单 SQL 关系数据库查询与大数据 MapReduce 流程中的相应查询。

简单 SQL 关系数据库查询与使用 MapReduce 的同一查询
图 2 简单 SQL 关系数据库查询与使用 MapReduce 的同一查询

REST

表述性状态转移 (REST) 定义了一个在使用创建、读取、更新、删除 (CRUD) 范例的 HTTP 上运行的公共 API(分别基于 HTTP 谓词 Post、Get、Put 和 Delete),从服务器向发出请求的客户端返回对象的表示形式。REST 设法允许将对象本身作为一个实体进行公共访问,而不只是对象上的功能操作。这不是一种规范或 RFC,只是一种设计建议。您可以严格遵循纯粹的 REST 设计,并要求 URL 进行格式化,以将对象视为一个实体,如下所示:

http://server/MapReducePortal/BookSales/Book A

或者,您可以选择更多的 RPC 样式设计,并要求 URL 使用要执行的类和方法进行格式化,如下所示:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName?bookName=Book A

REST 式 MapReduce

REST 式 MapReduce 就是要通过 HTTP 针对 API 和分布式服务器节点之间传输机制进行 MapReduce 操作。

针对 API 和传输的 REST over HTTP 的如下优点使其独具魅力:

  • 在端口 80 上使用 HTTP 协议不受防火墙影响。
  • 几乎所有平台的客户端应用程序都可以轻松使用资源而无需特定于平台的依赖项。
  • HTTP 谓词(Get、Post、Put、Delete)是一种请求资源的简单又简洁的范例。
  • Gzip 压缩可以帮助减少有效负载大小。
  • HTTP 协议本身具有其他优点,例如内置缓存。

目前,示例项目使用针对 API 和传输的 REST over HTTP 并仅支持 Get 和 Post,无任何写入操作,并完全在 JSON 中进行通信。这类似于某些通过外部方式访问 Hadoop 分布式文件系统 (HDFS) 的已知方法(如 Hadoop YARN 和 Hadoop WebHDFS),但只支持系统运行的绝对最低要求。我们不打算替换 Hadoop,或与其所有的丰富功能匹配。我们只是尝试在牺牲功能的情况下提供一种非常基本的、易于使用的替代方法。

MapReduce 配置

对于示例项目,只需将 MapReduceModule.dll 复制到您想要用作 Map­Reduce 系统中的分布式服务器节点的各个 IIS 服务器节点上的虚拟目录 \bin 目录,然后将条目放入 web.config 文件中的模块部分,如下所示:

<modules>
  <add name="MapReduceModule" type="MapReduce.MapReduceModule" />
</modules>

至此大功告成。就是这么简单。

如果 IIS 服务器节点上没有任何虚拟目录,可使用 \bin 目录创建新的虚拟目录,将其投入使用并确保它使用 Microsoft.NET Framework 4 应用程序池。增加应用程序池上服务于 MapReducePortal 虚拟目录的 w3wp.exe 工作进程计数,以向 MapReduce 请求提供更多的处理管道。用于优化 IIS 服务器的其他高级配置选项通常已由管理服务器的 IT 部门进行了设置,并且此内容超出了本文范围,如果还未配置,您可访问 Microsoft 网站轻松获取相关信息。

REST 配置

在示例项目中,只需将 PathInfoAttribute 置于您现有的任何业务对象数据 API 方法上,并指定将用于将 URL 映射到方法和方法参数的 PathInfo 字符串。就是这样。

示例代码的酷炫功能之一是,现有业务对象数据 API 方法当前返回的任何数据类型始终可以保持不变且无需更改。基础结构能够自动处理几乎所有的类型,因为它使用 .NET DynamicObject 来动态地表示返回的数据类型。例如,如果现有方法返回一个 Customer 对象集合,则 DynamicObject 将表示 Customer 数据类型。

PathInfoAttribute PathInfo 字符串使用 Windows Communication Foundation (WCF) 使用的相同 .NET UriTemplate 类,并允许您执行所有您在 WCF Web HTTP REST 项目或 ASP.NET Web API 2 项目中能够执行的操作,例如参数变量名替换、通配符等。您来选择什么 URL 映射到什么方法。您可以全面控制并以自己喜欢的方式自由实施您的 REST API。您可以严格遵守纯粹的 REST API,让您的 URL 段表示您的对象,如第一类实体:

http://server/MapReducePortal/BookSales/Book A
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

或者,如果您愿意,您可以松散地遵守 REST 并让您的 URL 段指定您要在 URL 段中执行的类名和方法名:

http://server/MapReducePortal/BookSales/GetTotalBookSalesByBookName/Book A
[PathInfoAttribute(PathInfo="/BookSales/GetTotalBookSalesByBookName/{bookName}",
  ReturnItemType="Book")]
public BookSales GetTotalBookSalesByBookName(string bookName)
{
}

这完全取决于您。

备受瞩目的因素

示例项目设计备受瞩目的因素之一是,通过使用 ASP.NET 管道作为 MapReduce 管道以执行 MapReduce 过程而获得的可伸缩性。由于 ASP.NET 管道按顺序运行,因此其适合执行 Map 和 Reduce 步骤。尽管该管道是有顺序的并且在前一步骤未完成之前不会移到下一步,但每个步骤仍可异步执行,这一点太酷了。这使得管道可以继续接收和处理新的 MapReduce 请求,甚至在该管道因等待从其他分布式服务器节点返回 Map 调用而受阻时也是可以的。

图 3 所示,每个 w3wp.exe 容纳了一个充当 MapReduce 管道的 ASP.NET 管道。W3wp.exe(IIS 工作进程)由分配给 Map­ReducePortal 虚拟目录的应用程序池管理。默认情况下,应用程序池拥有一个 w3wp.exe 以处理到虚拟目录的新传入请求,但可以非常轻松地根据需要配置为拥有任意多个 w3wp.exe。这使您可以在一个独立服务器节点上拥有多个 MapReduce 管道,所有管道进行合作以处理传入到 MapReducePortal 虚拟目录的 MapReduce 请求。单个 ASP.NET 管道的异步特性允许并行处理多个请求。拥有多个 w3wp.exes 以促进多个 ASP.NET 管道的能力将您提升到一个新的水平。

增加应用程序池的 IIS 工作进程计数以使更多的 MapReduce 管道服务于发送至此 IIS 服务器的 MapReducePortal 虚拟目录的 MapReduce 请求
图 3 增加应用程序池的 IIS 工作进程计数以使更多的 MapReduce 管道服务于发送至此 IIS 服务器的 MapReducePortal 虚拟目录的 MapReduce 请求

示例项目的设计还允许您根据需要不停添加任意多个 IIS 服务器,以形成越来越大的“网格”式服务器节点,如图 4 所示。网格增长得越大,就越有可能将较大的问题分解为更小的待解决部分,并且可能实现的并行性级别也越高。异步 ASP.NET 管道,结合每个服务器的多个管道,跨单个服务器的 CPU 内核实现并行性。服务器网格可跨多个服务器设备提供另一个级别的并行性。将多个 IIS 服务器添加到网格非常容易;您所要做的只是将 MapReduceModule.dll 复制到虚拟目录下的 \bin 文件夹,并将条目添加到 web.config 文件。由于 IIS 服务器都是独立服务器,因此无需额外配置。与此相反,类似 Hadoop 之类的产品通常需要更多的工作量、计划和专业知识,因为服务器通常必须配置为实际的服务器“群集”。

任何服务器节点都可发起 MapReduce 请求,而且 AJAX URL 中列出的任意数量的其他分布式服务器节点都可并行执行请求的 Map 步骤部分
图 4 任何服务器节点都可发起 MapReduce 请求,而且 AJAX URL 中列出的任意数量的其他分布式服务器节点都可并行执行请求的 Map 步骤部分

您甚至无需特别构建的 IIS 服务器。只需通过将 MapReduceModule.dll 复制到服务器上已存在的任何虚拟目录,您就可以直接使用任何可用的 IIS 服务器。这就是所有操作。现在,下一个 AJAX 调用可以在 URL QueryString 中的 distributednodes 列表参数中包括新的 IIS 服务器。

服务器网格设计的另一个好处是,它的运作不依赖于主节点。在诸如 Hadoop 之类的产品中,主节点管理服务器群集和该服务器群集中的数据位置。当 Hadoop 在生产环境中伸缩至其限制值时,此主节点是故障来源,而不是数据量或基础结构。

在此服务器网格设计中,没有主节点。任何服务器节点都可以发起 MapReduce 请求,数据存在于收集它的节点上。如图 4 所示,任何服务器节点都可以并行地同时既是数据请求方又是数据提供方。服务器可以从执行 Map 函数的服务器网格中的任何其他节点请求数据,可以接收结果,以在 Reduce 步骤中将它们合并为一个最终结果集。同时,同一个服务器节点还可以充当处理 Map 步骤的边缘服务器节点,为来自另一个服务器节点的 MapReduce 请求返回其部分结果并缩减回至那个节点。

目前,发出请求的客户端可识别数据的位置(通过 URL QueryString 中的 distributednodes 列表)。相反,您可以修改设计以在每个单独节点上的数据库表中存储此列表(或只是此节点最近的邻居节点或在多个节点间托管数据拆分的节点),并以编程方式在运行时将它们添加到该 URL。在某种意义上讲,这会使单一的主节点概念转变为分布式主节点概念,其中每个节点都知道从何处获取数据。这就好像主节点分布于网格中,从而允许其随着网格进行伸缩。

因为此网格设计使用一系列可靠的 Microsoft 产品(Windows Server、IIS Web 服务器和 SQL Server 数据库),所以您会获得这些商业产品中已内置的可靠冗余和容错功能(如 Windows Server for IIS 上的网络负载平衡 [NLB]、SQL Server 带自动页修复的 AlwaysOn 可用性组与带自动页修复的镜像)。这些功能的详细信息可随时通过 Microsoft 网站获取。

示例项目的设计还允许多个 MapReduce 请求“链接”起来以形成工作流,其中一个 MapReduce 请求的起始输入是前一个 MapReduce 请求的结果。这是通过将 MapReduce 请求更改为 Post(而不是 Get)并在 Post 请求的正文中包括前一个 MapReduce 请求结果来实现的。图 5 显示测试页面中生成输出的示例。

在测试页面中显示链接生成的输出
图 5 在测试页面中显示链接生成的输出

示例项目概述

从本质上讲,MapReduceModule.dll 将 ASP.NET 管道转换为 MapReduce 管道。它使用 HttpModule 来实现 Map 和 Reduce 功能。有趣的是,Reduce 步骤中执行的某些合并操作(如联合)依赖于 IEqualityComparer<T>,其中 T 是 DynamicObject,在某种意义上,该对象允许您基于作为运行时字符串值的属性名进行相等比较,即使 IEqualityComparer<T> 要求在编译时定义具体类型也是如此。太酷了!

图 6 高度概述了 MapReduce­Module.dll 的设计,其中显示了处理流通过 MapReduceModule.dll 时的情形。MapReduceModule 是唯一必需的 dll,您想要参与到 MapReduce 基础结构中的每一个服务器节点上都需要有此 dll。添加 MapReduce­Module.dll 到服务器非常简单,只需将 MapReduceModule.dll 复制到虚拟目录下的 \bin 文件夹,并将条目添加到 web.config 文件。

MapReduceModule.dll 处理流的高级别设计概述
图 6 MapReduceModule.dll 处理流的高级别设计概述

图 6 中,IHttpModule 通过订阅 AddOnBeginRequestProcessingAsync 事件(该事件在 ASP.NET 管道中的“开始请求处理”步骤期间触发)来使用 ASP.NET 管道中的第一步执行 MAP 功能。IHttpModule 通过订阅 AddOnEndRequestProcessingAsync 事件(该事件在 ASP.NET 管道中的“结束请求处理”步骤期间触发)来使用 ASP.NET 管道中的最后一步执行 REDUCE 功能。

简单地说,您只需订阅 ASP.NET 管道中的“开始请求处理”和“结束请求处理”事件。它们将按顺序执行,在上一个步骤未完成之前不会移至下一个步骤。

在“开始请求处理”步骤中,IHttpModule 将发起所有 MAP 请求,方式是,通过查询本地节点并发送 HTTP Web 请求至 URL QueryString 中的 distributednodes 列表参数中的所有分布式服务器节点。发送到每个分布式服务器节点的 HTTP Web 请求使用发起此请求的相同 URL,但不使用其 URL 中的任何 distributednodes 参数。

在接收 MAP 请求的分布式服务器节点上,相同的两个 ASP.NET 管道步骤按顺序执行,但是由于其 URL 没有任何 distributednodes 参数,“开始请求处理”和“结束请求处理”步骤实质上只查询该节点。在该边缘分布式服务器节点上执行使用 PathInfoAttribute 指定的 MAP 数据检索方法,以便从该节点中获取本地数据。然后,从各个边缘分布式服务器节点到发起最初请求的服务器节点的响应流中返回的数据将存储在将 URL 用作键的 HttpContext 中,以便之后可在最终的 REDUCE 步骤中对其进行检索。

在发起最初请求的本地服务器节点上,执行使用 PathInfoAttribute 指定的 MAP 数据检索方法,以获取发起最初请求的本地服务器节点上的本地数据。然后,来自本地服务器节点的数据将存储在将 URL 用作键的 HttpContext 中,以便可在最终的 REDUCE 步骤中对其进行检索。

在“结束请求处理”步骤中,IHttpModule 将执行 REDUCE 步骤,方式是,通过查看 HttpContext 以获取所有数据和 URL QueryString 中提供的 REDUCE 参数(其中可能包含预定义选项,如 sum= 和 union=、sort=,或自定义函数选项,如 reduce=CustomReduceFunction)。接下来,它使用指定的 REDUCE 参数将来自所有节点的所有数据集合并/化简为一个最终的结果集。最后,它将最终结果集序列化至 JSON,并在响应流中将该结果集返回到发起最初 AJAX MapReduce 请求的客户端。如果没有指定 REDUCE 参数,则返回来自所有节点的所有原始数据。图 7 显示测试页面中生成输出的示例。

测试页面中的生成输出
图 7 测试页面中的生成输出

比较示例项目与 Hadoop

图 8 比较了 Hadoop 和示例项目中的基本 MapReduce 功能。

图 8 基本 MapReduce 功能的比较

Hadoop 示例项目
统计字数的 Java MAP 作业函数 任何使用 PathInfoAttribute 修饰的方法就像 MAP 作业函数
求和字数统计的 Java REDUCE 函数 URL QueryString 中的 Reduce 参数(例如 sum =)就像执行求和操作的 REDUCE 作业函数
可写接口(序列化) [Serializable()] 属性(序列化)
WritableComparable 接口(排序)

IComparer<T> 接口(排序)

IEqualityComparer<T> 接口(sum、union)

MAP 作业的输入是一组 <键,值> 对,REDUCE 作业输出是一组 <键,值> 对 用 PathInfoAttribute 标记的方法的参数类似于 MAP 作业的输入,URL QueryString 中的化简参数执行化简操作并将结果序列化到类似 REDUCE 作业输出的 JSON

MapReduce 占有优势的一个常见情形是,计数特定词在数以百万的文档中出现的次数。图 9 为一些基本的伪代码实现的大数据比较,这与著名的“Hello World”示例程序 -“字数统计示例”等效。该图显示了 Hadoop Java 代码实现及可用于在示例项目中实现等效效果的相应 C# 代码。请记住,此代码只是伪代码,并不是正确或完整的代码。它只是用于说明在两种设计中实现类似功能的可能方法。图 10 显示了测试页面中的生成输出。

图 9“字数统计示例”伪代码比较

Hadoop MAP

public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output,
  Reporter reporter) throws IOException {
  String line = value.toString();
  StringTokenizer tokenizer = new StringTokenizer(line);
  while (tokenizer.hasMoreTokens()) {
    word.set(tokenizer.nextToken());
    output.collect(word, one);
  }
}

Hadoop REDUCE

public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable>
  output, Reporter reporter) throws IOException {
  int sum = 0;
  while(values.hasNext()) {
    sum += values.next().get();
  }
  output.collect(key, new IntWritable(sum));
}

示例项目 MAP

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count
[PathInfoAttribute(PathInfo="/WordCount/{fileName}", ReturnItemType="Row")]
public HashSet<Row> GetWordCount(string fileName)
{
  HashSet<Row> rows = new HashSet<Row>();
  byte[] bytes = File.ReadAllBytes(fileName);
  string text = Encoding.ASCII.GetString(bytes);
  string[] words = text.Split(new char[ ]{ ' ', '\r', '\n' });
  foreach(string word in words)
  {
    dynamic row = new Row();
    row["Word"] = word;
    row["Count"] = 1;
  }
  return rows;
}

示例项目 REDUCE

http://server/.../WordCount/Test.txt?distributednodes=Node1,Node2,Node3&union=Word&sum=Count

测试页面中的生成输出
图 10 测试页面中的生成输出

图 11 演示了如何在示例项目中实现基本的 MapReduce 功能。请注意 URL 中对象实体如何映射到 PathInfoAttribute 中等效的 MAP 步骤函数,以及 URL QueryString 中 REDUCE 参数选项(如 sum= 和 reduce=)与 Hadoop 中等效 REDUCE 步骤功能之间是如何对应的。

图 11 示例项目中的基本 MapReduce 功能

         (如 Hadoop MAP)                   (如 Hadoop REDUCE)

http://server/.../BookSales?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales", ReturnItemType="Book")]
public BookSales GetTotalBookSales()
{
}

         (如 Hadoop MAP)                   (如 Hadoop REDUCE)

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

其他示例

图 12 显示了实现 Map­Reduce 类型功能的其他方法以及 REST 式 URL 与这些方法之间的对应关系。为了简洁起见,此处省略了该方法的实现代码。该代码可以运用于不同的场合,包括:统计字数的算法、连锁书店中每个书店数据库中的“书籍销售”表数据、返回业务对象类集合的业务对象数据 API 方法,以及来自分布于全国各个位置的传感器数据。这全都取决于您的想象力 - 玩得开心!

图 12 使用示例项目实现 MapReduce 的多种途径

示例

http://server/.../BookSales/Book A?distributednodes=Node1,Node2,Node3&union=BookName&sum=Sales
[PathInfoAttribute(PathInfo="/BookSales/{bookName}", ReturnItemType="Book")]
public BookSales GetTotalBookSales(string bookName)
{
}

示例

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&union=AlarmID
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}

示例

http://server/.../Alarms?distributednodes=Node1,Node2,Node3&reduce=UnionIfNotDeleted
[PathInfoAttribute(PathInfo="/Alarms", ReturnItemType="Alarm")]
public Alarms GetAlarms()
{
}
private static HashSet<Alarm> UnionIfNotDeleted(HashSet<Alarm> originalData,
  HashSet<Alarm> newData)
{
}

示例

http://server/.../SensorMeasurements/2?distributednodes=Node1,Node2,Node3&union=SensorID
[PathInfoAttribute(PathInfo="/SensorMeasurements/{sensorID}",
  ReturnItemType="SensorMeasurement")]
public SensorMeasurements GetSensorMeasurements(int sensorID)
{
}

示例

http://server/.../MP3Songs?distributednodes=Node1,Node2,Node3&union=SongTitle
[PathInfoAttribute(PathInfo="/MP3Songs", ReturnItemType=" MP3Song")]
public MP3Songs GetMP3Songs()
{
}

总结

在本文中,我们演示了一个简单的基本基础结构,用于实现 Map­Reduce 功能,该功能可以通过 REST over HTTP 获取并在在诸如智能手机、平板电脑之类的小型设备上使用。我们还简要谈及如何将单节点应用程序转换为基本的分布式系统。

有大量的 MapReduce 基础结构可以完美地执行任何任务,但本文的目标和重点是使基本的 MapReduce 机制可以通过极为简单的方式进行设置,并易于使用。

我们解决方案的设置与扩展的简便性使您能够在小范围内测试自己的想法(在几台便携式计算机上),并在您的想法经过验证后轻松地向上扩展(根据需要在任意多台服务器上)。

示例项目允许您在 MAP 步骤中使用现有业务对象数据 API 方法,只需将属性应用于将 URL 路径映射至该方法的方法即可。它还允许您通过将简单的命令添加到 URL QueryString(例如基于主键的数据上的合并操作,如联合)控制 Reduce 步骤。

通过将属性应用于现有业务对象中的数据 API 方法,并基于 URL 中的主键字段指定联合命令,您将获得一种简单的机制,该机制可让您不费吹灰之力即可将单节点应用程序的部分转换成基本的分布式系统,从而能够在一个位置全局集中查看整个分布式系统。例如,通常只在该单节点上检索项目的业务数据对象现在可以在多个节点上检索项目,并基于项目中的主键字段进行合并。本地办公机构的数据可以根据需要相互关联或聚合,并显示在总部的一个屏幕上。

对于小型设备,“繁重的工作”都会发生在网格中的 IIS 服务器上而不是小型设备上。因此,例如,智能手机应用可以通过一个简单的 HTTP 调用观看 MapReduce 范例,从而使用最小的手机资源。


Doug Duerner 是一名高级软件工程师,拥有超过 15 年的使用 Microsoft 技术设计和实现大型系统的经验。他曾就职于多家名列财富 500 强之内的银行机构和一家商业软件公司,这家公司设计并构建的大型分布式网络管理系统得到了美国国防部国防信息系统局 (DISA)(用于其“全球信息网络”)和国务院 (DoS) 的采用。他有一颗极客的心,专注于各个方面,对最复杂且具有挑战性的技术难题情有独钟,尤其是那些大家都说“无法完成”的事情。您可以通过 coding.innovation@gmail.com 与 Duerner 取得联系。

Yeon Chang Wang 是一名高级软件工程师,拥有超过 15 年的使用 Microsoft 技术设计和实现大型系统的经验。他也曾就职于多家名列财富 500 强之内的银行机构和一家商业软件公司,这家公司设计并构建的大型分布式网络管理系统得到了美国国防部国防信息系统局 (DISA)(用于其“全球信息网络”)和国务院 (DoS) 的采用。他还为全球最大的芯片制造商之一设计并实现了大型驱动程序认证系统。Wang 获有计算机科学专业的硕士学位。他非常喜欢解决复杂问题,您可以通过 yeon_wang@yahoo.com 与他取得联系。

衷心感谢以下 Microsoft 技术专家对本文的审阅:Mikael Sitruk 和 Mark Staveley
Mikael Sitruk 是一名高级软件工程师,具有超过 17 年的使用各种技术设计和实现大型系统的经验。在 Microsoft 之前,他就职于电信软件提供商领先企业并实现了若干新颖的产品。他对于分布式系统、大数据以及机器学习充满热情。他曾有几年时间一直在研究 Hadoop 生态系统和无 Sql 技术(如 Cassandra 和 HBase)。您可以通过 Mikael.Sitruk@outlook.com 与 Mikael 取得联系

Mark Staveley 是 Azure 的大型计算团队的高级程序员。转到 Azure 之前,Mark 是 Microsoft Research(负责监督其大数据管理和处理程序)的成员,而之前也是 Xbox One 编译器与代码生成团队(关注于游戏引擎性能和兼容性)的成员。Mark 持有皇后大学的理学士学位、怀卡托大学的理科硕士学位以及纪念大学的计算机科学/计算化学的博士学位。进入 Microsoft 之前,Mark 是加拿大两个最大的高性能计算中心的一名研究员。