踏上架构之路(一)——一个初创公司产品的系统架构思路
"Believe it or not, the bigger problem isn't scaling, it's getting to the point where you have to scale. Without the first problem you won't have the second."
2014年底2015年初是我的一个迷茫期,当时我在工作的方向上一直徘徊犹豫。2015年5月,在投资人J(也就是后来的CEO)的“忽悠”下,我加入了现在的创业团队。J事实上是一家10几年传统行业公司的老总,是那种传统的不能再传统的行业,公司也是典型的老总一人说的算的私企。J在某行业里摸爬滚打了10几年,感觉到行业里有些深深的痛点,也渐渐有了自己的一点思路,于是想趁着互联网+的东风,做一个平台解决B端的问题。
于是在投资人原有公司的几个手下的帮助下,一起创立了这家创业公司。抱着边闯边试边学习的心态,就这么甩开膀子(不知天高地厚地)就干起来了。现在离团队雏形组建完成项目启动差不多整整5个月过去了,我们的产品第一个版本即将发布(2016.2更新:已经发布到1.2.0版本),于是我想把过去半年遇到的以及以后可能面临的问题都记录下来,给自己也给团队留下一个完整的历程纪录。这也是我第一次尝试为一个中型移动互联网项目设计从前端到后台完整的架构,也算是自己的学习心得吧。再次声明,我也是边学边干,这只是笔记,难免有大量的错误和不足,仅供参考,欢迎指正。
掀起你的盖头 ——[产品的大背景]
OK,上任后第一件事就是聊天。和J总前后聊了两个多星期,心里对产品需求有了一个整体上的概念:业务方向不多说,单从概念上说,这是一个B2B2C的电商平台产品。但是这又是一个特殊的电商平台,因为它的客户对象是一个非常垂直的群体,商品本身也有着一定的特性。由于身份和商业的关系,暂时不能明确指明产品和内容,但是综合整体来说,有以下特性和要求(有行业经验的人应该能够猜出具体方向):
1. 平台商品具有明显的层级关系,C端(Web端和App端)的UI具有对应的层级结构;
2. 商品的展示频率远远高于交易频率,并且交易频率可预期非常低,但是一旦交易,即为大额或高额交易;
3. 初期架构需要基本完整的电商闭环,即包含B端上传服务,展示交易服务,支付结算服务,客服售后服务,财务系统服务,同时包含基本的业务监控和系统监控;
4. 平台访问量,初期目标(上线后3~6个月内),日均不高于10万PV;(3个月后更新,残酷的事实证明是不超过5000PV,不过这并不是研发的问题,在此不表。)
5. 系统所有有效数据(包括内容数据,业务数据等)需要能够保存并为未来的潜在分析提供基础。
工欲善其事,必先利其器 ——[架构选型]
基本需求已经明确,接下来就是技术选型。起头阶段必须要同时考虑的几个方面:
1. 开发团队的组建成本和速度
2. 系统开发的速度和可维护性
3. 系统扩容的灵活性和稳定性
这里面第1条其实是最重要的,因为创业团队要的是速度和质量,有了第1条,第2条才有保障,而第3条其实是最不是问题的问题,我曾经看到过几句话,对我的思路有着深深的影响,即是本篇的开头引语,其实在它之前还有一句:
"In the beginning, make building a solid core product your priority instead of obsessing over scalability and server farms.Create a great app and then worry about what to do once it's wildly successful. Otherwise you may waste energy, time, and money fixating on something that never even happens."
创业初期,关键是你的App的内容,功能和质量。不要为了scale而scale,而导致整个项目的盲目复杂和拖累。事实上,这个思路一直贯穿了迄今为止我的整个项目,一句话“杀鸡勿用牛刀,够用就好”。
OK,有了这样的前提,首先来考虑后台框架。后台的框架类型和开发语言相辅相成,息息相关。摆在面前的无非几种:
1. PHP
2. C#/C++ (ASP .Net)
3. Java (Spring,Struts)
4. Python (Flask, Django,Tornado)
5. Ruby (ROR)
当然,除此之外,或许还可以考虑直接整合现成的BaaS/SaaS/PaaS服务 (LeanCloud等)。
.Net我想都没想就否决了,因为自己是做Linux出身,对微软的东西没啥好的印象,不是敌视,就是纯粹意义的不喜欢。Java也是如此,但是不可忽视的一点,常规情况下,要说做电商,毫无疑问的首选是Java和PHP,大量ECShop的模版均是基于这种体系。但是我考虑到:
PHP做起来快是快,人也好招,但不适合处理复杂业务逻辑,将来如果要考虑扩展性更是麻烦;Java是厉害,但是对于初期项目而言,开发和部署都太重,前期系统搭建和维护成本反而远远超过业务逻辑本身;ROR做一个社交系统可能比较方便,但是能够维护好Ruby的人我去哪里找?更何况我们需要交易,把复杂的支付交易逻辑交给Ruby我怕是睡不好的;于是只剩下Python了。看起来Python似乎满足了这个项目至少1~2年的基本需求:要处理队列?单机有RQ,ZQ,多机有RabbitMQ,想分布式扩展?试试Celery,将来要做数据挖掘?上SciPy、NumPy,Python天生就是爬虫的首选,其他诸如部署Fabric,监控用Supervisord等等,看起来应有尽有。
其实以上都是我瞎扯的,因为当时真实原因很简单:“Python我比较熟。” 架构师选型,自然优先是选择自己最熟悉的体系。所以,没有最好的,只有最合适的。合适你自己的东西就是好东西。
OK,Python定下来了,那框架呢,小打小闹的web.py不用考虑,FDT三家选谁?考虑到一期目标项目的规模和业务内容,选择Django自然是首选:第三方支持丰富,ORM处理方便,数据库操作简单,中间件扩展灵活,性能够用……当然,任何事情都有两面性,Django也有自己的问题,比如并发不是Django的强项,ORM的封装虽然强大,但是也基本上给将来想做DAO层的二次开发带来比较大的难度,事实上在后来数据库切分的时候,Django的不足就比较明显了,这个问题如果后面有时间再来慢慢记录。 但不管怎样,我仍然相信Django是中小型项目起步阶段的首选。
踩在巨人的肩膀上——[系统部署]
Web Server系统的选型确定了,接下来一件事,就是系统部署架构了。LAMP仍然是圣经王道,但是操作起来自然是LNMP,中小型项目你想不用都难,因为它就是这么好用。因为需要交易,MySQL选择InnoDB是必须的;又因为项目展示内容的特性,拥有强大的静态缓存和反向代理能力的Nginx也是当仁不让的选择。
好,菜都凑齐了,在哪炒呢,自己家建厨房还是上馆子店让别人代劳呢 ?可能第一反应是自己家炒嘛!数据什么的都在自己手上,进了机房门就能直接调试,舒舒服服,多好。可是,随之而来的问题呢:电源,空调,布线,监控,报警,宕机维护,扩容……Oh No,想都不敢想。再说,这些不要钱么?抛开这些不谈,光是一个外网访问带宽就能让你肉疼。所以,云托管。
好,问题又来了,选哪个云?七牛云,阿里云,百度云……天边飘来很多云,每家似乎都有特色。事实上,在做这个项目之前我也没有太多实际操作云的经验,但是简单的调研对比之后,我的目标基本锁定在七牛和阿里两家。考虑到整体分布式服务的支撑和稳定性,最终选择了以阿里云作为主业务服务部署端,七牛云作为内容服务器部署端相结合的架构。
1)阿里云为业务服务端云,是因为我的业务服务器都在阿里云上,而且阿里云能提供绝大多数我需要的基础设施,在这个项目中我用到了以下服务:
a. 云服务器ECS:作为项目提供所有服务器支撑;
b. 数据库服务RDS:作为项目的主业务逻辑的数据存储,选择MySql;
c. 分布式缓存OCS:作为RDS的前端缓存,为只读业务提供缓存,减缓RDS压力;(注意,阿里云的OCS是基于bmemcache的,所以不能提供Key的遍历服务,这也造成了我们在项目中期建设的时候,不得不额外搭建了自己的Redis缓存服务器来提供一些必要的服务支撑。这个后续有时间细表。)
d. 分布式文件存储OSS:作为B端业务主文件存储,主要存储图片和文本文件;
e. 图片处理服务:为B端提供图片访问和缓存支撑;
2)七牛云为内容服务端云,基本作用是提供文件存储和图片处理,最大的原因是以下几个:
a. 七牛的CDN向来做的不错;
b. 七牛的图片处理服务在我看来是顶级的,如果你的业务以图片处理和访问为主,七牛是比较好的选择;
c. 它每个月有10G的免费存储和100万次Get/10万次Put流量,免费的我干嘛不用。
实际上,上述的系统部署和基础设施的选用也不是一次到位的,这个过程也经历了从小到大,从简单到复杂,前前后后大约2个月左右的演变过程,虽然谈不上演变有多先进,但是系统的整个设计过程,我处处遵循了开篇时的那段引子的原则:“先功能再优化,先简单再复杂”。后面我会单起一篇分享一下项目的架构是如何一步步发展和演变的。
开始画图纸——[业务模块]
如果说,架构选型是找到最合适的设计院和工程队,系统部署规划是找到合适的地皮和基建设施,那么项目的业务模块设计就相当于盖房子之前的要画设计草图了。
基本上从大的角度来说,一个完整的分布式B2C系统所需要的各个模块部分是差不多的,这个网上有太多的文章可以参考。但是问题在于,并不是别人的系统就是最适合你自己的:也许别人几十人几百人花了几年搭建的系统,你拿过来照搬,打死你也做不出来;又或者别人提供的架构其实很多部分对于你自己的项目来说都是杀鸡用牛刀,根本就是多余的。所以,整体框架可以参考,但是结合到具体项目,还是必须根据实际需要单独设计。这点上,我并没有什么太多的创新的部分,更多的工作,是在已知通用业务框架上作一些裁剪和修补的动作,量体裁衣。
B2B2C系统,BBC3大部分这是跑不掉的,具体到每一个环节:
1)商户端B:
a. 用户管理,权限管理:商户信息设置,权限控制,主要体现为交易权限控制;
b. 内容上传,内容管理:商品内容空间的创建,图片文字数据上传,审核状态的操作,已上架信息的CRUD管理和预览等等
c. 交易管理:实时交易订单控制,非实时交易订单控制,物流管理等;
d. 消息管理:B2B消息,B2C事件消息管理;
e. 增值服务:推广服务,反馈服务等等;
2)平台服务端B:
a. 用户管理/权限控制:包括BC两端用户和平台各模块管理员的管理控制;
b. B端内容审核服务:主要负责B端内容的核查相关服务;
c. B端内容管理服务:管理B端数据,提供B端服务API;
d. C端内容发布服务:主要负责内容从B到C的转换;
e. C端内容管理服务:主要负责C端数据Restful API和业务API;
f. 业务逻辑模块:主要负责处理项目相关主业务逻辑,到目前本篇文章成文为止,已包括四大子模块,后续将增加更多模块:
i) 交易管理
ii) 订单处理
iii) 洽购处理
iv) 竞价处理
3)用户端C:
a. 内容管理:主要负责数据的展示
b. 交易管理:对应于商户B端,实时交易订单控制,非实时交易订单控制,物流管理等;
c. 个人相关数据管理:主要负责和个人相关联的写数据请求
用户端这部分,上述3个模块只是从整体上来简要说的,实际上,由于项目的跨平台跨端口的要求,具体又要对Web端和移动端做不同程度的定制化处理。比如在数据安全性上,Web端和移动端需要不同的处理办法。在前端缓存上,移动端App不像Web Browser那样自己本身能提供一定的缓存机制,需要单独做一些处理。
上面这些模块的划分,也仅仅是从大概念上做的比较粗粒度的,具体到每一项,都可以单独拿出来写,很多其实绝大多数书上和网上都有足够的参考,我并不能一一枚举,但是这其中仍然有一些模块是我觉得非常值得拿出来落成文字,也是我们花了大部分精力研究和完善的:1)交易订单的处理,对于一个电商平台来说是重中之重,2)App前端缓存处理。3)系统各模块的桥接处理。
后面我会结合系统架构的演变细节,仔细介绍前面提到的这几个部分我们曾经遇到什么坑,埋了什么坑,又是怎么填的坑。
红花都得有绿叶衬——[系统外围辅助业务]
一个完整的项目系统,除了提供主业务功能的主服务系统之外,还得额外考虑必要的辅助业务系统。对于我们这个项目而言,必要考虑的包括:
1)监控服务:主要负责对系统业务服务器的监控,以及主要业务指标的显示操作等管理。大部分情况下,阿里云自身的ECS服务器和RDS监控已经能满足最基本的监控需求,但是仍然还有一部分和业务密切相关的监控是必须要额外建立的,比如所有外部接口的频次统计,所有业务数据的实时统计,以及核心业务事件的追踪等等。这其中,接口统计和事件跟踪,又可以在前端和后端两部分分别完成,前端我们依赖于大量的第三方支撑,以免自己重复造轮子。目前使用的有:Web端-百度统计,移动端-友盟统计;后端部分,我们是在Django的中间件层,自己实现了一个简单的API中间件来实现全服务层的指标监控。这部分考虑到性能的因素,需要完全建立在缓存之上,一开始我们想在阿里自己的OCS上做文章,结果后来发现OCS在key的支持上非常的弱,没办法只好自己搭建了Redis缓存服务器来为这层中间件服务。但是后来的实践证明越来越多的系统内部服务需要依赖在Redis之上,所以渐渐的OCS退化为只是单纯的为C端用户提供只读数据缓存这一项单一功能了。
2)财务系统:主要负责对交易数据的归总和财务接口。B2B2C和C2C之间的最大区别,是除了常规的对账操作之外,还有必要的分账操作,在我们项目的初期阶段,考虑到业务的规模和复杂度,目前分账操作是线下的,但是我们提供了必要的基础服务给对账和分账操作提供基本支撑。
3)地推服务系统:这是和项目运营部门的实际需求挂钩的,同样建立在Redis缓存服务至上,在此暂不细表;
4)客服系统:主要提供平台和品牌的反馈追踪,交易跟踪处理等业务提供可视化数据服务,提供客服沟通效率;
5)日志处理系统:主要负责维护系统每天的日志输出。
6)系统自动化部署服务:实际上,在我看来,系统自动化部署完全不应该和以上这些“低级别”服务并列。因为它真的很重要,在大型公司,系统的自动化集成,发布和部署甚至是一个独立的大部门而存在。我从一开始就对这个部分很头疼,原因是之前的工作经历绝大部分情况下是接手已经成型的产品线,很多自动化框架都已完成,接受团队只需要依葫芦画瓢对新功能进行添补就可以。而对于现在这种完全从零开始的产品,一开始对自动化框架进行规划是再合适不过的。可是事实也是不完美的,对于我们这样的初创团队,一开始人手严重不足,单是为了以上提到的系统模块就已经让大家忙不过来了,所以自动化部署和发布这部分一直拖延,直到项目上线前的后期阶段才初步尝试。我们使用的是Fabric部署框架,结合大量的Shell脚本,主要完成后端服务器的代码管理,打包,远程分发以及做些发布前的必要的Config。
2016年2月12日,完稿于吉隆坡.