您的位置:首页 > 其它

【转】使用 F#、MapReduce 和 Windows Azure 分析日志文件

2011-11-08 08:52 483 查看
http://msdn.microsoft.com/zh-cn/magazine/gg983490.aspx

使用 F#、MapReduce 和 Windows Azure 分析日志文件

Noah Gift

下载代码示例

作为一名长期使用 Python 的程序员,我对访问 F# 语言架构师 Don Syme 很感兴趣。 在访谈中,Don 提到“有些人把 [F#] 看作一种强类型的 Python,只是句法上存在差异。”这让我觉得有必要做进一步的调查。

事实证明,F# 是一种富有想像力且激动人心的新编程语言,但许多开发人员对它还并不了解。 F# 也能为人们提供 Ruby 和 Python 程序员近年来所享受到的效率优势。 与 Ruby 和 Python 一样,F# 是也一种高级语言,语法很少并且表达式很简洁。 而让 F# 真正变得独一无二的,是它将这些实用的特性与精妙的类型推理系统以及函数编程领域中的许多最佳创意融为一体。 这让 F# 变得无与伦比。

但是,除了新的高效编程语言之外,今天的您还将面对许多新鲜有趣的技术。

云平台(如 Windows Azure)的广泛应用,使得无论是单独的开发人员,还是成规模的大型公司,都能够获得分布式存储和计算资源。 云存储也包含一些有用的工具,例如可以水平扩展的 MapReduce 算法,使您能够很快编写出代码,对潜在的庞大数据集进行快速分析和排序。

利用这样的工具,您可以利用一个下午写上几行代码并部署到云中,然后就能处理上千兆的数据。 多么新奇的体验。

在本文中,我希望与您分享我对于 F#、Windows Azure 和 MapReduce 的一些兴奋之情。 我将把所有的想法集中起来,向您展示如何使用 F# 和 MapReduce 算法来分析 Windows Azure 上的日志文件。 首先,我会介绍一些原型制作技术,这些技术降低了 MapReduce 编程的复杂性;然后,我会把结果……带入云中。

利用 F# 解决问题

.NET 程序员可以通过 F# 获得一种新的工作方式,那就是许多 Perl、Python 和 Ruby 程序员习以为常的交互式工作流。 这种编程方式经常使用交互式编码环境(例如 Python shell 本身)或者某种提供 readline 自动完成功能的工具(例如 IPython)。 这样,开发人员就可以从模块中导入一个类,将其实例化,然后利用 Tab 键自动完成功能来发现对象的方法和数据。

对于 .NET 开发人员来说,常见的交互式开发是在 Visual Studio 中编写代码,然后将代码段发送至 F# Interactive 窗口进行执行。 使用 Alt+Enter 组合键发送当前选定的代码段,或者使用 Alt+单引号发送单独一行代码段。 图 1 显示了这种方法的效果示例。



图 1 使用 F# Interactive 窗口

此方法非常适合用于对 F# 程序进行应急调试,并且您开发的 F# 脚本上可以同时提供 IntelliSense 和 Tab 键自动完成功能。

第二种方法仍然是在 Visual Studio 中编写代码,然后将代码的一部分从 Visual Studio 复制并直接粘贴到独立的 F# Interactive 控制台中(请参见图 2)。 如果您使用这种方法,务必在粘贴的代码之后添加两个分号。这样才能更好地利用 Tab 键自动完成功能与代码进行交互。 当您逐渐习惯通过更具交互性的方式进行编程时,您会发现自己正不断地使用这种方法。



图 2 F# Interactive 控制台

您还可以直接在 Windows PowerShell 中运行代码,以便交互式开发 F# 程序。这种方法将脚本传递给 fsi.exe(F# Interactive 控制台的可执行程序)本身。 此方法的优点之一是可以快速建立脚本的原型并将结果打印到标准输出。 此外,您还可以使用轻便型文本编辑器(如 Notepad++)反复编辑代码。 图 3 显示了在 Windows PowerShell 中运行时 Map-Reduce 脚本输出的示例。本文通篇都将使用此示例。

图 3 在 Windows PowerShell 中运行 F# 脚本

PS C:\Users\Administrator\Desktop> & 'C:\Program Files (x86)\FSharp-2.0.0.0\bin\fsi.exe' mapreduce.fsscript
192.168.1.1, 11
192.168.1.2, 9
192.168.1.3, 8
192.168.1.4, 7
192.168.1.5, 6
192.168.1.6, 5
192.168.1.7, 5


这些不同的程序编写方法都有各自的用武之地,可帮助您处理复杂的算法、网络编程和云。 您可以编写一个应急原型,并从命令行运行该原型,了解其是否达到了预期的效果。 然后,您可以回到 Visual Studio,开始构建更大的项目。

将这些背景信息放到一边,让我们先深入探讨一些实际代码。

MapReduce 式日志分析

除了具有上述交互编程的优势以外,F# 代码还兼具简洁和强大的特色。 图 4 中的示例代码少于 50 行,然而它却包含了 Map-Reduce 算法的所有重要部分,可以计算一组日志文件中的前 10 个常见 IP 地址。

图 4 用于分析日志文件的 MapReduce 算法

open System.IO
open System.Collections.Generic

// Map Phase
let inputFile = @"web.log"
let mapLogFileIpAddr logFile =
let fileReader logFile =
seq { use fileReader = new StreamReader(File.OpenRead(logFile))
while not fileReader.EndOfStream do
yield fileReader.ReadLine() }

// Takes lines and extracts IP Address Out,
// filter invalid lines out first
let cutIp =
let line = fileReader inputFile
line
|> Seq.filter (fun line -> not (line.StartsWith("#")))
|> Seq.map (fun line -> line.Split [|' '|])
|> Seq.map (fun line -> line.[8],1)
|> Seq.toArray
cutIp

// Reduce Phase
let ipMatches = mapLogFileIpAddr inputFile
let reduceFileIpAddr =
Array.fold
(fun (acc : Map<string, int>) ((ipAddr, num) : string * int) ->
if Map.containsKey ipAddr acc then
let ipFreq = acc.[ipAddr]
Map.add ipAddr (ipFreq + num) acc
else
Map.add ipAddr 1 acc)
Map.empty
ipMatches

// Display Top 10 Ip Addresses
let topIpAddressOutput reduceOutput =
let sortedResults =
reduceFileIpAddr
|> Map.toSeq
|> Seq.sortBy (fun (ip, ipFreq) -> -ipFreq)
|> Seq.take 10
sortedResults
|> Seq.iter(fun (ip, ipFreq) ->
printfn "%s, %d" ip ipFreq);;

reduceFileIpAddr |> topIpAddressOutput


此独立版本(将来会变成网络版本)可分为三个不同的阶段:映射阶段、归约阶段和显示阶段。

阶段 1 是映射阶段。 mapLogFileIpAddr 函数采用一个日志文件作为参数。 此函数内部定义了另一个函数 fileReader,后者使用函数编程技术从日志文件中延迟获取一行文本(不过,C# 和 Python 等语言也具有此功能)。 接下来,tcutIp 函数解析每一行输入,丢弃注释行,然后返回 IP 地址和整数 1。

若要查看延迟的原因,请突出显示整个映射代码块,并且在 F# Interactive 窗口中运行该代码块以及以下代码行:

let ipMatches = mapLogFileIpAddr inputFile


您将看到以下输出:

val ipMatches : seq<string * int>


请注意,现在还没有进行任何实际操作,日志文件也还没有读取。 唯一发生的事情就是已经对表达式进行了计算。 通过这种方式,执行将被延迟到实际需要时才会进行,而不需要只是为了计算表达式就将数据获取到内存中。 这是一项强大的数据处理技术,当您利用它来分析数以千兆甚至兆兆计的庞大日志文件时效果会变得尤其明显。

如果您想比较差异并且更快地计算代码,只需向 cutIp 函数中添加一行代码,使其看起来像下面这样(请注意:|> Seq.toArray 行对于函数的构造来说完全是可选的;在示例中,它的目的是有意加快函数的处理速度,如果省略它,该函数就只是会比较慢地运行,就像 mapLogFileIpAddr 函数):

let cutIp =
let line = fileReader inputFile
line
|> Seq.filter (fun line -> not (line.StartsWith("#")))
|> Seq.map (fun line -> line.Split [|' '|])
|> Seq.map (fun line -> line.[8],1)
|> Seq.toArray
cutIp


如果您将此代码重新发送至 F# 解释器并且提供一个包含数千兆数据的大日志文件,您可能想去喝杯咖啡,因为您的计算机将会忙于读取整个文件并在内存中生成键/值映射。

在下一节的数据管道中,我将使用映射结果的输出,并将序列结果集中提供给一个匿名函数,以便计算 IP 地址在序列中出现多少次。 它通过递归不断增加 Map 数据结构,以此来达到此目的。 这种编程方式对于刚刚开始使用函数编程的开发人员来说可能很难理解,因此您可能需要在匿名函数中嵌入 print 语句,以便了解它到底在做什么。

在命令式编程方式中,您可以更新将每个 IP 地址存储为一个键的可变字典,循环访问 IP 地址序列,然后更新每个值的计数,以此来完成同样的操作。

最后一个阶段与 MapReduce 算法没有任何关系,但是有助于在原型制作阶段中编写脚本。 映射阶段的结果通过管道从 Map 数据结构传输到 Seq。 结果进行了排序,并且将打印出前 10 个结果。 请注意,这种数据管道化方式能使一个操作的结果顺利进入另一个操作,而不需要使用 for 循环。

MapReduce 加上 Windows Azure

在我的概念验证脚本完成后 - 只用了不到 50 行代码,请记住 - 现在应该将它转移到类似于生产环境的环境中去。 我会将示例从桌面转移到 Windows Azure,以作演示。

作为背景知识,您可能会发现,查看 code.msdn.microsoft.com/fsharpazure 上的 Windows Azure F# 示例以及安装 Windows Azure 模板很有帮助。 特别有趣的是 webcrawler 示例,其中的 F# 辅助角色会同时使用 Blob 和队列存储终结点。 当您深入探究将 F# 与 Windows Azure 结合使用时,此项目便于您展开学习。

我不打算详细说明如何设置多节点的 MapReduce 场。 而是从更高的层面来概括它。 有关详细信息,请参见MSDN杂志 上由 Josh Twist 撰写的文章“在 Windows Azure 中同步多个节点”(msdn.microsoft.com/magazine/gg309174)。

有几种方法可以在 Windows Azure 上设置 MapReduce 场。 图 5 演示了一个使用 F# 辅助角色的示例,这些角色将被平均分配为映射辅助角色和归约辅助角色。 回到脚本,此操作简单到几乎只需将映射函数复制并粘贴到映射辅助角色,将归约函数复制并粘贴到归约辅助角色。



图 5 Windows Azure 中的 MapReduce 场

由 Jeff Dean 和 Sanjay Ghemawat 所做的 MapReduce 演示是很有用的参考,因为它详细说明了分布式算法和可能的实现方法 (labs.google.com/papers/mapreduce-osdi04-slides/)。 尽管在图 5 的示例中,它展示了若干日志文件由 F# 辅助角色并行使用。 然后,它们通过 Windows Azure AppFabric 服务总线传递到归约辅助角色或者通过写入磁盘的形式,返回其输出,该输出包含 IP 地址键以及值 1。

接下来,归约辅助角色读取这些中间数据,生成键/值对的总数,并将其写到 Blob 存储中。 每个归约辅助角色会生成自己的汇总报告,需要先将这些报告综合起来,才能由主控辅助角色进行排序和显示。

辅助角色的创建和发布

完成原型并规划好高级体系结构后,下一步就是在 Visual Studio 2010 中创建必要的项目并将其发布到 Windows Azure。

创建 F# 辅助角色并不那么简单,因此让我们逐步讲解其过程。 首先,您需要下载上文提到的 Windows Azure F# 模板。 接着,您需要为 Windows Azure 创建一个 Visual C# 项目。 我把我的项目命名为 AzureFSharpProject。

接下来,您可以选择创建如图 6 所示的 F# 辅助角色。



图 6 创建 F# 辅助角色

此时,您可以将映射函数放到映射辅助角色中,或者将归约函数放到归约辅助角色中。 然后进一步创建辅助角色以便用于其他映射辅助角色或归约辅助角色,具体取决于您的数据处理需要的规模。 可供参考的规范参考资料是 labs.google.com/papers/-mapreduce.html 上的 Google MapReduce 文件。 它详细介绍了 Map-Reduce 的体系结构、注意事项和使用案例。

当您准备发布到 Windows Azure 时,可以右键单击项目,选择“发布”,然后选择“仅创建服务包”,如图 7 所示。



图 7 发布到 Windows Azure

最后,需要登录新的 Windows Azure 管理门户,并使用其界面创建辅助角色(请参见图 8)。



图 8 配置新的辅助角色

此时,您可以使用任何您觉得合适的方法来绑定节点,并且使用 MapReduce 分析云中的日志。 当然,除了简单的日志文件以外,这种方法也可以轻松应用到其他数据源。 此 F# MapReduce 算法的概要以及我在编码过程中演示的交互式技术,可用于任何分析、映射和归约工作。

后续步骤

F# 是一种强大的语言,可以快速制作简化方案并从中创建出更复杂的解决方案,从而方便您快速解决问题。在本文中,我利用它将 MapReduce 算法分解成更小的部分。 这样,我就能展示仅仅 50 行的 F# 是如何转变成基于 Windows Azure 的日志分析器的。

关于在 Windows Azure 上实现 MapReduce,您还可以看看这一主题的另外两篇有趣的文章。 首先,请查看 MSDN 上关于辅助角色和 MapReduce 的讨论文章“为 Windows Azure 构建可扩展的多租户应用程序”(msdn.microsoft.com/library/ff966483)。 另外,Juan G. Diaz 有一篇非常值得一读的博客文章“Amazon EC2 与 Windows Azure 的使用比较、云计算以及 MapReduce 的实现”(bit.ly/hBQFSt)。

如果您还没有看过 F#,我希望本文能让您尝试一下。 如果您有兴趣收听 Don Syme 访谈的全部内容(也正是它让我喜欢上了 F#),请访问 Simple-Talk 博客并收听该访谈 (bit.ly/eI74iO)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: