从零到两百台服务器的创业野蛮生长史

作者:微信小助手

发布时间:2020-03-03T16:33:06


点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | 杨钦民

来源 | zhuanlan.zhihu.com/p/33401898

贝聊成立于2013年,是中国幼儿园家长工作平台,致力于通过互联网产品及定制化解决方案,帮助幼儿园解决展示、通知、沟通等家长工作中的痛点,促进家园关系和谐。贝聊是威创股份(A股幼教第一股)、清华启迪、网易联手投资的唯一品牌。在短短几年内,用户规模迅速达到千万级别,每年DAU均呈倍数级增长。

面对如此快速的发展,原有的技术架构很难支撑越来越复杂的业务场景,在系统可用性以及稳定性方面,都给贝聊技术团队带来了很大的压力。因此,如何针对当前需求,选择合适的技术架构,保证架构平滑演进,值得我们深入思考。

贝聊架构总体经历了三次重大历程,由几台服务器搭建的单体架构到目前的几百台分布式部署架构,在整个变化过程中,我们踩过了很多坑,遇到过很多重大技术挑战。

诞生期—技术架构选型V1.0

创业初期,我们的初始创业团队在进行架构选型时,主要基于以下几点进行考虑:

  1. 在创业初期,研发资源有限,研发人力有限,技术储备有限,需要选择一个易维护、简单的技术架构;
  2. 产品需要快速研发上线,并能够满足快速迭代要求,现实情况决定了一开始没有时间和精力来选择一个过于复杂的分布式架构系统,研发速度必须要快;
  3. 创业初期,业务复杂度比较低,业务量也比较小,如果选择过于复杂的架构,反而会增加研发难度以及运维难度;
  4. 遵从选择合适的技术而不是最好的技术原则,并权衡研发效率和产品目标,同时创业初期贝聊只有一个PHP研发人员,过于复杂的技术架构必然会带来比较高昂的学习成本。

正是基于以上几点考虑,最终选择了经典的LNMP技术架构,贝聊V1.0架构就这样诞生了,为了加快产品研发速度,尽快上线产品,首期通过外包公司实现了研发以及部署,后续由我们的PHP研发人员接手,并进行后续的迭代开发。

初期部署时,部署了三台ECS服务器,其中接入层nginx与系统部署在同一台机器,RDS数据库一台机器,Memcached缓存一台机器,V1.0架构具有以下特点:

  • 单体架构,架构简单,清晰的分层结构;
  • 可以快速研发,满足产品快速迭代要求;
  • 没有复杂的技术,技术学习成本低,同时运维成本低,无需专业的运维,节省开支。

LNMP架构支撑贝聊业务发展了将近一年半左右的时间,简单、易维护的架构为贝聊的快速发展做出了很大的贡献,期间业务发展迅速,用户体量也越来越大,原有架构逐渐暴露出越来越多的问题。

成长期—技术架构重构V2.0

我是在2015年初加入了贝聊,初始研发团队只有三人,有幸在这一时期

主导了贝聊技术架构重构,并经历了贝聊后续的几次架构演进路程,将原有PHP单体架构重构为JAVA分布式架构。

首先谈一谈我们做技术架构重构的契机,重构并非难在怎么做,而是难在何时开始做,所以我们做架构重构的契机主要基于以下几点:

  1. 原有LNMP架构经历了两个团队研发和维护,外包团队和公司PHP研发人员,由于业务变化比较快,原有的数据库设计逐渐暴露出来很多问题,很多表设计不合理, 很多字段定义不清,比较混乱;
  2. 2015年,由于业务发展,贝聊app需要拆分为两个客户端: 贝聊家长端和贝聊老师端,通过不同的客户端来服务不同的用户群体,达到精准运营的目的,如果在原有架构上继续进行开发,则会导致新旧接口逻辑混在一起,并且早期的很多接口定义不是很规范,维护起来越来越麻烦、越来越吃力;
  3. 原有API接口系统是单体架构,里面包含了各种接口,混合了各组业务逻辑处理,所有功能都集中在API接口系统中,代码非常臃肿,业务非常繁杂,迭代开发的速度也逐渐减慢,各种性能问题经常爆出,BUG也比较多,并且部署困难,任何修改每次都需整体部署。 由于业务逻辑混杂在一起,新入职研发人员需要很长时间才能够完全熟悉系统,很难快速通过以点及面的方式切入系统;
  4. 所有数据存储在一个RDS数据库中,只使用了一个主库,没有从库,同时很多系统共用一个数据库,一方面数据库没有做到物理上的隔离,另一方面很多表都放在了同一个数据库中,经常会碰到一个慢查询或者其他性能问题,导致整个RDS各项指标飙升,造成雪崩效应,所有系统连锁出现故障,最终都不能访问;
  5. 公共服务耦合比较严重,很多第三方服务都散落在各个系统里面,不便于统一维护,当需要修改公共服务参数或者做其他调整时,需要深入到每个系统里进行修改或者排查,非常麻烦,还非常容易出现遗漏,最终产生BUG,急需独立拆分出公共服务,将公共服务从业务系统中解耦出来,由专人进行独立维护、独立部署;
  6. 我们新的研发团队都拥有丰富的JAVA分布式架构设计经验,拥有高并发、高可用经验,因此将原有的单体架构重构为JAVA分布式架构也是顺势而为。

由于公司业务高速发展,如果停下来专门做技术架构重构是不可能的,我们选择了在维护现有系统的基础上,同时进行新的技术架构重构工作。重构期间,在原有PHP研发团队的大力支援下,我们的重构工作还算非常顺利,既保障了业务的快速迭代需求,又成功完成了新的技术架构重构,新的V2.0架构如下:

在V2.0架构时期,初步实现了分布式部署架构,根据不同的功能以及业务逻辑,完成系统级别的拆分,同时对第三方服务进行解耦,拆分出了独立的服务模块,针对DB,我们实现了系统级拆分以及物理独立部署,并实现了数据库主从分离,同时引入了MQ消息队列,并使用SLB实现了负载均衡和带宽流量入口统一。

V2.0时期的架构具有以下特点:

  • 分布式部署架构,系统易扩展;
  • 系统级拆分,拆分出业务功能逻辑独立的子系统,并独立拆分出DB;
  • 初步实现了服务化,系统间调用使用Hessian实现RPC;
  • DB实现了物理隔离,避免以前单DB出故障,引发业务连锁故障,同时实现了数据库主从分离;
  • 引入MQ消息队列实现消息和任务异步化,加快接口响应速度,提升用户体验,同时针对一些消息推送任务也实现异步化,避免早期的轮询MySQL机制,减少消息推送延时,提升消息推送速度;
  • 使用SLB实现了Nginx负载均衡,在V1.0架构时期,我们的Nginx是单点部署,若一台Nginx服务器挂掉,则会影响很多业务系统,有单点故障风险,通过SLB实现多台Nginx负载均衡,达到高可用的目的,避免单点故障。

系统拆分和DB拆分

针对系统拆分以及DB拆分,我们通过两个阶段来完成该项工作。

第一阶段

首先在系统层面进行拆分,将原有的大系统拆分出多个业务逻辑独立的子系统,而DB暂时不进行拆分,多套系统还继续共用一个DB,只是根据业务逻辑划分各个系统所依赖的表,不同业务逻辑系统之间不能互相访问表,这样新系统只访问自己所归属的表,通过此种方案,可以保证原有系统业务不受影响,同时新拆分的业务系统研发工作也可以顺利进行,此阶段大概花费了我们几个月的时间,最终顺利完成系统层面的拆分。

第二阶段

在完成系统层面拆分之后,我们紧接着实施DB层面的拆分,将各个子系统所依赖的表独立拆分出来,分别放置到不同的RDS数据库,实现物理的隔离,同时实现了数据库主从分离。最终实现效果如下图:

初步服务化

本阶段,我们采用了比较简单易用的Hessian实现初期的RPC服务化。针对第三方公共服务,从原有系统中解耦出来,独立拆分出服务化组件,并做独立部署,供其余业务系统统一调用。而系统间调用也通过Hessian来实现RPC远程调用。

SLB负载均衡