new Life(location)

有好久没有更新Blog了,在这一年忙着工作,忙着结婚,就在上个月到了加拿大开始全新的生活。

这里的工作文档写的很多,所以有更多可能把工作中可以公开的部分,写成文档放在这里分享。

最近正在修改simplecaptcha(http://simplecaptcha.sourceforge.net/)项目,增加和整理一些功能,修改后的代码发布在GitHub上叫ownCAPTCHA(https://github.com/sinofool/ownCAPTCHA)。
下一个版本完成的时候,会写一些例子在这里。

iCoupon黑名单

我从2013年5月27日起,连续收到通过iMessage的垃圾短信。垃圾也就罢了,也不是一天两天了,但是这个iCoupon妄图洗白自己,所谓退订链接甚至要留下手机号码,还谎称系统故障骗人退订留下更多信息。
因为暂时没有办法屏蔽,更不想越狱引入更大的流氓。唯一的办法就是抵制这些手段宣传过的产品了。

以下是通过iCoupon推广过的各种品牌,罗列在此,列入黑名单。

2013年5月
甜风集,蛋糕;
蓝色动力,汽车养护;

2013年6月
臆蜜坊,丰胸;
吾爱吾庐连锁公寓,酒店;
狗屎咖啡,咖啡;

2013年7月
火宫殿,餐饮;
DHC,化妆品;
世纪奥桥花卉园艺超市,花卉;
羽丹纤美,瘦身;
皙荷美道,丰胸;

******持续更新******

Build Google protobuf 2.4.1 for iOS development

Although iOS5 shipped with a private version of protobuf but it is too old for me.
Here is the script I used to build protobuf.
1. Download the newest protobuf(2.4.1 at the moment) and unpack.
2. Run this in the unpacked directory. It will create a folder named “protobuf_dist” on desktop.
3. Copy or add protobuf_dist to xcode project. That’s all.
#!/bin/bash

TMP_DIR=/tmp/protobuf_$$

###################################################
# Build i386 version first,
# Because arm needs it binary.
###################################################

CFLAGS=-m32 CPPFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32 ./configure --prefix=${TMP_DIR}/i386 \
--disable-shared \
--enable-static || exit 1
make clean || exit 2
make -j8 || exit 3
make install || exit 4

###################################################
# Build armv7 version,
###################################################

SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk
DEVROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer

export CC=${DEVROOT}/usr/bin/llvm-gcc
export CFLAGS="-arch armv7 -isysroot $SDKROOT"

export CXX=${DEVROOT}/usr/bin/llvm-g++
export CXXFLAGS="$CFLAGS"
export LDFLAGS="-isysroot $SDKROOT -Wl,-syslibroot $SDKROOT"

./configure --prefix=$TMP_DIR/armv7 \
--with-protoc=${TMP_DIR}/i386/bin/protoc \
--disable-shared \
--enable-static \
-host=arm-apple-darwin10 || exit 1
make clean || exit 2
make -j8 || exit 3
make install || exit 4

###################################################
# Packing
###################################################

DIST_DIR=$HOME/Desktop/protobuf_dist
rm -rf ${DIST_DIR}
mkdir -p ${DIST_DIR}
mkdir ${DIST_DIR}/{bin,lib}
cp -r ${TMP_DIR}/armv7/include ${DIST_DIR}/
cp ${TMP_DIR}/i386/bin/protoc ${DIST_DIR}/bin/
lipo -arch i386 ${TMP_DIR}/i386/lib/libprotobuf.a -arch armv7 ${TMP_DIR}/armv7/lib/libprotobuf.a -output ${DIST_DIR}/lib/libprotobuf.a -create

This is tested on OSX Lion with Xcode 4.2.1

使用Hive做数据分析

在大规模推广streaming方式的数据分析后,我们发现这个模式虽然入门成本低,但是执行效率也一样低。
每一个map task都要在TaskTracker上启动两个进程,一个java和一个perl/bash/python。
输入输出都多复制一次。

经过了一系列调研后,我们开始将部分streaming任务改写为Hive。

Hive是什么?

  1. Hive是单机运行的SQL解析引擎,本身并不运行在Hadoop上。
  2. SQL经过Hive解析为MapReduce任务,在Hadoop上运行。
  3. 使用Hive可以降低沟通成本,因为SQL语法的普及度较高。
  4. Hive翻译的任务效率不错,但是依然不如优化过的纯MapReduce任务。

数据准备

原始日志文件是这样的:
1323431269786 202911262 RE_223500512 AT_BLOG_788514510 REPLY BLOG_788514510_202911262

分别对应的字段是 <时间> <操作人> [[说明] [说明]……] <操作> <实体>
上面的例子对应的含义是:
  • <时间>: 1323431269786
  • <操作人>: 202911262
  • [说明]: RE_223500512
  • [说明]: AT_BLOG_788514510
  • <操作>: REPLY
  • <实体>: BLOG_788514510_202911262

扩展Hive的Deserializer

要用SQL分析数据,Hive必须知道如何切分整行的日志。Hive提供了一个接口,留给我们扩展自己的序列化和反序列化方法。

import java.util.Properties;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hive.serde2.Deserializer;
import org.apache.hadoop.hive.serde2.SerDeException;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.io.Writable;

public class RawActionDeserializer implements Deserializer {

@Override
public Object deserialize(Writable obj) throws SerDeException {
// TODO Auto-generated method stub
return null;
}

@Override
public ObjectInspector getObjectInspector() throws SerDeException {
// TODO Auto-generated method stub
return null;
}

@Override
public void initialize(Configuration conf, Properties props)
throws SerDeException {
// TODO Auto-generated method stub

}

}

三个函数作用分别是:

  • initialize:在启动时调用,根据运行时参数调整行为或者分配资源。
  • getObjectInspector:返回字段定义名称和类型。
  • deserialize:对每一行数据进行反序列化,返回结果。

定义表结构

在我们这个例子中,字段是固定的含义,不需要在initialize方法配置运行期参数。我们把字段的定义写成static,如下。

private static List structFieldNames = new ArrayList();

private static List structFieldObjectInspectors = new ArrayList();
static {
structFieldNames.add("time");
structFieldObjectInspectors.add(ObjectInspectorFactory
.getReflectionObjectInspector(Long.TYPE, ObjectInspectorOptions.JAVA));

structFieldNames.add("id");
structFieldObjectInspectors.add(ObjectInspectorFactory
.getReflectionObjectInspector(
java.lang.Integer.TYPE, ObjectInspectorOptions.JAVA));

structFieldNames.add("adv");
structFieldObjectInspectors.add(ObjectInspectorFactory
.getStandardListObjectInspector(
ObjectInspectorFactory.getReflectionObjectInspector(
String.class, ObjectInspectorOptions.JAVA)));

structFieldNames.add("verb");
structFieldObjectInspectors
.add(ObjectInspectorFactory.getReflectionObjectInspector(
String.class, ObjectInspectorOptions.JAVA));

structFieldNames.add("obj");
structFieldObjectInspectors
.add(ObjectInspectorFactory.getReflectionObjectInspector(
String.class, ObjectInspectorOptions.JAVA));
}

@Override
public ObjectInspector getObjectInspector() throws SerDeException {
return ObjectInspectorFactory.getStandardStructObjectInspector(
structFieldNames, structFieldObjectInspectors);
}

定义解析函数

为了能够让Java MapReduce任务复用代码,我们在外部实现了一个与Hive无关的类,这里不再贴代码。这个类定义了与日志字段相同的成员变量,并且提供一个static的valueOf方法用于从字符串构造自己。

@Override
public Object deserialize(Writable blob) throws SerDeException {
if (blob instanceof Text) {
String line = ((Text) blob).toString();
RawAction act = RawAction.valueOf(line);
List result = new ArrayList();
if (act == null)
return null;
result.add(act.getTime());
result.add(act.getUserId());
result.add(act.getAdv());
result.add(act.getVerb());
result.add(act.getObj());
return result;
}
return null;
}

建表

把上面程序编译并传到hive部署目录后,进入hive:

$ ./hive --auxpath /home/bochun.bai/dp-base-1.0-SNAPSHOT.jar


hive> CREATE TABLE ac_raw ROW FORMAT SERDE 'com.renren.dp.hive.RawActionDeserializer';
OK
Time taken: 0.117 seconds
hive> DESC ac_raw;
OK
time bigint from deserializer
id int from deserializer
adv array from deserializer
verb string from deserializer
obj string from deserializer
Time taken: 0.145 seconds


hive> LOAD DATA INPATH '/user/bochun.bai/hivedemo/raw_action' OVERWRITE INTO TABLE ac_raw;
Loading data to table default.ac_raw
Deleted hdfs://NAMENODE/user/bochun.bai/warehouse/ac_raw
OK
Time taken: 0.173 seconds


hive> SELECT count(1) FROM ac_raw;
…...显示很多MapReduce进度之后......
OK
332
Time taken: 15.404 seconds


hive> SELECT count(1) as cnt, verb FROM ac_raw GROUP BY verb;
…...显示很多MapReduce进度之后......
OK
4 ADD_FOOTPRINT
1 REPLY
24 SHARE_BLOG
299 VISIT
4 add_like
Time taken: 15.242 seconds

技术和故障

这是写给内部同事的日志,也有部分概念有通用意义,就不加密了。

做技术开发的人知道,只要写代码就一定会出错,叫bug。
有的错误在上线之前没有检查出来,直到被用户使用了我们才知道。这种bug就是故障。
出现故障是再正常不过的事了,我认为处理过程,可以让故障成为宝贵的财富。

我设计的故障处理流程,分为三个阶段:反馈、处理、反思。
1 反馈阶段
说实话,大部分的故障是类似的。反馈阶段就是要把各种用户描述归一成为同一个技术问题。
这个阶段具体还要分成“用户反馈”,“已沟通”,“技术已确认”,三个状态。分别由“客服”和“技术经理”操作。
2 处理阶段
这个阶段就是写代码的阶段,把造成故障的bug修复。技术都懂得怎么做。
这个阶段具体还要分成“已分派”和“线上已修复”两个状态。这两个状态的执行者分别是“技术经理”和“测试经理”。
换句话说,一线的工程师编码的工作,是在“已分派”状态进行的。然后交给OP和QA。
3 反思阶段
这个阶段是最核心,同时是最欠缺的阶段。
这个阶段具体分成“已确认解决方案”和“已完成解决方案”两个状态。
所谓“解决方案”,一定是切中要害的解决,并且可以保证类似问题可以避免再次发生。
“解决方案”和“修复BUG”的区别在于有没有反思发生的根源。

整个流程的设计,核心目的是自我进化。只要持续犯错,再避免重复犯错,最终一定是伟大的团队。

[Updated] Hive for Hadoop 0.21.0

终于要升级0.21.0了,目前最难的是Hive-0.7.0与Hadoop-0.21兼容问题。

这里有一个我修改过的Hive,可以在0.21运行:
http://sinofool.com/hive-0.7.0-r1043843.tar.bz2
MD5SUM: 8adb62c176b203b9d3cf5edc5d37b375
代码在:HIVE-1612

P.S. 这个版本有点儿旧,修改的时间是2010年11月10日,比官方的0.7.0落后一些。

UPDATED:

最新的2011年5月20日代码,还是用上面的patch编译的,基本可用。

去掉了HBase的支持,因为HBase本身也还不支持0.21.0。接下来解决HBase的兼容问题,再去jira里面提交。

hive-0.8.0-SNAPSHOT-r1125002-bin.tar.gz
MD5SUM: 7a48b50d375aae5ee69cd42dbd7bdd16

UPDATED:
最新的2011年5月23日代码,同上。
hive-0.8.0-SNAPSHOT-r1127826-bin.tar.gz
MD5SUM: 9dd4cb9d850894353a18df399b8c7b53

Hadoop reduce 慢

又一次从博客流量来源上看到一组有意思的词:hadoop reduce 慢。
我试着搜了一下,结果没找到自己的文章排在哪。

言归正传,慢真是个大问题!

首先是技术问题,也是最容易解决的问题,调参数。
我看到有人在网上问,说WordCount都慢,那就是环境问题了。调HeapSize,调GC参数,调TaskSlot,再不行加加机器。总是能解决的。

其实我更想说非技术问题,很多人误把hadoop妖魔化了,什么都往上套,一定会慢。
所谓快慢是要对比的,要么是跟旧系统比,要么是跟心理预期比。
如果你有旧系统,也是分布式,也是大数据量,写的并不太差,那hadoop是一定慢的。Hadoop能够带来的更多是开发效率的提高。
如果没有旧系统,比心理预期慢,那就必须先拷问一下自己凭什么预期它快。
还有就是,reduce天生就比map慢,这个不能比。

我们遇到过很多挫折:
在reduce的时候做矩阵运算,肯定快不起来。
在reduce输入和map一样的数据量,因为reduce个数少,肯定快不起来。
在map输出某个特殊的key,数据量不平衡,那某个reducer肯定快不起来。
在繁忙的机器上运行,也一定快不起来。

在遇到问题时,我会去调查:
Reduce要从网络读取多少数据。
排序能不能在内存完成。
Reduce有没有占很多内存。

Hadoop现在的名气大,能力相对没有那么多。盲目选择有风险,须谨慎。

休假有感

结束了连续11天的假期,团队运转良好,没有发生技术故障,我很高兴。

作为一名工程师,我被遗忘了,很好!总出事别人才会记住你,总延期别人就会担心你,总更包别人就会关注你,可是我认为技术进步到一个阶段,就可以被别人忘记了。尽量早的做优化,尽量多的留余量,尽量高层次的设计接口,就可以做到。
作为一名管理者,我没有授权充分。对大家OA审批的不够及时,对新服务器资源的分配没有执行,对报警跟踪的推进没有坚持。回去第一天都要补上。

五月我想做一些改变,让工程师都和我一样解放出来,被别人遗忘。

京ICP备06058813号  京ICP备13006450号