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

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

最近接触过一些优秀的 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,提供数据文件的增量备份。

Third stage – Standard

这个阶段技术团队规模长得最快,我们以前的做事方式不 work 了。比如编码风格不一致且代码里多处 hack 和 magic number,一人看不懂另一人的代码,不能接手和扩展别人的工作。再如除了作者本人没人知道一个子系统要怎样部署运维,还可以怎样改进。我们做了但不限于以下事情:

  • 统一代码规范,作为 code review 是否过关的首要标准
  • 制定接口协议,统一接口规范
  • 撰写 wiki 文档,每个大小系统都要有设计文档,部署文档,故障恢复文档
  • 使客户端更直观地看到接口设计,完善内部文档系统,完善单元测试,接口变更及时更新文档并发邮件通知
  • 统一管理服务器账号权限
  • 规范服务和类库的安装部署目录

Fourth stage – Workflow

流程会明显降低工作的推进速度,但系统和团队达到一定规模,没有流程让所有事变的混乱无序。想让大象跳舞,我们花了很大力气去规范内外部流程。

技术团队内部,敏捷管理中的 daily sync, weekly report, technical design, code review, tech share, DevOps 都坚持不懈地推进着。每个 ticket 无论业务票、技术票、运维票、数据票,都经历 backlog -> todo -> doing -> testing -> done 的过程,我们通过 Trello 来管理这个流程。

对外部由于需求方(如产品、市场、运营、老板)变多,为了保护 RD 能井然有序的做事,我们成立了 Project Management Office 虚拟团队,由主力 PM RD QA 组成,对需求方提供时间评估、难度评估、任务分配、优先级调整等方面的支持,PMO 保护着后面的 RD team。

Fifth stage – System

系统工作看似是些不为人知的小事,但出了问题就是大事。这里列下重要且紧急和重要但不紧急的:

backup

我们的 mysql, mongodb, redis, crabdb 都有 master/slave 实时同步,但以防万一还是需要冷备份。定时每天夜里停掉 slave,拷贝出数据文件,或通过数据库接口 dump 出数据。
图片备份先后应用过两套方案:A. 用自己编写的系统服务,监视文件目录的变化情况,发现变化就把变更文件发到另一台备份机器;B. 由于图片文件存储于 ZFS 文件系统,使用系统提供的 snapshot 把数据定期增量备份到其它机器。
图片备份做好的第二周,一块主硬盘就坏了,紧急切到了备份机才恢复,真是一阵冷汗。
除了数据,还有定期对 git + gerrit 代码库的备份。

monitoring

先是外部监控,我们使用监控宝对系统的基本指标和定制服务做监控,监控宝的节点遍布全国各地,尤其适合监测面向全国用户的服务。
但是外部监控频率较低,五分钟的间隔使得很多故障被发现就来不及了。接着搭建了内部监控和秒级监控。内部监控使用 nagios 集中管理,对每台机器的 cpu, memory, network, disk 有基本监控,还增加了我们自己开发的 message queue, database, application 的监控插件。
秒级监控是一个不断运行的 dstat 工具,收集每秒钟机器的各项基本参数,定期汇总到一起用 gnuplot 画出图表。

hardware

采购设备是一个漫长的流程,为确保生产环境的机器发生故障后能很快找到替身,server, disk, memory, switcher 一些关键部件规划时都要有余量。

logging

每天生产环境会产生大量日志,这些日志会在流量低峰期转移到 moosefs 分布式存储中留存,供后台数据分析使用。

automation

自动化测试和运维能省去大量重复的人力,虽然做到全自动化是需要一整个专业团队来搞定的,但持续推进自动化工作的意识根植在每人心里。使用的工具和技术有 shell, crontab, unittest/paste, hudson… 未来还会有 puppet。

staging

这里称为准上线环境,之前我们的 staging 环境一直不完整,有些问题只能在一定数据规模的系统上,且承担了一定访问压力的情况下,才可能发生故障,建立一个完整模拟线上环境的 staging 非常重要。“完整模拟线上环境”与整体系统架构有关系,如果系统设计成不可伸缩扩展的,如何也不能完全模拟出一个等比缩小的系统。

上学的时候,当我们谈论技术时我们在谈论代码,写好代码就可以作业评优,就可以竞赛获奖,就可以到处吹牛。创业几年,当我们谈论技术时我们在谈论解决问题,代码、架构、标准、流程和系统样样不能落,对技术的执念逼迫我们磕磕绊绊也要向前奔跑。街旁是我们的另一个学校,我在这里学会编写高效的代码、设计可靠的系统、管理敏捷的团队,这些积累会一直伴随着我们成长、成熟。

-EOF-