当前位置: 首页 > 科技观察

为什么要重构到微服务架构

时间:2023-03-16 13:54:33 科技观察

公司决定将支付业务从原来的部门中分离出来,成为一个独立的团队,以满足快速发展的业务需求。原团队负责支付系统开发的几名同学调到现在的团队,组成开发团队。之后开始招聘,3个月时间团队扩充到10多人。与此同时,公司业务也在快速发展。6月,宣布会员突破2000万。一些热门影片的上映,往往会引发会员注册缴费的小高峰。其他业务,包括直播、阅读、动漫等也进入了发展的快车道。日订单量已经超过百万,比去年某部电影上映时系统不堪重负的时候还要高。移动端每个月发布一个版本,桌面端半个月。产品经理没日没夜地计划各种功能,要开发的功能计划几个月。随着项目团队的扩大,一些奇怪的事情发生了:虽然团队扩大了4倍多,人员素质也有所提高,但开发效率与之前并无太大区别。大部分开发工作还是由少数老员工忙,新员工参与核心开发工作相对困难。除了管理因素,作为工程师,我们更期待从技术上找到根源,解决问题,提高效率。最终决定是使用微服务架构重构现有系统。本系列博文描述了我们做出的选择、取得的成果、走过的弯路,以及过程中得到的教训。1.原有架构从技术角度来看,原有系统是基于SSH架构的传统实现。软件架构整体就是大家熟知的多层Java软件架构:代码看起来很怀旧,虽然我和开发者都说是4年前开发的,但是熟悉的SSH架构是做过的东西10年前。使用ApacheStruts作为表现层,对数据访问层做一个简单的封装来实现业务逻辑层,基于Spring的AOP和Hibernate实现数据访问。数据存储在MySQL中,单库多表结构。二、架构问题1、DAO层使用Hibernate封装数据库访问操作。其优点是在面向对象领域,系统自动生成数据库访问语句,使开发人员无需考虑数据库的实现细节,而专注于对象的设计和使用,简化了开发工作。此外,使用Hibernate还支持系统在不同类型的对象数据库之间的无缝迁移。广泛应用于业务对象关系复杂的管理系统开发中。问题在于它隐藏了数据库的实现细节,导致开发者难以优化大数据场景下的数据库访问,而这正是互联网应用开发的重点。2.服务层业务逻辑层为Controller层提供具体服务的实现。但在实施方面,还存在不少问题。如果严格按照分层架构实现,将业务逻辑层进行拆分,将本地调用变成远程调用,可以相对容易实现拆分。但在实践中,经常会遇到以下问题:这一层往往没有实现单向依赖,将部分业务逻辑层实现注入到接口层参数(request、response)中,从而耦合到接口层。为了应对不断变化的需求,很多接口都使用map作为输入输出参数,这样的接口在维护时无法对其参数进行约束。服务层的大多数实现都使用工厂模式来管理数据对象。只有工厂类被建模,业务实体没有被建模。该层的实现是不完整的。这使得业务实体的操作需要推迟到Controller层,导致Controller臃肿。当服务之间存在大量依赖关系时,开发者往往会在每个服务中直接注入SpringBeanFactory,或者简单封装一个FacadeService,通过它可以访问所有的业务逻辑对象。该类的使用使得无法评估Controller层对Service业务对象的具体依赖关系。3、Controller层基于ApacheStruts实现。ApacheStruts漏洞频繁爆发且修复缓慢。目前,它很少用于外部应用程序。由于Service层的实现存在问题,Controller层承担了部分业务逻辑的实现,臃肿难测。3、功能问题从功能模块来看,没有对端服务和运营管理系统服务的区分,只实现了支付系统的基本功能:4、实现问题1、可扩展性差,性能提升困难Web应用的性能瓶颈基本上都在数据库上。本系统使用mysql作为数据库。三个应用对应三个数据库。没有读写分离。阅读和写作都在图书馆上进行。当时数据量最大的表是5000万条数据。数据库操作高峰期QPS在1000左右,压测结果显示可以支持2000QPS。这个指标让我很吃惊。为什么它能有这么好的表现呢?首先,没有复杂的查询逻辑,所有的查询都在一张表中操作,没有跨表的事务处理,复杂的处理分解成多条语句执行。在最复杂的动作中,执行了近20次数据查询。第二个也是最重要的因素是此处使用的SSD磁盘。从目前的情况来看,应该可以坚持到年底,这也为我们的技改争取到了足够的时间。尽管如此,我对mysql还是没有把握。运营部每次举办活动,我们都提心吊胆地盯着看,祈祷活动不要太有效果。从应用层来看,目前读写比为10:1,接口日访问量为10亿。峰值访问量为300QPS。公司业务增长迅速,数据量半年翻一番,访问量预计增长10倍。还有一个严峻的挑战。产品同学扬言要搞闪购,闪购……每秒10万的量必须支持。这超出了MYSQL所能承受的压力范围,需要将读操作切换到内存数据库。但是在SSH架构下,读写分离的实现不得不被打破。另外,由于Hibernate对数据库的操作进行了封装,不需要写SQL,也无法进行精细的优化。每次系统变慢,都得请DBA帮忙看看是哪些SQL卡住了。每隔一段时间,你就得让DBA把SQL语句导出来研究如何建索引。2、系统臃肿,学习周期长,100多个接口,分为三大项目。***项目有1300+类,其次是600+和300+类。SSH架构,SVN版本控制,resin作为容器,Nginx前端路由。路由这个是喜人的,是对整个重构工作的有力支撑。一个纯后端项目,为移动应用程序和PCWEB应用程序提供接口。这也大大降低了重构的难度。如果前端也耦合进来,那就更酸了。庞大的系统规模让团队成员难以接管。支付业务独立后,开发者数量在2个月内从原来的5家扩大到10家。与此同时,兴奋的产品同学们犹如打了鸡血,各种想法都变成了产品,开发压力陡增。而新加入的学生,看着上百个班级,往往无所适从,无从下手。不知道哪些功能实现了,哪些功能需要完善。直到三个月后,新员工才逐渐进入角色。尽管如此,还是有不少恐龙级别的密码无人敢挑战。一个类的大小2000多行,核心方法500多行,大量重复代码,每次调整都以失败告终。3、合作成本高随着项目组成员的增多,每次新版本开发都需要多人协同修改同一个项目代码。虽然使用了版本控制工具来管理分支,但是难免要花很多时间在代码冲突的解决上。新功能、增强功能、bug修复、对各种客户端的支持都在一个项目上进行,需要建立不同的分支。高峰期同时开展五六个分支的情况很常见。在这种情况下,代码冲突的频率非常高。小版本开发一周,解决冲突1天很正常。4、考试难度大,考试工作逐渐变差。搭建测试环境难度大。随着分支的增加,每个进入测试的分支都需要准备一个独立的测试环境。环境建设成本高。由于处理分支合并冲突,刚刚测试的功能不得不再次运行。严重影响工程进度。5、上线风险高随着系统复杂度的增加,上线风险也越来越大。修改一个小功能,打印日志,修复bug,都需要整体上线。一旦有错误的修改,系统就会崩溃。上线时间很长,上线一次需要半个小时。6、新技术引进困难。互联网公司特别渴望追求和使用新技术。SSH框架降低了开发难度和成本,同时阻断了其他技术的引入。对于缓存机制、数据库优化、读写分离等,SSH有自己的一套逻辑体系。调整姿势,成本比较高,技术难度也很大。它需要对底层实现有深入的了解。康威定律已经存在很长时间了。该系统由2-3名开发人员维护。对外接口和操作系统都是一起实现的,访问量不会太大。3个独立系统,对应3个版本库,每人负责1-2个系统。当系统增加一个新的功能时,人们的首要任务是如何改进现有系统,而不是考虑是否构建一个新系统。而当公司进行业务调整,人员数量快速增加时,就需要改变原有的合作方式。这对应于所谓的康威定律:任何设计系统(广义定义)的组织都会产生一种设计,其结构是该组织通信结构的副本。我们需要一种新的机制来应对系统演进中新形态的需求。5.分层和共享库为了避免对原有系统进行大规模调整,我们首先考虑利用原有系统分层实现的特点,实现分层分工。在实践上,我过去负责的管理系统开发项目,大多采用SSH架构,而且大多采用分层分工:封装业务逻辑层、数据访问层、数据表现层进入可以独立维护的库;接口层按业务拆分,代码依赖调整为库依赖。为各个库和项目创建单独的代码库。层通过接口交互,底层通过单元测试保证质量。这种分层的好处是可以很好的解决学习周期的问题。各层技术相对独立,开发者可以快速上手。DAO层比较简单,所以对于团队中的新手,可以从这一层开始,熟悉系统架构和软件流程;业务逻辑层是整个系统的核心,由老员工负责。可以辅助显示层的开发。接下来可以指导DAO层的新同学了。显示层需要了解HTML、CSS等技术。这种分工适合10人以下的团队一起工作。团队之间存在紧耦合的合作关系。对于大型项目,首先需要按照业务划分项目,将每个子项目分配给一个10人以下的团队来完成,然后对每个团队采取层级分工。但采用这种合作方式存在问题:需要良好的系统架构设计。在编码开始之前,需要确定每一层的接口和数据库结构。而这对于开发轻架构的互联网应用来说,几乎是不现实的。很多互联网公司连架构师这个角色都没有,很多有架构师的公司也是名存实亡。团队内部沟通成本高。层与层之间存在紧耦合关系,接口的变化必须通知所有用户。这就需要开发者通过约定的规则建立稳定的合作关系,降低沟通成本。上述各种问题依然存在。基础库发生变化需要在线系统更新库后重新上线。6、微服务在开始支付项目改造之前,我们刚刚完成了公司数据仓库项目的微服务架构改进。本项目的详细实现过程已经在dockone社区分享,详见这里。我们认为,调整为微服务架构可以解决上述问题。1.性能问题对于性能要求高的接口,可以通过创建数据缓存的方式进行优化。2.学习周期一个项目只包含几个紧耦合的接口,接口的业务逻辑单一。开发者只需1-2小时阅读代码即可快速上手。3、合作成本每个项目都是相对独立的,项目之间只是通过接口进行交互。接口确定后,开发、测试、上线全部独立进行,降低沟通成本。4.版本控制由于项目是接口依赖而非代码依赖,因此每个项目都可以建立独立的代码库。同时,项目被分成小块。每个项目开发时,只有一个开发人员会对其进行修改。基本上没有代码合并的工作,避免了代码合并过程中的各种问题。其实基于微服务架构的开发,我们并没有采用分支策略,而是直接用主干进行开发。5.测试难度每个项目独立部署,独立测试。由于消除了代码分支,不存在代码合并的隐患,减少了重复测试的工作量。6.上线风险每个项目独立上线。即使有问题,也只会影响一小部分接口。7、新技术经过四分之一的微服务改造,系统引入了各种新技术,开发不再局限于SSH架构。Spark、Hadoop、Hbase等与大数据处理相关的技术,以及Couchbase、Redis等缓存系统都开始在项目中应用,有效解决现有业务问题。当然有利也有弊,微服务带来的问题也有很多,包括项目多,出现问题排查困难。在实施过程中,也积累了很多经验。这些问题会在后续的分享中逐步介绍。【本文为专栏作者《凤凰牌老熊》原创稿件,转载请微信联系作者公众号《凤凰牌老熊》转载】点此阅读作者更多好文