Category Archives: Tech

当我们谈论技术时我们在谈论什么

先声明这不是一篇抒情散文,而是一篇技术文,我只是想把标题搞的文艺点:)

最近接触过一些优秀的 startups,多是很靠谱的人做着很靠谱的事,有的产品刚上线,有的已经融了 A B 轮,大家坐在一起聊技术建设时,谈论着创业公司成长阶段的各种痛点。这就把我带回了事情开始的原点:三年前我和我的团队。那时的我们也遇到同样的痛,我想得有一篇文章,讲讲这些痛并快乐着的事。

在街旁成长的三年中,技术团队的关注点我大致分五个阶段,分别是 coding, architecture, standard, workflow, system,每个阶段不独立于其它,而是交叉进行的。但因为条件受限,尤其是创业前期,同一时间只能重点搞一件事。后来不断有血的教训告诫我们,这些阶段都应该从一开始就考虑进来。

First stage – Coding

当然是开发功能,基本上每人负责若干功能,前端开发,接口设计、业务逻辑、数据库设计、运维部署,从前做到后。那是一段不断赶工新功能的日子,做得很快但没有章法。我们后端同时提供一套 internal api 和一套 openapi。

这期间先后上线了 feed, friend, notification, message, photo, board, sync, push, advertisement, gaming/badge/points, coupon 等一系列功能模块。代码管理使用 git,后期又引入 gerrit 进行 code review,这是一套由 google android 团队开发的系统,我发现国内很多团队都有在用。

事情似乎发展很顺利,产品快速被验证,得到用户反馈又不断调整。用户和数据开始越来越多,但系统越变越慢,直到有一天晚高峰开始宕机,用户完全刷不出内容。此后的每一个晚高峰时段,系统慢到超时失去响应。

我们通过 api log 和 db slowlog,找到不合理的代码和数据库查询语句,不断反复地找反复地改,系统有些起色,高峰时期虽然很慢但还可以服务。

Second stage – Architecture

当我们意识到能优化的代码已经优化过了,可系统性能还是差强人意,我们把重点转移到调整架构。说到架构的演变将来可以单独有篇长文来回放历史,这里罗列做过的部分事:

  • Increase hardware infrastructure, deploy each service in independent server
  • Turn function into service, build Service-oriented architecture
  • Refactor codes to make module hierarchical
  • Remove single point failure, hot backup for every service
  • Multiple datacenter deploy
  • Databases master/slave, replication, partition
  • Make business functions asynchronous processing as much as possible
  • 有一条贯穿架构调整从始至终的主线是让系统变得可伸缩扩展 make system scalable

在发展的中期,我们也开发了一些自己的系统基础组件:

  • crabdb – another nosql database system,提供节约空间的分组存储和更高效率的多维度查询,支持丰富的查询表达式。
  • crabwsgi – python application server,用 C 实现的 python 服务器,支持更直观的进程控制面板。
  • memcachefile – image storage service,支持使用 memcached 协议与文件系统交互。
  • imgsync – image backup service,提供数据文件的增量备份。

Continue reading

通知系统的存储设计

街旁通知随着功能的演进和不断增长的数据规模,底层存储经历了从 MongoDB 迁移到 CrabDB 再到 Redis 的过程。通知系统包含以下几个组成部分:数据存储,业务逻辑,交互逻辑,正文拼装,服务接口,任务队列。本文只讨论数据存储部分,着重从数据特点、查询维度、存储结构等方面,介绍当前遇到的挑战和解决方法。

通知是什么

用户接收到的站内消息提醒,通常和某一行为关联在一起。通知可以由用户发给用户,也可以由系统发给用户,是一对一的。

例如,评论了某人的状态,加某人为好友,收藏了某人的攻略,获得了徽章。

数据特点

数据总量大,规模仅次于签到;
单条数据很小;
热点分布不均匀;
查询维度很多;
用户通知有上限;
数据字段可变;
可以滞后更新。

搞清数据的特点是设计存储结构的第一步,可供选择的存储技术有很多,没有绝对的好与不好,只有适合与不适合。架构不能脱离业务而谈,明确数据特点才能为技术取舍找到更好的利弊平衡点。

查询维度

查询用户某些类型的通知列表 to_user, type
查询用户某些类型的未读通知数 to_user, type, unread
删除用户对动态标记赞的通知 to_user, from_user, type, post
把用户动态被回复的通知标记已读 to_user, from_user, type, unread, post
把地点册留言相关的通知标记已读 to_user, type, venuelist_id, unread
……

查询维度表明了我们如何去检索数据,这决定了怎样为数据建立有效的索引。上面几个例子看出,通知数据的查询维度多种多样,很难用两三个索引覆盖查询的全部情况。

这里展开说几句,事实上社交服务的复杂性,主要就体现在数据量大且查询维度多上面,解决问题没有标准做法,就我看到业内主要思路有:
增加缓存,提高命中率;增加冗余,牺牲强一致性;离线计算;专有存储和计算服务;分布式扩展;降维。解决方案通常是上述几个的综合运用。

Continue reading

机房那点事儿

等到 IaaS 普及的那一天,开发者的线下运维就真要成为历史了。现在就想写点什么,以此留念。

我们团队很长一段时间没有专职运维,和硬件打交道的事,都是两三个研发抽空搞一搞。代码写久了,搬搬机器换换脑子,我倒是挺乐此不疲的。我们从最初一个机房几台机器,发展到后来两个机房几十台机器,在精力人力都受限的情况下,怎么管理和维护倒是有一些积累。

先说装备,每次去机房我会过一遍的 checklist:

  • 出入证,身份证,出门前检查一下;
  • 待上架的设备,最沉的东西请自行找好苦力;
  • 网线,电源线,视频线,条件好的机房有提供;
  • 机架位分布图,记录每台机器的位置和连接端口;
  • 键盘,我用一个 IBM 外接键盘,机房专用的,当然也可以管机房借;
  • 笔记本电脑,万一要访问个 Web 界面什么的,iPad 也行,想好怎么连内外网;
  • 标签贴,给每台设备贴上 IP 和主机名;
  • 手机和 Evernote,用来记录各种密码和备忘录;
  • 手电筒,有些设备很短,卡在架子上露头不露尾,要用手电照明;
  • 蓝牙耳机,当你需要场外援助时,能解放双手;
  • 系统恢复盘和外接光驱,不一定每次都用到,不过留在机房以防万一;
  • 外套,机房太冷最好预备着以防被冻死。

再有一些注意事项:
网线用超五类,长度适中,不要太长否则堆在一起不好梳理。把每根网线的两头都贴上标号,方便辨认同一根线的两头。
电源线用服务器专用的立头,扁头的容易被不小心掀掉。有备用电源的一定要接上。
每上架一台服务器,都要严格记录好它的位置,所用网线标号,和连接的交换机端口号。
提前设置好机器的系统账户和网络配置,省得到机房手忙脚乱。

Continue reading

Python的循环引用问题

假设有一个 module x 引用了 module y,然后定义一个方法 run:

# this is module x
import y
def run():
    print 'execute function in module x'

如果你的程序 import x,Python 会加载 module x 并执行里面的语句。当执行到 import y 时,就去转而加载并执行 module y。

到现在为止,Python 已经在 sys.modules 里注册了 x 和 y,但是 x 还没有执行 def run 即还没有定义 run 方法。

如果 y 引入了 x (循环引用问题),它会得到一个没有定义 run 方法的 x:

# this is module y
import x
x.run() # error

这时如果对 x 调用 run 方法则会报错提示找不到方法:

Traceback (most recent call last):
  File "test.py", line 1, in 
    import x
  File "/path/x.py", line 2, in 
    import y
  File "/path/y.py", line 3, in 
    x.run() # error
AttributeError: 'module' object has no attribute 'run'

解决这问题可以尝试两个方法:
1. 尽量避免在程序中出现循环引用。
2. 把 import 语句放到方法定义的后面。

在这个例子中,可以把 module x 中的 import y 语句移到最后面,让程序先执行方法定义,再 import y 即可解决。

-EOF-

街旁雷达使用指南

街旁雷达是基于街旁开放API开发的一个第三方应用,它可以帮助你追踪好友行踪,当有人进入到你预设的区域范围,就会发送短信通知。

[UPDATE] 街旁雷达已经暂停服务

街旁雷达v0.1 开始进行公开测试,使用方法如下:

1. 在你的Firefox浏览器上安装插件Greasemonkey:
https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/

2. 在Greasemoney上安装脚本Jiepang Radar,点击Install进行安装:
http://userscripts.org/scripts/show/113665

3. 进入一个街旁地点页面,例如:
http://jiepang.com/venue/8D69B8047DE83B11

4. 如果是第一次使用,将跳转到第三方应用授权页面,点击确定进行授权

5. 点击添加雷达按钮进行添加

p.s.

1. 被发送手机短信的人需要在街旁网上绑定手机号码,才能收到短信,但发送者无需绑定;

2. 街旁雷达尚处于测试阶段,服务有可能不稳定。

联系作者:@黑客芋 & @hustKiwi

-EOF-

Web死了吗

前几天Wired.com杂志主编安德森写了一篇文章 The Web Is Dead. Long Live the Internet,掀起了业内大讨论。这位老兄算是IT评论界的大牛,以前出过两本畅销书:「Free: The Future of a Radical Price」和「The Long Tail: Why the Future of Business is Selling Less of More」。前者我大概读过,讲在互联网时代,企业如何利用免费产品,吸引到更多的客户,我在市场营销课的结课论文上还引用过其中的观点。后者,也就是那本长尾理论,现正安静地躺在书桌上,等待我阅读。

老安这篇Web已死的文章,国内有译文,大概是想说明浏览器网站正在走向衰亡,而跑在iOS、Android系统上的原生App将走向繁荣。

下面是这篇文章里的一段话:

You wake up and check your email on your bedside iPad — that’s one app. During breakfast you browse Facebook, Twitter, and The New York Times — three more apps. On the way to the office, you listen to a podcast on your smartphone. Another app. At work, you scroll through RSS feeds in a reader and have Skype and IM conversations. More apps. At the end of the day, you come home, make dinner while listening to Pandora, play some games on Xbox Live, and watch a movie on Netflix’s streaming service.
You’ve spent the day on the Internet — but not on the Web. And you are not alone.

那么难道跑在移动操作系统上的App真的会取代网站,我们喜爱的Twitter, Facebook, YouTube都将变成你手机里的一个个App来运行吗?

-EOF-