在我们的第一个MapReduce练习中,我们使用针对本地开发集群的.NET SDK实现了一个有目的的简单MapReduce作业。 在本练习中,我们将使用相同的SDK实现稍微更复杂的MapReduce作业,但不使用我们的基于Azure的远程群集。
对于本练习,我们将使用ufo_awesome.tsv数据文件中显示的UFO Sightings数据库。 该文件由制表符分隔的数据行组成,包含以下字段:
DateObserved (in YYYYMMDD format)
DateReported (in YYYYMMDD format)
Location
UFO Type
Duration
Description
在我们的工作中,我们将统计不明飞行物的年份和形状。 在map函数中,我们将简单解析输入行,提取年份和形状。 年将作为关键,在reduce功能中,我们将使用LINQ计算形状的数量,因为LINQ可通过.NET框架提供给我们。 reduce函数的输出的年份将作为键,形状和计数(由制表符分隔)作为值。
开始,我们将像以前一样执行以下步骤。 但是,请注意在第二步中添加Windows Azure Storage 包。 这个软件包使我们能够在Azure中使用HDInsight:
1.启动Visual Studio并打开一个新的C#控制台应用程序项目。
2.使用NuGet将以下软件包添加到项目中:
- Microsoft .NET Map Reduce API for Hadoop
- Microsoft ASP.NET Web API
- Windows Azure Storage
3.如果Program.cs
文件尚未打开,请打开它。
4.将以下指令添加到程序中:
using Microsoft.Hadoop;
using Microsoft.Hadoop.MapReduce;
using Microsoft.Hadoop.WebClient.WebHCatClient;
一切都到位,写一个Mapper类如下:
using Microsoft.Hadoop.MapReduce;
using System;
using System.Collections.Generic;
public class MyUfoMapper : MapperBase
{
public override void Map(string inputLine, MapperContext context)
{
// tabs 分隔行
string[] inputValues = inputLine.Split('t');
string dateObserved = inputValues[0].Trim();
string ufoType = inputValues[3].Trim();
//年份 观察
string yearObserved = "unknown";
if (dateObserved.Length >= 4)
yearObserved = dateObserved.Substring(0, 4);
//获取ufo类型
if (String.IsNullOrEmpty(ufoType.Trim())) ufoType = "uknown";
//发送output
context.EmitKeyValue(yearObserved, ufoType);
}
}
map函数的逻辑是非常不言自明的。 制表符分隔的输入行在选项卡上进行解析。 DateObserved和UFO Type(shape)字段被访问,解析和清理。 DateObserved字段的年份作为键发出,UFO类型作为值发出。
现在我们将编写一个Reducer类:
using Microsoft.Hadoop.MapReduce;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyApp
{
public class MyUfoReducer : ReducerCombinerBase
{
public override void Reduce(string key, IEnumerable<string> values, ReducerCombinerContext context)
{
//按类型计数
var query = from v in values
group v by v into g
select new
{
ufoType = g.Key,
sightings = g.Count()
};
//将结果发送到output
foreach (var item in query)
{
context.EmitKeyValue(key, item.ufoType + "t" + item.sightings );
}
}
}
}
reduce功能的逻辑更多地涉及到使用LINQ。使用此功能,我们查询UFO类型值的传入集合,按类似值分组,并通过UFO类型提取计数。 (我们可以通过各种各样的方式完成这个任务,但是LINQ可以通过.NET框架提供给我们,我们也可以利用它来使编码功能尽可能简单。)reducer函数然后发出年密钥 和UFO类型并计数值。
通过定义Mapper和Reducer类,我们可以创建一个MapReduce作业。 为此,我们将定义一个配置,连接到Hadoop,并使用我们的配置执行我们的工作,就像我们以前一样。 也就是说,我们与Hadoop的联系看起来有很大的不同:
static void Main(string[] args)
{
//连接到集群
//Uri myUri = new Uri("http://localhost:50070");
//string userName = "brysmi";
//string hadoopUser = "hadoop";
//string passWord = "my password";
//string storageAccount = "brysmi.blob.core.windows.net";
//string storageKey = "my storage key";
//string container = "mycontainer";
//IHadoop myCluster = Hadoop.Connect(
// myUri, userName, hadoopUser, passWord,
// storageAccount, storageKey, container, false
// );
IHadoop myCluster = Hadoop.Connect();
//配置作业
HadoopJobConfiguration myConfig = new HadoopJobConfiguration();
myConfig.InputPath = "/demo/ufo/in";
myConfig.OutputFolder = "/demo/ufo/out";
//执行作业
MapReduceResult jobResult =myCluster.MapReduceJob.Execute<MyUfoMapper, MyUfoReducer>(myConfig);
//将作业结果写入控制台
int exitCode = jobResult.Info.ExitCode;
string exitStatus = "Failure";
if (exitCode == 0) exitStatus = "Success";
exitStatus = exitCode + " (" + exitStatus + ")";
Console.WriteLine();
Console.Write("Exit Code = " + exitStatus);
Console.Read();
}
如果您不清楚在哪里获取连接到Azure群集所需的值,请参阅早期帖子中的设置和数据加载练习。
重要
在我们在Visual Studio中执行我们的工作之前,我们需要做最后一件事:将项目的目标(CPU)平台设置为x64。 默认情况下,Visual Studio将尝试针对x86架构运行程序,这将导致在针对Azure群集中的HDInsight执行时出错:
1.从项目菜单中,选择<项目名称>属性项目。
2.单击“属性”页面左侧的“构建”项目。
3.在页面右上角的“平台”下拉列表中,将值更改为x64
现在设置了目标平台,运行应用程序。 作业成功完成后,您可以连接到Azure群集的名称节点或底层存储,以查看输出文件 /demo/ufo/out/part-00000
的内容。 在将数据加载到Azure时的早期文章中将介绍如何执行此操作。