北哥在前文陆续总结了程序员成长所具备的核心能力,以及Java程序员成长过程中应学习的基础知识。在一个Java程序员工作3、5年之后,已经可以承担起大部分的核心开发工作,成长为团队中的高级开发人员。大部分工作中遇到的问题都已经可以自行解决。这个阶段很多同学会面临着新的成长困惑,到底接下来自己还需要在哪些方面继续提升?如何能够成长为团队里面的架构师呢?
市面上有很多分析和拆解架构师能力的书籍,例如《聊聊架构》《亿级流量网站架构核心技术》《大型网站技术架构:核心原理与案例分析》等,书中有一些相关的实战和理论知识分享。这些书籍如果有时间推荐大家去读一读。
今天北哥结合自己工作中的经验,也来浅谈一下如何做应用系统架构设计。
需要说明的是,架构师本身要求的是综合能力,架构也分为业务架构、应用系统架构、技术架构、数据架构等多个维度多个细分领域,但今天分享的关于架构的相关内容,更多还是侧重在服务端的应用系统架构。
01指导思想
作为一个架构师,在做应用系统架构时,最好逐步沉淀自己的一套指导思想,指导思想用于在做架构设计过程中遇到困惑或遇事不决时的一个指引。我个人总结下来的经验有以下三点
- 平衡和取舍
架构是一个复杂的工作,既要考虑当下的需求,还要关注未来可能的变化;既要考虑的足够全面,还要简单容易实现;既要衡量实现成本,还要关注落地的效率。这些无不意味着在做架构时需做好平衡,学会取舍。
- 迭代和演进
一个好的架构,一定是经过长期迭代演进而来的。在做架构设计时,受限于当前已经明确的需求,往往无法对未来考虑的那么全面。即使在做架构设计时已经考虑到了方方面面,系统上线后也会遇到一些新的未知问题,并且随着需求的不断迭代,又会引入新的变化和挑战,因此持续的对架构做优化和迭代演进,是必须要重视的。
- 聚焦业务需求
没有毫无瑕疵完美的架构,也没有一成不变适合所有业务的架构,只有适合当前业务和产品需求的架构。我们可以借鉴、吸收别人的经验和实践总结,但最合适的架构一定是结合业务的实际需求设计和演变出来的。脱离业务需求设计的架构一定会在开发中遇到新的问题。
02架构设计目标
在做应用系统架构设计时,应遵循一些普适的架构设计目标。这些目标包括
- 可实现的:架构被设计出来,一定是需要能够被实现和落地的,如果设计架构所采用的技术还没成熟或不具备生产环境可用性,这种架构只能停留在理论阶段,不具备落地的可行性,这样的架构也就没有特别大的价值和意义。
- 可扩展的:虽然无法预知需求未来的变化,但有些场景下,我们设计的架构也应该具备当用户量、带宽、数据量等增长时,具备较好的扩展性;除此之外,在做需求分析时,把系统、领域服务甚至模块做好规划和设计,能够合理的应对未来需求膨胀可能带来的问题,也是架构可扩展性的一种体现。
- 高可用的:系统架构需要考虑各种异常情况下系统可用性,如流量的突然爆发的消峰限流,某个服务或接口故障后的降级等,通过在架构中设计的各种措施保障整个应用系统是高可用的。
- 可衡量的:应用系统架构设计最终都需要落地到代码层面实现,提供出可线上发布的系统和服务。我们在做系统架构设计时,需要根据业务数据的预估,设计出满足一定量级的系统数据指标,将架构设计的结果量化。在遇到系统负载过高需要扩容时,就可以参考定量的数据指标预估出所需的服务器和资源等。
接下来从业务需求分析开始,到最终系统上线运营,按照不同阶段需要关注的知识和内容,来说明整个应用系统架构设计过程需要考虑的问题。
03需求分析阶段
正如前文所述,系统架构应该是聚焦业务需求的,业务需求分析,是做系统架构设计的必要前置。这里简述下我在需求分析阶段工作的大致思路。正常情况下,进入到架构师层面的需求,都经过了业务人员和产品人员至少一轮的讨论沟通,或已经有基本的业务和产品需求雏形,或已经产出业务的MRD(业务需求说明书)或产品的PRD(产品需求说明书)。这时架构师进入后需要再和业务及产品同学进行充分的讨论沟通,明确和产出以下几点信息:
- 确定清楚业务目标,搞清楚业务需要解决的核心问题是什么,通过产品和系统期望带来的效果是怎样的。
- 进行业务流程梳理和业务建模。业务建模的核心在于梳理清楚用户角色、业务场景、流程和相关规则策略。弄清楚不同用户角色在哪些场景下可以进行什么样的操作,由此对业务有更清晰的全局认识。
- 根据业务模型、业务产出的MRD或产品产出的PRD,整理出大概的子系统和功能列表。功能列表先从第一层级梳理,再层层细化,一般以不超过四层为宜。如第一层级分为用户管理、订单管理、售后退换货管理等。第二层级中用户管理再细分为注册登陆,基础资料维护、禁用启用等,依次类推。
- 根据业务模型和功能列表,预估和完善非功能性需求,如对总用户量、同时在线用户量、系统访问量等非功能需求,系统性能、响应时间、qps等系统性能要求,系统工期、人员投入、项目组织结构、其它成本等项目约束条件等。在需求分析阶段,我会最终产出一份基于业务模型拆解出的功能需求列表和非功能需求列表。这将作为后续应用系统架构的核心参考和依据。
04架构设计阶段
在需求分析阶段完成后,就进入到系统架构设计阶段。
在系统架构设计阶段,我一般会首先做数据建模。根据业务模型和功能列表,已经可以分清楚大概的系统、模块和功能,由此数据库的概念模型基本能够确定下来。通过数据库的概念模型设计,结合需求分析阶段产出的功能需求列表,整个系统的详细需求基本可以被印在大脑中了。同时经过概念模型的设计,不同数据实体之间的关系已经相对清晰,服务或领域的划分也具备初步的雏形了。完成数据库的概念模型后,开始进行详细的系统架构设计和技术选型阶段。
在系统架构设计阶段,我会按照分层架构设计的思想,逐步做细化展开。在目前的主流技术方案中,前后端分离基本上是默认的标准,核心原因一方面是前后端技术演进快速发展,技术专业人员分工更明确;另一方面当前的产品开始移动化,前后端一体的架构无法支撑当前移动端特别是App类应用的开发,前后端分离也成为不得不为之的举措。
按照分层架构设计的思想,将整个架构分为前端接入层、服务层、数据访问层、数据存储层。在部分高并发的系统中,还会有缓存相关技术贯穿在整个架构层级之间。
前端接入层
首先,在前端接入层,主要解决用户流量从终端发起请求到被应用服务器接收之前这一段的问题。
在前端接入层,核心需要解决终端展现时的响应速度和稳定性。
在传统的PC互联网场景下,大部分产品和应用以浏览器为终端,通过网页的方式展现在用户面前,如京东、淘宝等电商类的网页版。在终端层面,因为用户请求量巨大,会借助页面静态化、CDN等方式,提升页面加载速度。在请求层面,为了提升稳定性和应对更大的流量挑战,在流量进入到应用服务器前,会通过使用DNS、软硬件负载的方式进行流量分流,将请求打到不同的应用服务器上。这里会使用到的硬件负载有F5等,而软件负载主要通过nginx实现,早期还有LVS、Haproxy的方案等。
服务层
说完接入层,再来看服务层。服务是应用系统的核心,承担着业务和产品核心需求和逻辑的实现。
在服务层,需要解决的重点问题问题是,服务是否可以灵活快速的水平扩展,以应对未来系统可能的访问量剧增。
为解决这个问题,需要对是否进行分布式架构设计进行权衡。分布式架构设计的核心是通过对服务的解耦,来解决应用的水平扩展问题,降低单服务单机器的压力。如果系统所需要承担的流量在可预期的未来会有较大的增长,分布式架构设计就是有必要的。而如果系统流量在很长一段时间内都相对有限且平稳,则分布式架构就显得没那么必要。这里即需要做好平衡和取舍。
分布式架构虽然能够带来服务水平扩展的便捷性,以应对未来可能的系统流量大幅增长,但需要付出较大的系统维护成本。如果选择暂时不采用分布式架构,服务层也同样可以在工程层面,按照服务的接口层、服务的逻辑层和服务的数据层进行划分模块和子模块,然后在代码构建打包时集成到一个单体应用作为一个jar或war包一起发布。根据我之前的经验,在一个业务和产品的早期阶段,采用模块化划分的单体应用方式的架构,可以快速完成产品MVP版本的上线试错,当业务发展到一定规模后再启动分布式架构的服务化改造,也不失为一个较好的选择。
无论在一开始就选定分布式架构方案,还是在后期做分布式架构的服务化改造时,都会面临着另外一个选型问题:到底是采用分布式服务还是微服务方式来构建应用。
分布式服务和微服务是在分布式架构演进过程中产生的不同概念,按照我的理解,分布式服务关注的重点是分布式,重点解决服务的压力负载分担,而微服务重点关注的是服务的单元颗粒度大小,更关注服务的原子性、独立性。二者都是为了解耦合,但在解耦合的颗粒度上稍有不同。具体的选择也需要根据应用系统的规模、领域划分等综合考量。
落地到分布式服务的架构技术选型上,有以传统的RPC框架为主导的技术体系,和以Spring Boot微服务框架为基础的Spring Cloud全家桶。
传统的RPC框架以早期阿里开源的Dubbo框架为代表,核心的实现是基于动态代理+反射的方式来实现服务之间的接口通信,服务和服务之间的底层调用是基于socket来实现数据传输交互的。而Spring Cloud全家桶,核心是基于Http协议实现的一套微服务框架,服务和服务之间的调用是通过Http接口实现交互的的。相比Spring Cloud,RPC框架一般具有可自定义数据结构、网络传输速度快、效率高等特点,而SpringCloud因为是基于Http协议进行网络传输,消息的包体大小受Http协议限制做了封装显得相对臃肿,在网络传输时效率相对低一些,但Spring Cloud因有一整套组件和生态的支撑,因此在不做过多性能苛求的情况下,也是目前可以快速采用的方案之一。另外一种将二者结合的方案,利用Spring Cloud设计和实现接口的接入网关,再通过网关将请求转发到RPC服务中,则是目前一些大型应用普遍采用的方案。
在使用Spring Cloud或RPC框架的过程中,另外一个需要关注的问题是服务之间的耦合问题。虽然分布式服务或微服务本身就是为了解决耦合问题,但应用和应用、服务与服务之间的依赖关系并不因为使用Spring Cloud或RPC框架就消失了,在一些场景下,适度的服务之间的依赖是允许且必要的,但过度的服务之间依赖,甚至发展成服务与服务之间相互依赖,就是需要避免的一种情况了。通过消息中间件,将原本有上下游关系的依赖,通过消息解耦,从而让服务和服务之间避免强依赖,是其中的一个解决方案。常用的消息中间件有RabbitMQ、RocketMQ、Kafka等。
数据访问层
说完服务层,再来看数据访问层。所有的应用都离不开数据,数据是一个系统的灵魂所在。数据访问层主要用于完成服务层和数据存储层之间数据的交互。因为数据存储方式的多样性,数据访问层一个重要功能是实现对不同数据存储方式的封装,尽量做到对服务层屏蔽具体的数据存储细节。另一个数据访问层的重要作用,是解决数据存储场景下的资源管理问题,包括连接池资源、分库分表、读写分离等。具体是否需要做分库分表、读写分离等,需要根据应用的实际数据量大小、选择的数据存储方式等做综合考量。
数据存储层
最后,再来考虑数据存储层的搭建和选型。
数据存储层是用于将具体应用产生的数据持久化。不同的数据存储方式适用的业务场景也有很大不同。如对事务要比较高,一般采用关系型数据库如Mysql、Oracle等;对数据结构和表结构扩展性要求较灵活的可以采用NoSQL数据库如MongoDB、CouchDB等;如果需要存储的数据量非常庞大,可以选择目前基于hdfs的存储方案如Hbase、Hive等;还会有对文件、图片等有存储需求的,可以采用分布式文件存储或云存储的方式等。数据存储的方案本身没有好坏之分,具体还是要分析业务的实际需求来做平衡和选择。
关于缓存
除了以上所述的几个层级,在大部分互联网应用中,当系统访问用户量达到一定量级,或QPS较高的场景下,还会通过增加缓存来降低系统和接口的响应时间,提升系统的性能。常用的缓存框架有Redis、Memcache等。在架构设计阶段,我们把整个系统按照分层的思想做拆解说明,根据业务的实际需求,对各层中采用的技术和方案做平衡和取舍。架构设计阶段完成并不意味着整个应用系统架构的完成。接下来需要将架构设计落地到代码中。
05编码阶段
在架构设计方案完成后,对于开发工程师来说,已经可以根据架构设计方案来指导进行详细需求设计和编码。对于架构师来说,在这个阶段需要重点关注的有以下几点:
-
工程结构:良好的工程结构,可以大幅降低后续代码的维护成本和大团队的协作成本。每一层的工程结构也会有所侧重和不同,我们可以在平时沉淀适合自己企业的工程结构脚手架,在需要时一键创建,既能提升效率,还可以统一工程模板。
-
设计模式:针对复杂的需求,架构师需要重点关注,将不同设计模式组合、扩展应用到代码设计中,提升代码的可扩展性和可维护性。
-
代码模板:代码模板可以统一代码风格,提升代码的可读性和可维护性。
-
代码检查:通过对代码的检查,发现不合理的代码组织和设计、潜在的代码隐患等等。也可以借助一些第三方插件或工具如PMD、Findbugs等辅助做检查。
06上线运营阶段
一个好的架构,一定是需要经历线上系统的检验的。在测试人员完成质量测试后,需要对应用系统做上线发布,进入到上线运营阶段。
在上线运营阶段,架构师核心职责是做好各项监控和告警指标的设定。
在监控层面,首选需要对应用系统相关的各项性能指标做监控。通过一些开源或企业自研的监控工具,需要对应用系统所采用的物理资源如磁盘、内存、CPU、网络等进行监控;需要对数据存储组件如数据库的慢查询、I/O、库大小等进行监控;对中间件如RabbitMQ、Redis等读写、容量等进行监控;对应用系统性能如接口响应时长、95线、99线的监控等等。
另外一个重要的监控对象是业务指标的监控,如一段时间内的登陆用户量、短信发送量、订单下单量、订单支付量等等。业务指标的监控往往能反映出一些系统层面的异常,引导去追究引起业务指标异常的根本原因,从而提升系统的可靠性和稳定性。
具体建立哪些监控指标需要根据不同的监控对象进行合理的设定。
建立完监控指标后,还需要建立告警体系,将异常的指标及时告警通知出来。一般的监控系统都会有提供类似短信、钉钉等告警方式的接入。
最后,在上线运营阶段还需要收集和关注系统的日志,及时排查修复异常,保障系统的稳健运行。
以上是从一个业务的需求阶段开始,到整个业务的应用系统上线运营,作为一个架构师需要关注的方方面面。
当然,其中的每一个架构点和技术方案,展开来看都是一个很大的课题。当你具备架构的全局思维之后,接下来就可以深入到不同领域专研,并在工作中不断实践和试错,最终完成自身技能的进阶。
自计算机诞生以来,各项软硬件技术即在不断迭代更新,催生着软件架构的不断升级换代。而随着大数据云计算5G时代的带领,新的业务场景如音视频、直播、物联网等等业务挑战也会随之而来,我们对架构的追求也永无止境。
希望你我都可以在持续的架构演进中,不断学习,持续进步。
相关阅读
一个Java程序员成长之路
聊聊程序员的核心能力
·END·