Monday, February 1, 2010

22岁,人生第一个十万....

05年大一,看着宿舍楼下"IT培训成就十万年薪"的商业广告,我嘴角的微笑告诉别人,我的不屑.作为ACM教练的我,觉得这个数字很容易得到...

07年大三,退学的我,为了生计每日奔波,做着一个普通的埃踢民工,会因为每月10号多出来的2k而欢呼不已,会在周末的时候带着兜兜出去"吃大餐",那年我看到一套房子的首付也不需要十万...

09年正式工作刚好一年,出来混社会差不多两年,我每日在忙碌工作之余,常常在想啥时候能年薪十万丫,兜兜告诉我不急,生活的意义不在于此...另一方面我开始留恋数码新品,开始讲究生活质量,却没有为兜兜做过什么让她可以放下担心的事情....这一年,我的东西多了,兜兜的没变多少

10年2月1日,财务的变动让我开始关心今年自己的总收入到底有多少,在网银的总计一栏我赫然发现那个数字超过了十万,而我似乎开始麻木了...

再过几个小时就是我23岁的生日,十万,一个曾经的梦想对我来说承载了太多的回忆和承诺.比这些更重要的是 感谢兜兜从那个时候开始就一直在我身边,无论是几平米的小屋,抑或是下雪天去雪地里刨食,不论是我赋闲待业,也不论我是连续加班,不离不弃.而我欠她的太多太多...
如果说男人天生需要奋斗,那么世俗的金钱和权利即是一座座里程碑,那么这样一个开始或许意味着前方将有更难走的路和更难爬的山,但是这些之后的风光是否更加美丽,更加绚丽?

默默许下人生第二个心愿,希望在这个路标之后,到达下一个真正意义上的里程碑的时候,我可以看到兜兜在我身边幸福的微笑.

Tuesday, January 5, 2010

使用awk处理网站访问日志[上]

对于一个程序来说,Loging是一件非常有利的武器,其可以帮助程序员快速的找到BUG,分析性能瓶颈等等...甚至还可以在技术社区彰显一番代码的华丽,实乃死coder居家旅行必备之宝.而网站访问日志除了对开发者有修改缺陷,提升功力之良效以外,对待运营人员,也是分析用户行为的第一手宝贵资料.

本博打算通过若干系列文章来讲讲如何有效运用访问日志来改善程序和用户行为分析:)也就是说:站在程序员的角度来审视程序的运行状态;通过数字手段来分析用户访问状况

既然一切都从访问日志开始,那么首先就来parser weblog吧.让数据变得更加可读些,嘿嘿,自然也更方便处理些.
这里的日志格式如下:
124.205.30.210 - - [14/Dec/2009:16:02:46 +0800] "GET /HLRelationLog/ent.hunantv.com/y/l/20090306/225172.html H
TTP/1.1" 404 252
124.205.30.210 - - [14/Dec/2009:16:04:51 +0800] "GET /HLRelationLog/ent.hunantv.com/y/l/20090901/410652.html H
TTP/1.1" 404 252
211.152.32.122 - - [14/Dec/2009:16:15:10 +0800] "GET /manager/html HTTP/1.1" 404 210
124.205.30.210 - - [14/Dec/2009:17:02:18 +0800] "GET / HTTP/1.1" 200 44
124.205.30.210 - - [14/Dec/2009:17:02:19 +0800] "GET /favicon.ico HTTP/1.1" 404 209
124.205.30.210 - - [14/Dec/2009:17:24:59 +0800] "GET /HLRelationLog/ent.hunantv.com/x/20090616/331092.html"

格式很简单:来路IP,本地时间,以及访问的路径.实际情况下往往还有一个User-Agent.但是在本篇我们无视这个参数.先将这些数据"取"出来存入数据库再说.

awk -F ' ' '/HLRelationLog/ {printf("insert into logs (url,ip,time) values (\"%s\",\"%s\",\"%s\");",$7,$1,substr($4,2))}' access.log > target.sql

然后将数据导入
mysql -u root -p -h 8.8.8.8 < target.sql

这里简单再来看看大致的访问情况:
awk -F ' ' '/HLRelationLog/ {print $7}' access.log |sort|uniq -c|sort -nr > sort.txt

结果摘抄如下:
 13102 /HLRelationLog/ent.hunantv.com/e/h/20080917/49514.html
   7112 /HLRelationLog/ent.hunantv.com/e/h/20080719/23308.html
   6539 /HLRelationLog/ent.hunantv.com/m/20091231/536520.html
   6451 /HLRelationLog/ent.hunantv.com/m/20090427/281233.html
   5739 /HLRelationLog/ent.hunantv.com/m/20091222/527487.html
   5248 /HLRelationLog/ent.hunantv.com/x/20091224/529517.html
   4863 /HLRelationLog/ent.hunantv.com/y/20091219/524512.html
   4597 /HLRelationLog/ent.hunantv.com/m/20091221/526802.html
   4168 /HLRelationLog/ent.hunantv.com/m/20091221/526782.html
   4162 /HLRelationLog/ent.hunantv.com/y/20081028/87956.html
   3951 /HLRelationLog/ent.hunantv.com/m/20091222/527342.html
   3729 /HLRelationLog/ent.hunantv.com/e/20080716/22250.html
   3631 /HLRelationLog/ent.hunantv.com/e/h/20080728/26581.html
   3561 /HLRelationLog/ent.hunantv.com/m/20091222/527785.html
   3372 /HLRelationLog/ent.hunantv.com/e/h/20080903/42076.html
   3339 /HLRelationLog/ent.hunantv.com/y/20091216/522056.html
   3163 /HLRelationLog/ent.hunantv.com/y/l/20091221/525879.html
   2998 /HLRelationLog/ent.hunantv.com/y/20091216/521082.html
   2914 /HLRelationLog/ent.hunantv.com/x/20091221/526583.html
   2889 /HLRelationLog/ent.hunantv.com/y/20081231/154998.html
   2851 /HLRelationLog/ent.hunantv.com/z/20091225/530766.html

下面来处理下频道的访问分布,每个link的倒数第三段[x,y,e...]:首先通过awk得到每个link的频道的识别符和访问数量,丢进一个文件

map(lambda x:[x,sum(map(lambda y:int(y[1]),filter(lambda z:z[0]==x,t)))],set(map(lambda x:x[0],t)))
>>>
[['x\n', 48173], ['y\n', 266146], ['d\n', 4035], ['z\n', 40631], ['e\n', 98009], ['ent\n', 558], ['m\n', 174542], ['t\n', 59660]]

来看看并发的情况:
tail -10000 access.log | awk '{print $4;}' | sort | uniq -c | sort -nr | head

=====我是分割线====

33 [31/Dec/2009:22:03:18
     16 [25/Dec/2009:18:16:07
     14 [25/Dec/2009:18:16:06
      9 [31/Dec/2009:22:03:19
      8 [25/Dec/2009:18:13:35
      7 [31/Dec/2009:22:06:38
      7 [31/Dec/2009:19:22:40
      7 [25/Dec/2009:19:55:54
      7 [25/Dec/2009:14:39:06
      7 [02/Jan/2010:18:37:54

Sunday, December 13, 2009

[宅男学厨]三明治

今年出去买了鸡蛋和煎锅:)瞅着手边的材料,除了上次还没用完的土司就还有番茄和奶酪等.那么就做个简单的三明治吧.

将土司去边,从对角线切开,奶酪也如此对切。

去鸡蛋一枚,入碗打散,加入少许盐.将之前的土司夹奶酪浸入碗中,三边开口处都要粘满蛋汁哦.

将土司丢入煎锅,先用中火炸四边,待封口后,转用小火炸两边.至两面均酥皇,将剩余的鸡蛋汁全部交上去,片刻后起锅:)




Friday, December 11, 2009

[宅男学厨]煎蛋,培根与吐司

昨天晚上和March Liu交流了下做饭的心得,得其真传.今天决定从最简单的开始,打好基本功.给自己做一顿早餐.

看看手边的材料,似乎鸡蛋为最.在我系统分析了33种鸡蛋相关做法之后,我决定做史上最有名的......煎蛋吧.岂料杯具发生了,家里的鸡蛋被我放置的太久,竟然,竟然坏掉了很多(:

好不容易找到了一个能用的,打蛋装碗.

锅里放油,开火,以油熟且尚未冒烟为宜(否则,你的油就会...)好,就是此刻,将鸡蛋丢入锅中,待蛋白部分发白略黄的时候,在其周围加水若干滴(保证下形状,嘿嘿).加盐.翻转鸡蛋,煎另一面.起锅.



怎么样,第一次煎蛋还算不错吧:)

接着,将培根丢入锅中约2min,起锅,配合我事前准备的奶酪.


战斗结束,历时6min ;)之前在网上查看食谱的时候,见到一种做法:


两片厚片土司去边 一个鸡蛋加少许盐打散 一片或两片奶酪 
平底锅里放一点黄油熔化后 将去边土司加好奶酪片 放在鸡蛋液里沾一下 
四周都要沾到 夹起来沾好蛋的土司 在锅里 用中火煎 先煎四边 
再煎两面 然后转小火 将剩下的鸡蛋均匀的倒上去 如果没剩下鸡蛋 
就免了这一步了 之后用小火慢慢煎到两面金黄松脆 里面的奶酪半融化状
的时候 就能吃了 非常好吃 蛋香奶香麦香稍微有点淡淡的咸味

 
厨艺果然是创意无限.不知为啥我想起了Bitmap来:)

使用coLinux+Debian+Putty+Emacs构建快速开发环境

本博的笔记本比较古董,跑VirtualBox之流甚是吃力,更不用说Vmware这样的超级杀器.本来一直采用的是Msys来进行*nix的模拟的,不过由于某些软件包实在不给面子,害的我每次都得连接至公司的服务集群上进行测试,如此下来,多有不便:(

于是乎,经过一番爆狗,终于找到了coLinux这样的好东西.CoLinux是在Windows上能够运行的linux. 在Windows计算机上安装Linux的时候,可以不用追加新的硬盘,也不用重新做分区等工作。 如果使用coLinux的话,不重新安装Windows,不变更硬盘分区就可以很轻松地构筑Linux环境。

如果说Cygwin是在C库程序阶段模拟UNIX(在源码级别的互换性)的话,则coLinux是在能驱动真的Linux原核程序上,与Linux和应用程序具有互换性。即:Debian和Fedora能够直接运行。换句话说,coLinux就是一个 Linux 内核,它经过修改,以与另一个操作系统协作运行。主机操作系统(Windows 或 Linux)控制操作系统的物理资源,而访客(guest)操作系统(coLinux)获得硬件的虚拟抽象。主机操作系统必须提供以特权级别(ring 0)执行驱动程序的方法,并提供分配内存的方法.

接下来的事情就很容易了,猛击此处下载当前的coLinux的二进制版本,同时本博下载了列表下方的Debian5.0的压缩包.运行coLinux的安装程序,一路Next至Over(安装目录最好不要出现中文或空格).解压前述Debian的压缩文件至coLinux的老巢.接下来,可以使用Debian提供的BAT脚本直接执行了.

不过也许各位已经发现Debian里只有2GB左右的空间,而且貌似不能上网也没有开启sshd.接下来我们一步一步解决这些问题:

首先,再次猛击一下下,我们得到一个已经做好的4GB大小的分区文件(下载文件很小,只有4xKB的样子).解压丢至coLinux的基地去.

接着,在你的Debian里配置下第二块网卡(eth1):
allow-hotplug eth1
iface eth1 inet static
address 192.168.1.6 //根据实际情况,自行改变
gateway 192.168.1.1
netmask 255.255.255.0

这里贴下我的coLinux.conf文件
# The default kernel
kernel=vmlinux

# File contains the root file system.
# Download and extract preconfigured file from SF "Images for 2.6".
cobd0="Debian-5.0r2-lenny.ext3.2gb"

# Swap device, should be an empty file with 128..512MB.
cobd1="fs_root" //扩展的4GB文件

cofs0=d:\ //与Windows交互设置,这是coLinux自己的方式

root=/dev/cobd0

initrd=initrd.gz

# Slirp for internet connection (outgoing)
# Inside running coLinux configure eth0 with this static settings:
# ipaddress 10.0.2.15   broadcast  10.0.2.255   netmask 255.255.255.0
# gateway   10.0.2.2    nameserver 10.0.2.3
eth0=slirp //dhcp
# Tuntap as private network between guest and host on second linux device
eth1=tuntap //这里就是之前在系统里设置的eth1

启动进入系统,进行挂载测试

mkdir -p /mnt/ext
mount -t ext3 /dev/cobd1 /mnt/ext

如果可以访问的话,那么写入你的/etc/fstab.系统启动时会自动挂载

/dev/cobd1 /root ext3 defaults 0 1 //这里我用来扩展了/root,你自然也可以改成/home/xxx 不过记得拷贝文件

类似的,将windows交互目录挂载进来

mkdir -p /mnt/host
mount -t cofs cofs0 /mnt/host

接下来,重启系统,通过apt-get install ssh来打开sshd,选择putty登陆上去.编译Emacs之前记得先安装ncurses (:

展示下效果


这个黑黑的CMD窗口就是coLinux启动的Debian终端,如果觉着不爽,也可以用colinux-daemon把其做成系统服务.最后赞一下,coLinux的速度真的很快

Wednesday, December 2, 2009

基于REST风格构建WEB应用的实践反思

江湖上有着这样一则传闻:
面试官:"请问REST是虾米?"
面试者:"哦,您放心,我是永远不知道休息的..."

我想面试官的这个问题,本文的读者应该大体是有一个概念的.不过为了行文的方便,这里本博还是将REST的核心原则摘录于下:.

  • 为所有“资源”定义标识(URI)
  • 将所有资源链接在一起
  • 使用标准方法
  • 资源多重表述
  • 无状态通信

由于公司战略需要一款SNS产品,本博有幸参与了整个开发活动,并主持了架构设计和实现.因此得以实战REST,并将前后遇到的一些问题和思考记录下来,遂成此文.


那么为啥会考虑采用REST作为系统架构风格呢?

首先,由于是一款自己运营SNS产品,根据产品规划部门制定的需求,在技术上要求我们实现大量的用户行为记录,同时还要为用户提供包含博客、相册、视频在内的web2.0“标准”应用以及支持第三方植入应用的OpenAPI体系.因此系统在安全性,并发性,稳定性,扩展性等方面均有较高的要求.
其次,由于项目团队的人员配备,开发模式,开发周期等因素的影响,敏捷开发也成为一个潜在的要求.
针对这些客观实际情况,本博考察了主流SNS的技术方案,结合自身特定,决定采用REST来实施构建:

  1. 资源URI可以有效描述系统涉及的角色(用户,及其产生的行为结果),无论是在用户群体还是在单一个用户对象.
  2. 系统对外以URL形式(对内则是URI)来与客户端通讯,这也是负载均衡得以实施的前提
  3. URI(Entity,QueryString,Method)封装了编程实体,后端程序可以有效建立Resource-Object映射,配合Key-Value缓存以及ORM等技术手段优化,可以满足对伸缩性的要求.
  4. REST提倡的HTTP操作方法封装了数据操作的CRUD
  5. URI之间的聚合(combination)有效建立起一组可复用的API体系.


实践中采用REST风格来构建项目,较为显著的带来了两个方面的提升:技术层次上,团队内推广和普及了敏捷开发,并在实践中性能成一套符合自身情况的开发流程;在HTTP协议,Web服务器,键值缓存,开发语言等方面开拓了技术视野并形成了相应的技术积累.管理层次上,由于构建REST应用的需要,将人员合理分配成应用开发,API开发等不同的小组,责权分明.但是很显然,这一架构的引入不可能是一帆风顺的,在实践中我们也遇到了很多问题,很多场景下我们也不得不去破坏学术定义(REST Anti-Pattern REST反模式):

  • 什么是资源?
        这样一个基础的命题上,保持怎样一个粒度,对API的构建乃至整个系统至关重要.是按照"语义"级别,将用户定义为/user/uid这样,还是针对系统级别,将数据表结构定义为资源.在这个问题上,内部产生了很多的思考和争议.虽然我们最终采用了类似前者的思路,但是这只是根据当时需求情况的取舍,这样的定义来带的后果是对"简单数据"CRUD的极大便利;资源交叉高复用,原子操作性较强.但是对集合类似资源(用户组等)至少在概念层次上是无力的,往往需要引用大量的SQL模板来进行封装使用,这也成为性能的一个隐患.而实际上后端开发人员80%的事情均用在这里(从整体最优来看,也是值得的).
        反之,如果我们将数据表,列这些定义成为资源的话,那么实际上我们将走到一条Meta-Data(元数据)编程的路子上.显然在逻辑层次上我们将不需要去考虑业务中角色以及相互关联的
问题.这种类似"虚拟机"(提供一套策略而不是机制)的架构风格应对复杂逻辑是绰绰有余的.但是在SNS这样一个毕竟存在大量单一应用以及"简单数据"访问的情况下,这样有似乎有点"过度设计".当然这只决定于需求和性能之间的平衡.

  • 对Method的使用
        几乎任何一份REST实践指南的资料中都指出过DELETE和PUT的模拟问题,使用GET/POST去实现这样一种模拟.
        完全的使用GET方式去实现,这么做的唯一好处,是开发便利:只需要将这样一条URL贴到浏览器的地址栏中,就可以完成测试.但是,这样的系统本身并没有把URI看成是"资源",而仅仅是
一种传递参数的字符串而已.同时这种链接一般不可加入书签,而且有“爬虫”造成非预期副作用的风险(假设你传递了一个?method=delete这样的参数).
        完全的使用POST方式去实现,这种做法被flicker等知名网站广泛使用,但实际上走回了SOAP的老路上.他不但完全忽视了REST的根本原则并且接下来也无法利用"缓存".我们的系统采
用这种方式的原因是设计上的"便捷"(:
  
  • NoSQL?
        SNS为NoSQL的思想贡献了相当大的关注力,这种思想意图使用key-value键值数据库来完全取代现在有的关系型数据库,通过键值缓存技术来提升性能.虽然如此,但NoSQL对系统的设计要求是相当之高,否则很容易就会发现构建出来的系统几乎不能满足良好的扩展性.

  • 工具与方法论
        开发初期,如何能使更多的开发人员理解REST思想,并应用于开发活动,我们提供了一个在线的调试器,当你输入URL的时候,可以查看返回数据以及相关信息.国内的Taobao开放平台也提供
了类似的沙盒环境(sandbox).推广一种工具往往比推广一种思想要容易的多.当我们意识到并不是所有人都需要明白什么是REST的时候,我们提供了一组language binding clinet library.


软件开发世界没有"银弹",试图用一种架构风格/模式去解决遇到的所有问题是不现实的.在实践中遇到的种种问题,探究他们的缘起以及解决之道,有利于加深对REST架构的理解和应用.那么,当我们 意识到这些问题,并尝试解决的时候,不妨跳出原有的思维局限,开拓眼界,引入更加符合实际情况的混合架构风格设计方案,这也是我们下一代技术产品的思路,并且有打算以开放源码的方式展现在 大家眼前,提供一个Resty的"砖头".

Monday, November 30, 2009

Django的HTTPHandler模型图

很早之前的一篇读书笔记,今天有朋友问我这方面的问题,正好就重新贴出来吧.
时间过得好快啊(: