Author Archives: admin

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

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

最近接触过一些优秀的 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

技术改造交通

驾驶是我除了编程以外的另一大爱好,开车能带给我心无旁骛的专注感,就像写代码一样。过去几年,互联网技术改造了通信、商业、金融和传媒,这样的革命还远没有结束,下一个让我有所期待的是用技术改造交通系统。

回首过去一年,无论 Tesla 还是 iOS in the Car 已经陆续有厂商开始布局了。但智能汽车只是这场改造的冰山一角,我心中理想的智能交通应该是立体而系统的,是交通工具、交通参与者和道路基础设施协同运作的结果。

汽车与司机的协作

车辆会配备更先进的安全系统,现在驾驶员要依靠经验和注意力才能够保证安全驾驶。而车型,路况,天气都会导致驾驶时的视线盲区,若能辅以更智能的碰撞警告、盲区信息监测、紧急自动刹车、自适应巡航控制,会极大提高安全性。驾驶员可以在偶尔走神、身体疲劳或视线偏离时依然能够保持较高的安全操作能力。

自动驾驶技术还有很大的提升空间,未来驾驶员会从主动操控变为被动操控,汽车依靠自身的自动驾驶系统进行正常行驶,只有在进行交通决策时(比如岔路口转向),驾驶员才介入控制。这时驾驶员获得了更多自由去做其它事情。

人机交互方式也会有创新,Leap motion、MYO、Siri 的出现带给了我们更多灵感,手势控制和语音控制会逐步取代按键操作,成为人操作车内信息系统的主要方式。这对于我这样的 Knight Rider 迷,真是想想都会兴奋的事。

Continue reading

为什么街旁照片要裁剪成正方形

在知乎和微博上,常听到用户抱怨街旁只能发布正方形照片,对此我有一些想法,不代表官方。

为什么街旁照片要裁剪成正方形,原因有三:

1. 正方形更适合在列表模式下展现,无论是表格式还是瀑布流,都可以有整洁美观的排版。

2. 街头摄影多以人文题材为主,画面中心区域所传达的信息量高于边缘区域,通过裁剪掉边缘区域突出画面重点,提高内容丰富度。

3. 迫使用户在发布前,重新审视照片的美感,对于不完美的照片要么调整剪裁区域,要么换一张。

为什么街旁照片不一定要裁剪成正方形,原因亦有三:

1. 拍摄自然题材的照片时,裁剪成正方形会破坏山川河流的雄伟感,画面中心区域和边缘区域相辅相成,同等重要,所以旅行摄影者更偏爱全画幅单反。

2. 正方形照片也不利于表现聚会场合其乐融融的氛围,近距离范围内正方形只能装下三四个人的上半身,为了把大家都放到画面里,只能后期拼图处理。

3. 很多人是 original style 的忠实拥护者,喝奶茶要喝原味,看美女要看素颜,拍照片也从不用滤镜,当然更不喜欢被剪裁,他们始终相信原始的就是最好的。

-EOF-

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-