18:31 Back on My Feet » I got Spam?!
Hi everyone! I apologize for the lack of posts lately. I've been under the weather. Posting will resume today so stay tuned! See full article.

Related Entries:

Get Free Weather Forecasts with Vonage - 17 October 2006

Stormy Weather - 06 September 2008


Knock out SPAM at server level!

Contents of this feed are a property of Creative Weblogging Limited and are protected by copyright laws. Violations will be prosecuted. Please email us if you'd like to use this feed for non-commercial activities at feeds - at - creative-weblogging.com.
Hadoop 实战:谁是最倒霉的人?Free Mind » 车东 在 Google 阅读器中共享的项目

hadoop-logo.gif

上一次介绍了 MapReduce 的工作方式以及 Hadoop 这个开源的 MapReduce 实现,这次尝试用 Hadoop 来写一个简单的应用。要解决的问题是这样的:现在我手里有大量的邮件数据,并且我知道每封邮件是正常邮件还是垃圾邮件,现在我想要找出收到的邮件中垃圾邮件最多的人,亦即找出“谁是最倒霉的人”。

首先是 Map 的过程,输入数据是一封一封的邮件,彼此之间没有任何关联,因此可以很自然地分组处理。Map 将邮件转化到以邮件的收件人进行分组,如果邮件是垃圾邮件,则映射到收件人的垃圾邮件数“+1”。Reduce 的过程就是将各个收件人的邮件数统计结果加起来。

在 Hadoop 中实现一个 map 过程只需要实现 Mapper 接口就行了,一般同时继承自 MapReduceBase ,可以省下不少力气。map 接受 key, value 的 pair ,这是按照初始输入数据进行分组的,通常 map 方法从 value 中解析感兴趣的内容,并进行重新分组。map 的另一个 OutputCollector 类型的参数就是专门用于收集 map 之后的新分组的。map 方法看起来是这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void map(LongWritable key, Text value,
		OutputCollector<Text, LongWritable> output, Reporter reporter)
		throws IOException {
	String header = value.toString();
 
	if (isSpam(header)) {
		String addr = getToAddr(header);
		if (addr != null) {
			output.collect(new Text(addr), new LongWritable(1));
		}
	}
}

由于数据需要在网络上传输,Hadoop 要求 Key 和 Value 的类型都必须是可以序列化的,不过这不是 Java 自带的那个序列化接口 Serializable ,而是 Hadoop 自己定义的一个更加简易的 Writable 接口。另外,由于 Key 是需要用于进行排序和分组的,所以需要实现的是更加具体的一个叫做 WriteComparable 的接口。不过,常用的数据类型 Hadoop 都提供了现成的支持,比如 Text 可以用于存放文本,LongWritable 可以用于存放长整型数据。

在这个任务中,我 map 的输入 key 是长整型,在这里是分组时在文件中的 offset ,这里不需要用到它,直接忽略。而 value 是一封邮件的邮件头的内容。首先我要判断邮件是否是垃圾邮件。原本我可以用作好的分类器进行在线分类,或者使用一个已经做好的 index 来进行判断,不过为了让示例简单一些,我对邮件内容进行了预处理,直接将垃圾邮件标记作为一个邮件头域插入了邮件首部。因此判断是否是垃圾邮件的代码是直接从邮件头里搜索相应标签:

1
2
3
4
5
6
7
private boolean isSpam(String header) {
	Matcher matcher = labelPattern.matcher(header);
	if (matcher.find() && matcher.group(1).equalsIgnoreCase("spam"))
		return true;
	else
		return false;
}

判断出是垃圾邮件之后,就解析出邮件的收件人地址,这里暂时不考虑多个收件人或者有抄送之类的情况。得到收件人之后(比如,是 foo@bar.com),就算得到了一组结果。这将作为后面 reduce 任务的输入:key 的类型是 Text ,亦即收件人地址;value 的类型是 LongWritable ,即收到的垃圾邮件数目。当发现一封垃圾邮件时,就将这个中间结果 (foo@bar.com, 1) 收集起来。

这就完成了 map 的过程,之后应该是 reduce ,这个过程很简单,系统会按照 map 的结果将各个分组的结果传递到 reduce 函数,这里 reduce 只要把各个 LongWritable 加起来得到总和就可以了。在 Hadoop 自带的 Word Count 的示例中有一个类似的例子(不过这里加的是 IntWritable):

1
2
3
4
5
6
7
8
9
10
11
12
public class Reduce extends MapReduceBase 
                    implements Reducer<Text, IntWritable, Text, IntWritable> {
    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));
    }
}

不过因为 Hadoop 自带了一个 LongSumReducer 可以完成我们需要做的事情,就不用自己再费力写一个了。 :p 另外,Hadoop 在 map 和 reduce 之间还有一个叫做 combine 的步骤,可以看作是“本地的 reduce ”。除了某些特殊情况,一般 combine 和 reduce 做的事情是一样的(因此这两个任务通常也是通过同一段代码来实现的),只是 combine 只在本地运行,将当前节点得到的局部结果进行一下局部的 reduce ,这样通常可以减少需要进行网络传输的数据量。例如,如果当前节点发现了 5 封发给 foo@bar.com 的垃圾邮件,需要对 5 个 (foo@bar.com, 1) 进行 reduce 调度,而经过本地 combine 之后,只需要处理一个 (foo@bar.com, 5) 就可以了。

map 和 reduce 做好之后,新建一个任务,并告知用于完成 map 和 reduce 任务的类:

countJob.setMapperClass(CountMapper.class);
countJob.setCombinerClass(LongSumReducer.class);
countJob.setReducerClass(LongSumReducer.class);

再设置输入输出的 key 和 value 的类型:

countJob.setInputKeyClass(LongWritable.class);
countJob.setInputValueClass(Text.class);
 
countJob.setOutputKeyClass(Text.class);
countJob.setOutputValueClass(LongWritable.class);

配置好之后调用 JobClient.runJob(countJob) 就可以开始 MapReduce 了。大致的流程就是这样,不过中间还有一些细节需要处理。一个问题就是结果如何输出?我们最终得到的是一些 (Text, LongWritable) 的 pair ,一个办法是让它以文本方式按行输出到文本文件中,这样只需要使用内置的 SequenceFileOutputFormat 即可:

countJob.setOutputFormat(SequenceFileOutputFormat.class);

另一个问题则是输入的格式,Hadoop 需要理解了输入文件的格式才能将其解析并作为参数传递给 map 函数,更重要的是:进行合适的分割,将任务分配到各个节点上去。Hadoop 使用 InputFormat 来控制输入格式,默认情况下,将输入目录中每个文件当作以行为单位的文本进行处理和分割,除此之外 Hadoop 也内置了二进制记录等文件类型的支持。不过我这里的情况比较特殊:一个文件中只有一封邮件,需要当作一个不可分割的原子来处理。因此我实现了一个 AtomFileInputFormat ,并在 isSplitable 方法中总是返回 false

public class AtomFileInputFormat extends FileInputFormat<LongWritable, Text> {
 
	@Override
	public RecordReader<LongWritable, Text> getRecordReader(InputSplit genericSplit,
			JobConf job, Reporter reporter) throws IOException {
		reporter.setStatus(genericSplit.toString());
		return new FileRecordReader(job, (FileSplit)genericSplit);
	}
 
	@Override
	protected boolean isSplitable(FileSystem fs, Path path) {
		return false;
	}
}

另外,我定制了一个 FileRecordReader (需要实现 RecordReader 接口),从每个文件中读取邮件头,丢掉邮件的内容部分。

@Override
public boolean next(LongWritable key, Text value) throws IOException {
	if (hasRead) {
		return false;
	} else {
		Path file = split.getPath();
		FileSystem fs = file.getFileSystem(job);
 
		key.set(0);
 
		FSDataInputStream in = fs.open(file);
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
 
		StringBuilder sb = new StringBuilder();
		while (true) {
			String line = reader.readLine();
			// empty string, email headers and body are separated by an 
                        // empty line
			if (line == null || line.length() == 0)
				break;
			sb.append(line);
			sb.append('\n');
		}
		value.set(sb.toString());
 
		reader.close();
 
		pos = split.getLength();
		hasRead = true;
		return true;
	}
}

这样差不多任务就完成了,不过还有一点就是最后的结果是按照 key 进行排序的,亦即按照收件人地址排序,而我期望找到“最倒霉”的人,甚至需要按照“倒霉程度”进行排序,所以我再启动另一个 MapReduce 任务,将 key 和 value 颠倒过来,并进行排序。由于这是一个非常常见的任务,Hadoop 内置了相应的支持,因此只需要把内置的 InverseMapperIdentityReducer 组装起来即可:

sortJob.setInputFormat(SequenceFileInputFormat.class);
sortJob.setMapperClass(InverseMapper.class);
sortJob.setNumReduceTasks(1); // write a single file
FileOutputFormat.setOutputPath(sortJob, new Path(args[1]));
sortJob.setOutputKeyComparatorClass(LongWritable.DecreasingComparator.class);

这样就能得到最终结果了!完整的代码可以从这里下载。

最后,总结一下:这样一个简单的 AtomFileInputFormat 似乎没有达到预期的目的,Hadoop 默认情况下似乎把“一个文件”当作一个“很大”的单位了,通常都是考虑将文件进行分割,再分派到各个节点,因此每个文件最少启动一个 map 任务,而现在我的情况是一个文件中只有一条数据,结果似乎是每一个 map 操作都启动了一个新的 map 任务。如果要改进的话,一个是更加深入地定制 InputFormat 的风割方法;另一个办法是对输入数据进行预处理,后者要方便一些,但是在实际应用中如果遇到数据量大到进行预处理本身就需要 MapReduce 的支持的话,就有些不现实了。 ;) 另外,这种分布式的应用出了问题之后不知道要怎样调试才好?且不说调试器的方法肯定不管用了,就算最原始的 printf 大法,也不知道最终的输出会 print 到苍穹的哪一个角落里去了 :p 必须要有一个强大的 log 系统才行,而且如果是在单机环境下也能成功重现的那种 bug 的话,应该也会好姐姐一些。

总之,在上一次介绍的时候我就说了:MapReduce 是对分布式任务进行了一定的限制而得到的结果,考虑到 80%/20% 原则,它可以相当棒地完成一部分类型的任务(也许还不到 80% 吧),但是如果妄图任何事情都要用它来搞,大概也就是削足而适履了。还有就是,没有像样的硬件环境,玩 MapReduce 真的一点都不好玩! :D

10:11 有没有好的网络公关? » laolu's blog: Blog

(从三鹿到蒙牛、伊利,波及百度、新浪等,这两天还曝出了海尔的网络公关……似乎品牌保护几乎就是给老大交保护费来摆平。除了这些坏的网络公关,难道就没有好的网络公关?正好MarketingSherpa最近有篇相关的文章,记录一下。)

原文:How to Practice Defensive Branding: 6 Key Factors to Build Credibility, Swat Bad Buzz(仅开放存取7天)。以下为翻译:

防御性品牌推广:创建可信度、消除坏流言的六大关键因素

摘要:网上的消费者议论保存期长——不像咖啡店的闲谈那么容易消失。这就是为什么保持关于你品牌的在线议论尽可能正面如此重要的原因。一位网络品牌专家探讨了维护你品牌网络可信度的6个因素——正面用户议论的关键推动力,还有发现和回应好的和坏的议论的提示。

社会化媒体是个回音室。有关你品牌谈话的重复和传播,比任何其他地方都要快。保持这些议论的正面,对维持你品牌的可信度极其重要。

Nielsen Online数字化战略服务(Digital Strategic Services)部门的执行副总裁Pete Blackshaw,专长于他所称的网络“防御性品牌推广(defensive branding)”。建立品牌可信度,在社会化媒体发生风暴之前就挫败它们,他的战略是什么呢?以感应和透明来监听用户创建的媒体。

“如果你分析一下原因,为什么品牌得到大量非议,在Google上产生负面的搜索结果,或是在水冷塔边上的对品牌的负面闲谈,你会发现这大多数源于这些品牌是否有可信度的问题,”Blackshaw说。

通过网络上可信的品牌能获得以下好处:
- 更正面的网上会话
- 成为业界专家的首选
- 更多免费的、正面的报道
- 较少依靠付费的媒体
- 更正面的搜索结果

Blackshaw说,可信的品牌建立起受信任和被赞许的评论的巨大仓库,在网上起到类似大众媒体的作用。以下是Blackshaw提出对于你品牌的网络可信度的六个最重要因素,以及保持对你品牌的会话有好意的一些提示。

品牌可信度的六个推动力

这六个推动力决定你品牌的网络声誉,通过以优秀的产品或服务,作为一个坦诚、透明和有感应的公司,围绕着防止负面口碑而展开。消除负面声音的最佳方式,就是永不给任何人——除了你的竞争对手——任何抱怨的理由。

推动力#1:信任(Trust)

信任也许是可信度最重要的推动力,Blackshaw说,他写了一本书《满意的用户告诉3个朋友,愤怒的用户告诉3000个人(Satisfied Customers Tell Three Friends, Angry Customers Tell 3000)》。消费者之间的互相信任,要超过他们对公司或广告的信任。对这种疑虑的最佳克服,是通过“坦诚、道德、直率、一贯和可预期的商业运作”,Blackshaw说到。

建立信任的方法:
·高性能 - 消费者需要相信,他们买的物有所值。
·安全 - 如果你的产品有害消费者,那他们就不会相信你的品牌。
·直率的沟通 - 在你的网站或产品资料中,排除含糊不清或难以捉摸的陈述;它们令人困惑,不会达致信任。
·可预期 - 对于危机,一个理性、坦诚的公司将会如何做出回应,很容易预见。它会为错事而道歉,并为解决问题而工作。
·牢固的担保和质保 - 履行你产品的担保和质保。

“你不用在场上弄很多花样,尤其是在社会化媒体的世界里,信任极其脆弱”,Blackshaw说。

推动力#2:真实(Authenticity)

真正的品牌坚持其清晰表述的准则。任何偏离其准则的品牌,都向蒙骗和虚假的指控敞开了大门。消费者有丰富的工具来证明或反证一家公司的宣称。遭到一个活跃的论坛或一个单独的博客揭发的任何一家公司,若推卸其准则,就会引发大量的负面议论。

·准则贯穿公司

比如,一个将自身定位于环保引领者的品牌,要针对被批评曝光而仔细检查公司。如果其办公室用白炽灯照明,或其包装不是可再生材料,或其主要供应商有个超级基金污染场地(superfund site: 指需要动用超级基金来整治的污染场地。译者注。),那非议就等着来了。排除被曝光,建立可信度,并且防止负面议论。

推动力#3:透明(Transparency)

透明就是开诚布公。在信息时代,消费者能找出几乎所有关于你公司的事。通过公开披露相关信息,可防止负面议论的爆发。“你不要想隐瞒任何事,或不希望用户发现你控制范围之外的事”,Blackshaw说。对讯息或批评的控制都如此。

让以下这些都能容易找到:
·你的产品是怎样制造的
·在哪里制造的
·你的供应商有哪些
·你的合作伙伴有哪些
·公司政策(如退货政策)
·你的劳动条件

还有,让你的产品标签容易阅读,不要生硬堆砌、令人费解地塞在广告中。

·注意:懂得分辨哪里是底线。不必泄露交易机密、产品设计或“秘密配方”。

推动力#4:倾听(Listening)

烦恼的用户想要被倾听。当他们被忽视的时候,他们会在博客或向朋友们抱怨你的品牌。一旦他们发现其他人也有同样的遭遇,议论会变成咆哮。向用户提供多种联络到你公司的方式,并且鼓励反馈意见。你能防止公众的抱怨,并预先认识到问题。让用户向你公司抱怨,比他们在YouTube上抱怨,要更好。

倾听的一部分,是使[用户]连络到你公司真的很容易。倾听的其他方式有:
·在你网站上有容易找到、容易使用的反馈表
·用户论坛
·用户提交的录音和录像
·针对反馈的特定email地址
·出现在社会化媒体网站中

监听用户创建媒体的发泄,对倾听也很重要。这些是你品牌被讨论的场所——即便你没有被邀请去对话。你可以利用各种工具,如Technorati和社会化网站的搜索特性等,手工地监听这些会话。也有厂商提供收费的、自动运行的解决方案。

推动力#5:感应(Responsiveness)

倾听只是会话的一半工作,回应用户也一样重要。对用户抱怨或建议没有响应的公司,会在网上失去大量的可信度。有感应包括识别你用户的愿望和偏好,并把他们加进你的服务中。这些信息可以是来自向你网站的提交,或你在社会化网站发现的会话。这些信息也能激发一种新产品的上市,或者说,你要在客户服务上投资更多些,以赢得品牌支持。

有感应也包括:
·为问题道歉并解决问题 - 快速地
·通过发布真相,与虚假的谣言斗争
·客户服务中快速的周转时间
·一直倾听

在回应用户会话之前,分析其根本原因。不要用不着边际的建议,来引导有关你品牌的对话。你不需要对每一个品牌谈论,都做出回应意见。例如,如果你注意到关于你产品成分的大量讨论,你要仔细检查,它们是安全的,并被完整地说出来。

·大量的触发

特定事件会触发关于你品牌的额外讨论。像新产品上市、超级杯(Super Bowl,美国美式足球联盟的年度冠军赛。译者注。)以及假日季节等事件,将会引发更多的讨论,Blackshaw说。这些时候会提供更多的机会和危险——取决于消费者在说什么。

推进力#6:肯定(Affirmation)

“肯定是一种提醒”,Blackshaw说。从内部而言,公司也许会相信是一个用户满意的巨人,或其产品是品质领先者。快速的网络研究会决定真相。对用户来说,如今研究是如此容易。所以,Google的搜索结果、Wikipedia的条目支持品牌的基本准则和目标,对品牌的可信度来说很必要。如果简单的研究就能推翻这些信赖,这会极大地败坏品牌的可信度,而且在社会化媒体世界中,为批评提供了可乘之机。

相关链接:
Pete Blackshaw: Tell 3000
http://www.tell3000.com

译者注:

1. 文中提到Pete Blackshaw写的书《满意的用户告诉3个朋友,愤怒的用户告诉3000个人(Satisfied Customers Tell Three Friends, Angry Customers Tell 3000)》,网站是http://www.tell3000.com,首页上还有6个推动力的表格(图片,转帖过来)。

2. Pete Blackshaw对该书的介绍视频

3. 视角是从企业出发。网络声誉的根本不在网络上,在于产品和企业的自身。弘扬真相,而不是制造“真相”、压制负面。倾听和回应加起来才构成对话。企业网站的内容组织和表达、反馈形式和易用。社会化网站、搜索引擎、维基百科等,整体影响。


^==Back Home: www.chedong.com

^==Back Digest Home: www.chedong.com/digest/

<== 2008-10-03
  十月 2008  
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    
==> 2008-10-05