时间飞快,清晰的记得2010年末写总结的情形,转眼间,一年的时间又过去,我需要坐下来回顾下2010年写给2011年的计划的完成情况,顺便给我的2012年列些预期了:

2010年总结的时候曾写到:《2010年总结,2011年展望
1. eoe能健康发展,成为这次移动互联网浪潮的android方面的赢家!
回顾:2011年是eoe的发展年,我们从小到大,逐步成长,2011年初的时候我们还在西二旗的辉煌国际,20来个人,没什么品牌,没多少收入。2011年的eoe成长了不少,我们从20多人扩展到60多人,我们从商住两用的200平的办公室搬到了凯旋中心正规的400多平的办公室,我们自己的产品按照自己的规划和节奏逐步完善;我们有了自己的线下沙龙,扩展到上海,广州等地,初步形成我们自己的品牌和口碑。我们有了一些收入,我们有了发展壮大的资源,我们的团队更爱自己的工作,更爱eoe的氛围。现在说输赢都还为时尚早,我们正在前进的路上~

2. 能做一个有可能改变世界的产品;
回顾:主导了一个产品,按照既定的计划和方向,这个产品还在完善中,何时能腾飞成为明星还不得而知,但是我们磨练了团队,摸索前进了大半年,我们对这个方向坚定不移,我们还在等待机会,我们需要一些时间,2012将是看到我们成果的时间~

3. tina能找到自己真正喜欢的工作,做自己喜欢的事情;
回顾:tina在2011年的上半年是折腾的,尝试了好几家公司和工作,在下半年找到适合自己的工作,也是个很不错的德国企业,这个算是达成了~

4. 在技术,英语,管理,交际方面有着长足的提高;
回顾:这个目标不符合swot规则,无法鉴定完成的怎么样了,大概说说感受。2011年的技术只在架构方面有所长进,英语还是不过关,管理学会了系统化看问题和分解问题,懂得了一点点的管理,交际好像改观不大,客串了几次活动主持人,现在上台完全不紧张了,算是进步不?

5. 能找到一个合适的定居的地方为安定下来做些准备;
回顾:这个不及格,虽然有了一个自己的小窝,但是依然还在北京漂着,还是没有找到理想的定居地点,还是不喜欢北京的天气和氛围,还是希望能找一个南边的地方定居,我喜欢温暖的,开放的,年轻的城市,哪个才是我理想的定居城市,继续寻找中~

6. 能和tina一起去一次远途旅行,欧洲?也许吧;
回顾:2011年和tina出去转了两次,10.1日去了宁夏的银川和中卫,第一次去了祖国的大西北,看到了戈壁和沙漠,体验了大西北的风土人情。然后在2011年圣诞节前夕去了新加坡,没能去成欧洲,就选择去了新加坡,很喜欢新加坡这个城市,干净整齐,气候温暖清新~

7.能拿到驾照,顺便也有个属于自己的交通工具;
回顾:开始学驾照了,交规考了100分,但是还没来得及练车,也就没能拿到驾照,更没有自己的交通工具,看来还的抓紧了~

8. 多些空闲,多看看书,多些思考,多些时间写写东西!
回顾:2011年的下半年时间多了一些,不用天天救火和打杂了,看了一些感兴趣的书,比如《怪诞行为学》,《异类》,《暗时间》,《裂变》,《未来是湿的》,《jobs传》以及一堆技术,产品和交互设计的书籍,看的多,但是写的不多,在weibo上絮叨了3000多条,但是不系统,都是零碎散落的,2012年会多写读后感,多些文字纪录的;

9. 亲戚,朋友包括我和tina都健康快乐!
回顾:2011年是平安的,自己,家人,亲戚,朋友都健康无恙,谢谢上苍;2012年1月的适合,90岁的奶奶在她生日的那天安详的离去了,天堂更加美好,我们会想念您的。

10. 世界太平,和谐迎接2012!
回顾:2011年世界不太平静,地震,火山,核泄漏,战争,饥荒,瘟疫,空气污染还是遍布世界,这是个伤痕累累的地球,能否挺过2012,深表担忧!

2011年已经成为过去时,过去的2011年波澜不惊,虽有过压力,也有过迷茫,但是始终没摇摆的是一颗勇敢的心,有家庭的支持和朋友的鼓励,我们顺利的上岸了,归总一句话:2011年没有浪费,是满负荷的,每天也都是新的,每天都有新收获~

2012年来的有点突然,有点匆忙,还有点蛮横,一不留神,2012已经来到面前,2012年最终会是怎样还不得而知,现在的我至少可以列举一些预期,权当自勉~

2012年的预期如下:
工作
1. eoe再上一个大台阶,产品和品牌在业界都能排在第一梯队,公司和团队能健康发展;
2. 主导的x产品的推出和扩大影响力,达到预期水平,可以在一个领域成为领头羊;
3. 规划布局和管理能力可以有沉淀,有积累;
4. 外语能有长足的进步,可以听说流畅;
5. 理解产品和流量运营,新媒体等社会化营销的相关理论和操作方法;

兴趣爱好
1. 系统学习摄影,有一台单反;
2. 主导或者贡献一个开源产品或项目;
3. 在一个计算机技能的新领域有所突破;
4. 策划并出版至少一本新书;
5. 多看好书和电影,多些分享和总结,多写blog;

生活
1. 拿到驾照,有自己的交通工具;
2. 感情甜蜜,并开始实施定居和下一代计划;
3. 和tina一起去至少两个地方旅行,草原和欧洲;
4. 常回家看看,多些时间陪陪他们
5. 自己,tina,家人,朋友都健康快乐度过2012;

希望2012不是世界末日,希望我还有机会写2012年的年度回顾和2013年预期~

这里纪录一些不常用但很有用的linux命令

#通过端口号找到进程(13543这个就是进程号)
[root@ lab]# netstat -lnp | grep 9000
tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN 13543/unicorn.rb -D

前几天遇到一个很诡异的情况,昨天终于找到根本原因并解决了,我觉得有必要把这个过程纪录下,应该会有人遇到类似的问题。

前几天在一个坛子里问“每天500w条log如何入mysql库比较靠谱”,需求是

我们有个产品,每天差不多500w的业务量,需要对500w做相关的分析,就需要入库~
目前用的是mysql 按月切表定期老化数据,但是导入数据情况比较糟糕~每天导入数据差不多需要20个小时,和MySQL 5000 records/second差距巨大~
尝试过三个方案:
1. 定期解析日志,直接入库;
2. 定期解析日志,生成sql,再直接sql入库;
3. 生成data文件,load到库里
但是效果都不理想~ 没什么大数据处理的经验,大家支招~~~

其中第一种方案基本上被抛弃了,采用第二种方案,但是还是很糟糕,500w的数据差不多需要10来个小时,还是无法接受,于是仔细剖析了下过程:

1. 准备sql文件
sqlFileName = RAILS_ROOT + “/log/insert.sql.” + LOG_POSTFIX_FOR_YESTERDAY
sqlfile = File.open(sqlFileName, “w”)
2. 打开日志文件
File.open(“../log/apps.log.#{LOG_POSTFIX_FOR_YESTERDAY}”).each do |line|
3.提取需要的数据
id, apk_id, ip, client_id, channel_id, uniquely_code ,track = line.split(” : “)[1].split(“,”)
4. 生成sql串
sqlInsert = “INSERT logs_downloads…..balalaalala
5. 写到sql文件里
sqlfile.puts( sqlInsert )
6. 调mysql直接导入sql
里面还有一些数据的清洗和排重工作~
每天的日志文件差不多500w行,解析生成sql的过程不理想(其中没用数据库操作)~ 难道是我磁盘性能很糟糕~~

过程看到基本上都没问题,无外乎解析文件,写文件的操作,不应该慢成这样的,最后把目光放在了数据的清洗和排重工作种,这个过程是想在入库的时候就把一些虚假的量排除掉,于是我们自己设计了一个算法来过滤,算法的逻辑很简单,根据一些参赛md5出一个key,然后把这个key丢在Array中,然后用include?来判断这个key是否已经存在,问题就出在这里!

ruby的Array中的include?是需要很小心的,我之前想当然的认为这个方法应该很快,也没仔细看就用了,结果发现问题就在这个方法上,直接上下源码看看:

当看到:

for (i=0; i #blalalal
}

这个的时候我就明白了,果然,这个方法直接遍历Array,然后你可以想想当我有个500w大小的Array的时候,再用include?是何等的情景啦~
修改方案很容易了,改用hash就好了,不多说~~

时间很快,转眼间走完了2011年,正在感谢2011年亲戚朋友都健康的时候,奶奶即将迎来90高寿。
然前天晚上家里电话说奶奶住院,有点不舒服,应该无大碍。夜里睡觉不能安心,几次惊醒,就觉得不对劲了。
果不其然,昨天早上家里来电,奶奶病危,出院回家了,让我们准备回来,准备归的时候又接电话说奶奶回到家了,状况好转。昨天是奶奶90岁生日,我们都觉得挺过这个坎应该就好了,默默祈祷,快到夜里12点的时候,我们都觉得应该过去了。
但是,最后10多分钟还是没能走完,奶奶离开了我们,在她90岁生日的时候,奶奶没有受苦,是安详的离去的,千里奔袭,一路走一路默默流泪,回去送奶奶最后一程。

晚上心血来潮整理起自己的博客,从2007年开始到现在,快900篇文章,慢慢的整理,记忆一点点的浮现(工作,深圳,北京,创业,成长),发现博客的记忆是片段的有思想的,微博则是零碎的不成体系的废话,为了记忆,以后还是多写点博客~

1.文章分类汇总

 

从分类上看,过去几年更多的积累还是在技术上,ruby,mac,android和security还是比较多(杂七杂八的分类是中间迁移的时候分类信息丢了的缘故,里面也基本上技术的文字),产品分类是才加上的,现在技术做的比较少了,产品关注的多一些,以后会有更多关于产品相关的~

2. 按月归档

看看之前写的纪录,2007到2010年都还算蛮规律的,基本上每个月都会有数量不等的文章~而2011年开始看到写的急剧减少,一个是因为weibo的出现,平日的只言片语都丢weibo上了;二是懒了,写不出大段的文字~


今天晚上仔细把模板修葺了一下,以后会多三个方面的内容,一个是前面说到的产品,产品是现在看的比较多的;另外一个是和创业相关的文字,创业两年来,我想是有些东西可以分享的;第三个是旅行相关的,本来这个博客是定位技术的,生活会少一些的,后面会写一些和自己的旅行,成长相关的内容~

使用过nginx的应该都都晓得upstream,前面一篇文章说到《nginx upstream的5种配置方式》,例如如下是很常见的一段配置

#create by ice 2011.12.22
upstream unicon_v2_www {
# This is the socket we configured in unicorn.rb
server unix:/var/www/v2/tmp/sockets/unicorn.sock backup;
server 192.168.0.1:5000 weight=9 max_fails=2 fail_timeout=30s;
server 192.168.0.2:5000 weight=3 max_fails=2 fail_timeout=30s;
}
server {
listen 80;
server_name www.iceskysl.com;
error_log /opt/nginx/logs/error/error_v2_www.log;
access_log /opt/nginx/logs/access/access_v2_www.log main;

root /var/www/v2/public/;
index index.html index.htm;

location / {
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header Content-Length $content_length;
proxy_set_header X-Forwarded-For $remote_addr;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
chunked_transfer_encoding off;
proxy_pass http://unicon_v2_www/;
}
}

其中upstream的这段配置:
upstream unicon_v2_www {
# This is the socket we configured in unicorn.rb
server unix:/var/www/v2/tmp/sockets/unicorn.sock backup;
server 192.168.0.1:5000 weight=9 max_fails=2 fail_timeout=30s;
server 192.168.0.2:5000 weight=3 max_fails=2 fail_timeout=30s;
}

我们了解到socket模式的这个是backup,其他两个机器分别weight=3和weight=9,于是我们想监控下upstream每个后端具体处理了多少请求,有没有问题等,这个好像就不好监控了,nginx自带的status模块只能监控总的请求数,无法区分每个upstream的具体情况。
找到一个ustats(http://code.google.com/p/ustats/),其介绍是:NGINX basic upstream statistics module,具体的如下:

USTATS module provides basic statistics for each backend in nginx upstreams:

Number of requests
Http 499/500/503 errors count
Tcp errors
Http read/write timeouts
Fail timeout
Max fails count
Last failed access time
Total fails count
Blacklisted backend highlighting
Down backends highlighting
The module allows you to sort values in some columns for each separate upstream. The data can be retrieved as JSON by appending “?json” to the end of location on which the module was set to work on (see configuration instructions below).

看上去正是我需要的,有时间试试看~(为啥nginx不学haproxy官方自带一个详细statistics的页面呢~haproxy那个statistics很详细很好用~)
详细使用指南:http://code.google.com/p/ustats/

nginx的upstream目前支持5种方式的分配

1、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。

2、weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
例如:
upstream bakend {
server 192.168.0.14 weight=10;
server 192.168.0.15 weight=10;
}

3、ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
例如:
upstream bakend {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}

4、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend {
server server1;
server server2;
fair;
}

4、url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法

upstream backend {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}

tips:
upstream bakend{#定义负载均衡设备的Ip及设备状态
ip_hash;
server 127.0.0.1:9090 down;
server 127.0.0.1:8080 weight=2;
server 127.0.0.1:6060;
server 127.0.0.1:7070 backup;
}

在需要使用负载均衡的server中增加
proxy_pass http://bakend/;

每个设备的状态设置为:

1.down 表示单前的server暂时不参与负载
2.weight 默认为1.weight越大,负载的权重就越大。
3.max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误
4.fail_timeout:max_fails次失败后,暂停的时间。
5.backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。

nginx支持同时设置多组的负载均衡,用来给不用的server来使用。

client_body_in_file_only 设置为On 可以讲client post过来的数据记录到文件中用来做debug
client_body_temp_path 设置记录文件的目录 可以设置最多3层目录
location 对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡

参考:

http://wiki.nginx.org/HttpUpstreamModule

http://www.pagefault.info/?p=251

前面一篇文章《防范垃圾利器Akismet使用体验和原理分析》讲到Akismet的原理,其中说到看谁有兴趣给dz加个类似的功能,我是懒得写php的code,写写rails的还有点兴趣,于是在android-group这个项目里添加了下,android-group是直接fork的ruby-china,把大概过程说下:

1. 加插件ruby-akismet
已经有人写了akismet的ruby插件https://github.com/joshfrench/rakismet,有有人封装了一次https://github.com/ysbaddaden/ruby-akismet,就很好使用了。
#Add this gem to your Gemfile:
gem ‘ruby-akismet’, :require => ‘akismet’

#Create an initializer file like config/initializers/akismet.rb with your configuration:
Akismet.key = ’123456789′
Akismet.blog = ‘http://example.com’
Akismet.logger = Rails.logger

2. 修改代码
#修改Reply.rb,添加
field :spam, :type => Boolean, :default => false

#修改RepliesController

# coding: utf-8
class RepliesController < ApplicationController

load_and_authorize_resource :reply

before_filter :find_topic
def create

@reply = @topic.replies.build(params[:reply])

@reply.user_id = current_user.id
@reply.spam = Akismet.spam?(akismet_attributes, request)
logger.info("akismet_attributes:#{akismet_attributes}")
logger.info("@reply.spam:#{@reply.spam}")
if @reply.save
current_user.read_topic(@topic)
@msg = t("topics.reply_success")
else
@msg = @reply.errors.full_messages.join("
")
end
end

def edit
@reply = current_user.replies.find(params[:id])
drop_breadcrumb(t("menu.topics"), topics_path)
drop_breadcrumb t("reply.edit_reply")
end

def update
@reply = current_user.replies.find(params[:id])

if @reply.update_attributes(params[:reply])
redirect_to(topic_path(@reply.topic_id), :notice => '回帖更新成功.')
else
render :action => "edit"
end
end

protected

def akismet_attributes
{
:comment_author => @reply.user.login,
:comment_author_url => user_url(@reply.user.login),
:comment_author_email => @reply.user.email,
:comment_content => @reply.body,
:permalink => topic_url(@reply.topic_id)
}
end

def find_topic
@topic = Topic.find(params[:topic_id])
end

end

#修改Cpanel::RepliesController,增加如下两个方法

def spam
@reply = Reply.find(params[:id])
@reply.update_attribute(:spam, true)
Akismet.submit_spam(akismet_attributes)
redirect_to(cpanel_replies_path)
end

def ham
@reply = Reply.find(params[:id])
@reply.update_attribute(:spam, true)
Akismet.submit_ham(akismet_attributes)
redirect_to(cpanel_replies_path)
end

#修改routes文件

resources :replies do
member do
post :spam
post :ham
end
end

#再修改下view和locales文件就可以了,具体的就不贴了,可以在github上看到

https://github.com/IceskYsl/android-group/commit/79fabb06b12fd776b301fed2c7c10aee744c5974

也可以参考:

http://rubydoc.info/github/ysbaddaden/ruby-akismet/master/frames

项目地址:

https://github.com/huacnlee/ruby-china

https://github.com/IceskYsl/android-group

博客不经常更新的原因是垃圾太猛烈,每次来要删除一批垃圾评论,而且wp后台的那个未审核评论变成垃圾评论还真是麻烦,每次都直接跑去查数据库

select count(*),comment_approved from ice_comments group by comment_approved;
+----------+------------------+
| count(*) | comment_approved |
+----------+------------------+
| 5709 | 0 |
| 579 | 1 |
| 71 | spam |
+----------+------------------+

看到那个comment_approved是0 的就是还没审核的,一个一个审核实在是麻烦的不行~~
找到一个很多人不错的插件 Akismet(Automattic Kismet)是应用广泛的一个垃圾留言过滤系统,其作者是大名鼎鼎的WordPress创始人Matt Mullenweg,Akismet也是WordPress默认安装的插件,其使用非常广泛,设计目标便是帮助博客网站来过滤留言spam。
开启后回自动把comment_approved是0的检查一遍,看看我的

mysql> select count(*),comment_approved from ice_comments group by comment_approved;
+----------+------------------+
| count(*) | comment_approved |
+----------+------------------+
| 5214 | 0 |
| 579 | 1 |
| 566 | spam |
+----------+------------------+
3 rows in set (0.00 sec)

慢慢自动检查去吧,希望这个能把垃圾评论都找出来~~~

说到这里,对Akismet的工作原理有了更多兴趣,顺手查了下:当一名用户把一条评论列为垃圾评论时,该数据会自动上传到Akismet的服务器上,以方便其他用户的Akismet自动拦截垃圾评论,因此Akismet可以说是广大Akismet用户共同维护的评论防火墙。
大概的流程估计是这样的:

1、所有评论首先都会传送一份到akismet服务器进行判断。
2、akismet收集到了名字,网址,邮箱,和内容,ip,5个数据,其中名字应该不可能用作判断依据。
3、akismet有一个信任名单,中立名单,黑名单几个级别。
4、每当博主对评论手动通过一次,该评论的网址,邮箱,ip就获得一次加权。当积累到一定量之后进入信任白名单,所发评论能自动通过。
5、当博主对评论进行一次垃圾标记,该评论的网址,邮箱,和内容,ip就获得一次负加权,当积累到一定量之后进入黑名单。

如上过程应该有个机器学习的过程~加上一些算法和挖掘的原理,应该是可以实现的,其压力应该在服务器负载上,想全球那么多的wp程序(还有些其他网站接入Akismet的),每次评论的时候一个请求,这个量不得了呀~ 所以我估计Akismet的处理逻辑应该也是分层级的,比如说一个ip已经进入黑名单了,那么他的处理应该很简单了,如果不是黑名单,估计还回根据内容等其他信息判断~~

于是乎,我想到eoeandroid的管理员和版主每天和spam抗争的惨烈情景,不晓得Discuz论坛是否有Akismet插件,如果没有,谁有兴趣可以开发一个,大体思路如下:

1.使用Akismet提供的API对信息进行检测,地址 http://akismet.com/development/api/
2.对信息的检测只针对特定的用户组,比如“新手上路”、分低于200或者发贴量少于50个帖子的用户,做此限制主要是为了不影响论坛的忠实支持者的操作;
3.用户提交的内容检测为Span时,阻止发贴、回复的操作,并提示用户,并记录用户提交垃圾信息的数次,当达到一定程序时将此用户禁言一定时间;
4.相关参数在后台实现设置接口。
5.在Discuz管理后台设置API Key,并检测API Key的有效性。

查了下,好像没看到Discuz有这个插件,谁有兴趣开发一个~~

This module provides a way to process XML documents. It is build on top of XML::Parser. XML::Twig offers a tree interface to the document, while allowing you to output the parts of it that have been completely processed. It allows minimal resource (CPU and memory) usage by building the tree only for the parts of the documents that need actual processing, through the use of the twig_roots and twig_print_outside_roots options.(as pointed by yum info)

Let’s first search for the package
$ yum search perl-XML-Twig
perl-XML-Twig.noarch : A perl module for processing huge XML documents in tree

Note that you must be the root/administrator to install the package. To install this package in Red Hat/Fedora Linux.
$ yum install perl-XML-Twig.noarch

You can check the details of this package perl-XML-Twig
$ yum info perl-XML-Twig.noarch

或者:
perl -MCPAN -e ‘install XML::Twig’

姚尚朗, 网名IceskYsl, 简称Ice, 80后, 典型巨蟹男, 移动互联网创业者; Google产品重度依赖者, Mac, Android, Iphone, BBer非典型用户;关注创新,技术,产品和一切新奇的玩意儿;
求学武汉, 毕业南下深圳, 尔后北漂在京, 至今数年有余; 追寻内心的想法, 不随波逐流, 爱折腾, 爱旅行, 孩子气, 享受工作, 安静的做喜欢的事情...

已屏蔽的垃圾评论