架构学习
https://www.figma.com/design/E0CliZzdGb0RhAjRYrHlen/Figma-basics?node-id=1669-162202&t=8nwlOX5ET7clbGmc-0
https://aspoem.com/zh-Hans?ref=producthunt
架构学习
架构本质
通过合理的内部编排,保证系统高度有序,能够不断扩展,满足业务和技术的变化。首先,架构的出发点是业务和技术在不断复杂化,引起系统混乱,需要通过架构来保证有序。其次,架构实现从无序到有序,是通过合理的内部编排实现的,基本的手段,就是“分”与“合”,先把系统打散,然后将它们重新组合,形成更合理的关系。
分类
业务架构:概念业务架构就是讲清楚核心业务的处理过程,定义各个业务模块的相互关系,它从概念层面帮助我们理解系统面临哪些问题以及如何处理
应用架构:逻辑应用架构就是讲清楚系统内部是怎么组织的,有哪些应用,相互间是怎么调用的,它从逻辑层面帮助我们理解系统内部是如何分工与协作的
技术架构:物理技术架构就是讲清楚系统由哪些硬件、操作系统和中间件组成,它们是如何和我们开发的应用一起配合,应对各种异常情况,保持系统的稳定可用。
好的架构要求
业务复杂性满足业务的可扩展、可复用
技术复杂性满足系统的高可用、高性能和可伸缩,并尽量采用低成本的方式落地。
好的架构师要求TA 必定是一个出色的程序员,写的一手好代码需要有思维的高度,具备抽象思维能力思维的深度,能够透过问题看本质:一个跨网络调用,知道数据是如何通过各种介质(比如网卡端口)到达目标位置具备良好的沟通能力(感性)良好的平衡取舍能力(理性)
业务架构
概念
产品和业务架构师区别产品经理的职责就是:告诉用户,系统长什么样子;告诉开发,他要实现什么功能。对业务架构师来说,TA 的工作,就是把业务流程和节点打散,按照业务域的维度来划分系统模块,并定义这些模块之间的关系,最终形成一个高度结构化的模块体系。
业务架构目标
业务的可扩展业务的主题是变化和创新,系统的主题是稳定和可靠。
业务的可复用按照业务域来划分业务,把业务流程中的节点拆分到各个业务域,按照业务域构造系统模块
模块要求模块的职责定位要非常清晰模块的数据模型和接口设计要保证通用实现模块的高复用,还需要做好业务的层次划分
可扩展架构
概念
何为系统?即 模块 + 关系对模块的要求:1、定位明确,概念完整;2、自成体系,粒度适中对关系的要求:1、同时简化依赖的方向和减少依赖的数量;2、我们要尽量把网状结构转化为层次结构,模块结构层次化是简化模块依赖关系的有力手段。
如何打造可扩展架构该系统本质是:通过构建合理的模块体系,有效地控制系统复杂度,最小化业务变化引起的系统调整。
实现方法:通过拆分,实现模块划分;通过整合,优化模块依赖关系拆分:一般做业务架构时,我们先考虑垂直拆分,从大方向上,把不同业务给区分清楚,然后再针对具体业务,按照业务处理流程进行水平拆分整合:1、通用化整合:通用化指的是通过抽象设计,让一个模块具备通用的能力,能够替代多个类似功能的模块。2、平台化整合:平台化是把定位相同的模块组织在一起,以组团的方式对外提供服务。对于外部系统来说,我们可以把这些模块看成是一个整体,一起对业务场景提供全面的支撑。
案例一:电商平台的架构演变
单体架构 2000年
特点单体应用内部一般采用分层结构,从上到下,一般分为表示层、业务层、数据访问层、DB 层。单体架构在水平方向上,通过层次化的划分,降低了业务的深度复杂性;不过在垂直方向上,单体应用缺乏清晰的边界,上下层模块之间是多对多的网状依赖关系在单体架构中,模块结构是否合理,很大程度上依赖于开发者的个人水平。
架构图
优点快速落地项目初期,适应快速验证市场
弊端业务系统的体量变大时,弊端就会暴露1、多团队并行开发的话,需要额外的资源来协调不同团队之间的并行开发和上线。2、代码合并和编译非常复杂
分布式 2006年
特点分布式架构,简单来说就是系统由多个独立的应用组成,它们互相协作,成为一个整体。在分布式架构中,API 接口属于应用的一部分,它和表示层共享底层的业务逻辑把一个大系统的业务复杂度,分割成多个小业务的复杂度,从而降低了整体的复杂度。分布式架构适用于业务相关性低、耦合少的业务系统
架构图
优点通过拆分后,各个应用之间的耦合度低,就可以很好地支持多团队的并行开发。
弊端作为应用的开发者,除了要满足自身业务的需求之外,同时还需要考虑外部业务的需求,这两部分经常会打架。每个应用都是从头到尾,自搭一套完整的体系,导致业务之间重复造轮子,造成资源浪费。
SOA 2009年Service Oriented Architecture
传统SOA
目的它解决的是企业内部大量异构系统集成的问题
特点让各个系统通过提供标准的服务,来满足外部调用需求。外部系统通过这个服务访问系统内部,解决不同系统相互集成的问题
架构图
新 SOA
目的它解决的是系统重复建设的问题
特点它通过服务化思想,提供更好的业务封装性,并通过标准技术,能更友好地对外输出业务能力;SOA 服务不依附于某个具体应用,它可以独立地部署和扩展,这样避免了直接影响现有的系统;服务通过封装通用的业务逻辑,可以供所有应用共享,解决了重复造轮子的问题。
架构图
弊端然而 SOA 服务化的思想很好,但在系统实现上比较重,落地比较困难。
微服务 2014年
特点相对于SOA架构,思想是相似的,但是架构更轻,但两者不同的地方在于,微服务是去中心化的,不需要 SOA 架构中 ESB 的集中管理方式。这就需要不同的通讯方式
每个微服务,都是负责端到端的业务包括前端的 UI 展现部分和后端业务逻辑。由这个小团队负责应用的整个生命周期管理。从一定程度上说,微服务叫做微应用,微产品,是拆分得更细的分布式架构微服务强调围绕业务,进行清晰的业务和数据边界划分,并通过良好定义的接口输出业务能力微服务强调所谓的哑管道,通过 HTTP 等简单的技术手段就可以访问微服务强调智能终端,所有的业务逻辑包含在微服务内部,不需要额外的中间层提供业务规则处理
架构图
微服务 = 小应用 + 小服务
实际而言侧重小服务很难把一个大系统,按照端到端业务的方式,拆分为一个个应用需要把现有的职能团队打散后重组,这种人员组织的调整实际上也很难落地。
实践方法:弱化微服务的小应用定位,然后扩大化微服务小服务的定位封装底层基础业务的是共享微服务封装流程的是聚合微服务封装具体业务场景的服务端是应用微服务封装基础中间件(如 Redis 缓存、消息推送)的是系统微服务
中台 2018年
产生背景
概念前台指的是面向 C 端的应用,比如像微信、淘宝这样的应用,后台指的是企业内部系统,比如 ERP、CRM、仓库管理系统等等
冲突前台和后台的特性是不一样的。前台对外,我们知道,消费者的需求快速多变,所以前台需要能快速响应,做到低成本试错;而后台对内,企业内部的业务流程不能经常变,所以后台需要稳定,不能随意调整
解决方案前台和后台是企业 IT 系统的一体两面,它们需要紧密协作,共同服务于企业的业务战略,如何实现前后台的平滑对接,这是一个巨大的挑战,中台架构因此而生
中台适用场景
示意图
要求1、这和公司业务线的数量有关,业务线越多,意味着重复建设的成本会更大,当我们开始上第 3 条业务线时,就应该要考虑转到“山”字形了2、和各个业务线的相似度有关,相似度越高,意味着业务线之间有更多类似的逻辑
优势从业务角度来看,中台收敛了业务场景,统一了业务规则从系统角度看,中台相当于操作系统,对外提供标准接口,屏蔽了底层系统的复杂性从数据角度看,中台收敛了数据,比如使用同一套订单数据模型,让所有渠道的订单使用相同的订单模型总结:通过实现基础业务的平台化,实现了企业级业务能力的快速复用。
中台落地模式
互联网企业
架构图
特点松散的微服务 -> 共享服务体系 -> 中台,这是微服务架构向中台架构的演进过程。基础业务能力由通用基础业务平台来实现;另外,通用聚合服务对基础业务进行组合,进一步提升了业务能力的易用性;而通用中间件平台,通过技术手段保证了业务中台的稳定性,三者一起实现了企业整体业务能力的复用
优点微服务升级为了商品中心、订单中心,每个中心更强调体系化,包括更好的业务通用能力,更好的系统运营能力(如监控、稳定性、性能的强化),更好的业务运营能力(比如商品中心自带配套的商品管理后台)。
传统企业
架构图
特点渠道 & 应用层,这是整个系统的对外部分,包括了各个应用的前端,如 App、小程序、公众号等等应用平台是各个具体应用的母体,它包含了各个应用的服务端,做流程编排和信息的聚合。中间网关实现前后端隔离,具体负责外部访问的安全验证和监控,以及内外部请求的路由和消息格式转换。业务中台是中台架构的核心,它包括一系列的通用基础服务后台包括两部分,第一部分是适配插件,用于连接商户内部系统和中台基础服务;第二部分是企业内部系统,这个是企业的 IT 基础设施,业务最终会在这里落地。
优点中台代表了企业核心的业务能力,它自成体系,能够为 C 端的互联网场景提供通用的能力,并通过各种插件和后台打通
案例二:一号店app端架构演变
可复用架构
概念
复用分类
架构图
技术复用:代码复用和技术组件复用代码复用有自己打包的类库,第三方提供的 SDK,还有各种算法封装等技术组件复用有我们自己封装的,更多的是大量开源的中间件,比如 Redis、MQ、Dubbo 等代码级复用和技术组件复用都属于工具层面,它们的好处是在很多地方都可以用,但和业务场景隔得有点远,不直接对应业务功能,因此复用的价值相对比较低。
业务复用:业务实体复用、业务流程复用和产品复用业务实体复用针对细分的业务领域,比如订单、商品、用户等领域业务流程的复用针对的是业务场景,它可以把多个业务实体串起来,完成一个端到端的任务。如下单流程服务最高层次的复用是对整个系统的复用,比如说一个 SaaS 系统(Software-as-a-Service),它在内部做了各种通用化设计,允许我们通过各种参数配置,得到我们想要的功能;
对比总结从技术复用到业务复用,越往上,复用程度越高,复用产生的价值也越大,但实现起来也越复杂,它能复用的场景就越有限
如何实现?首要划分基础服务边界
完整性原则要尽可能在服务内部封装完整的业务规则,对外提供完整的业务语义,最大程度地简化服务的使用。不能仅仅是DAO在划分服务边界时,要保证服务数据完整、功能全面,这样才能支撑一个完整的业务领域。
一致性原则服务的数据和职责要一致,谁拥有信息,谁就负责提供相应的功能
正交原则基础服务处于调用链的底层,服务之间不会有任何的调用关系,也就是说基础服务相互之间是正交的。服务之间可以有数据的依赖关系,但没有接口的调用关系。
案例一:如何设计一个基础服务
可能遇到问题对服务的边界没有进行很好的划分,结果在落地的过程中,大家反复争论具体功能的归属对业务的了解不够深入,我们要么设计不足,导致同一个服务有很多版本;要么服务过度设计,实现了一堆永远用不上的功能
核心服务边界的划分和功能的抽象设计是核心。服务边界确定了这个服务应该“做什么”,抽象设计确定了这个服务应该“怎么做”
举例:订单业务架构
业务场景图
订单服务边界划分
主要服务基本信息管理订单优惠管理
订单生命周期管理订单服务的状态要做到通用,能够支持各种可能的状态定义和状态转换过程
不包含服务订单服务不主动调用其他服务
订单服务不负责和第三方系统的集成同步功能时特定定制的,不具备通用性,对接功能不在订单服务内部实现,由额外的同步程序实现同步程序主动调用订单服务,在后在和第三方对接
订单服务不提供优惠计算或成本分摊逻辑不负责具体的优惠计算,只提供优惠结果的存储和查询该服务不提供履单详情,不负责详细物流信息的存储
订单服务内部设计
内部数据模型:订单状态通用化
数据模型图
存在问题不同的行业甚至不同的企业,他们对于订单状态管理都是不一样的,订单服务作为一个共享服务,它必须要满足不同项目的订单状态管理
结局方案
开放订单状态定义理论上可以满足各种状态的定义,满足各种状态之间的变化订单状态是完全由外部负责管理的,上层应用的负担会很重,不但要负责定义有哪些状态,而且还要维护状态的转换规则,一不小心,订单可能从状态 A 非法地变成状态 B,导致业务出问题。
评价订单状态是订单业务规则的核心体现,这样的订单服务是没有灵魂的,也失去了大部分业务复用的价值
应用和服务共同管理订单的基本状态,我们称之为“主状态”,它们由订单服务负责定义子状态有哪些具体的取值,不同的项目是不一样的,这个就开放给各个应用来定义。
评价订单服务抓大放小,通过主状态管理把控住了订单的核心业务规则,同时把子状态开放给应用进行管理,为具体的业务场景提供了灵活性。
外部接口
服务接口调用在服务接口命名时,一定要规范和统一,接口名字要能够望文生义,方便调用者快速找到所需要的接口
如何设计查询接口粗粒度接口,只返回订单最基本的 7-8 个字段中粒度接口,返回订单比较常用的十几个字段细粒度接口,返回订单的详细信息
异步消息通知胖消息包含了尽可能多的字段,但传输效率低瘦消息只包含最基本的字段,传输效率高
案例二:如何对现有系统做微服务改造
微服务化改造面临的挑战面临的一般是巨大的单体应用,底层的数据表集中放在一个数据库里,这些表加起来可能有几百张应用和数据表紧密耦合在一起,代码模块和表是多对多的依赖关系表都在一个数据库里,开发人员往往会随意对表做关联改造不能影响业务的稳定性
一号店“库存”改造案例
背景所有商品相关的表都存在产品库里面,数量超过了上百张;(表多)商品是电商业务的核心,几乎所有的前后台系统都需要访问这个产品库;(压力大)对这些表的访问是怎么方便怎么来,有些 SQL 语句会对大量的表做 Join 关联。(使用乱)
存在问题从应用方面来说,各个系统功能重复建设;另外,如果修改了库存表的某个字段,这些系统同时会受影响,正所谓牵一发而动全身。从数据库方面来说,数据库的可用性是比较差的,如果某个系统有慢查询,它就很可能拖垮整个产品数据库;另外,数据库的连接数也经常不够用。
目标首先是对这个大数据库按照业务维度进行垂直拆分,比如分成产品数据库、库存数据库、价格数据库等等然后基于这些拆分后的库,构建微服务,以接口的方式来支持数据库表的访问最后将各个业务系统统一接入微服务,最终完成整个商品体系的微服务化改造。
改造步骤
改造流程图
准备阶段
圈表圈表就是用来确定库存微服务具体包含哪些表,也就是确定服务的数据模型;库存微服务就负责这些表的访问,当然,库存微服务也不会访问其它的表一方面要满足所有的库存访问需求,这些表之间关系紧密,和其它的表关联不大;另一方面,这些表的数量不能太多,一般不超过十几张。
改造系统从圈表开始的原因表是现成的,它比业务功能要直观很多,所以从表入手比较高效;从下往上确定服务边界如果从表入手,构造的服务和表是对应的;避免表字段的拆分带来额外的复杂性为了避免一下子引入太多变化,我们先不对库存的表结构进行调整
收集SQL我们会收集所有业务系统访问这些表的 SQL 语句,包括它的业务场景说明、访问频率可以根据业务场景和访问频率设计查询方法,设计缓存等
SQL拆分各个业务团队先进行拆分,保证最后提供给服务开发团队的 SQL,只包含访问库存的相关表
实施阶段
微服务开发接口设计、代码开发、功能测试对业务方提供的 SQL 进行梳理,然后对接口做一定的通用化设计,避免为每个 SQL 定制一个单独的接口,以此保证服务的复用能力
服务接入存服务经过功能和性能验证以后,会由各个业务开发团队逐步接入,替换原来的 SQL 语句
独立数据库所有的 SQL 语句都被替换后,业务系统已经不会直接访问这些库存的表;我们就可以把库存相关的表,从原来的产品库中迁移出来,部署成为一个物理上独立的数据库通过库存表独立成库,我们可以从物理层面,切断业务团队对这些表的依赖,同时,也可以大幅度降低产品库的压力
改造前后架构图对比
总结除了做好库存服务本身的设计开发工作,相关团队之间的配合也是非常重要的,需要团队之间沟通和确认从项目推进的角度来看,这种核心服务的改造,很多时候都是技术一把手工程基于现有系统进行改造和全新的服务设计是有所不同的,我们不能追求理想化和一步到位,而是要考虑到系统的平滑过渡,先实现微服务的顺利落地,后续再考虑各种优化
一个高复用和扩展平台架构图
案例三:中台如何练成
升级中台因素业务上有什么重大变化,导致当前系统的弊端已经很明显,不能适应业务发展了如何在业务、系统、资源三者之间做好平衡,对系统进行分步式的改造
订单系统改造案例
背景公司作为供应商,为大型餐饮连锁企业打造 O2O 交易平台,包括三方聚合外卖、自有小程序、App 点餐,这些线上用户的订单最终会落到门店的收银系统,由门店进行履单。
改造经历
聚合外卖订单架构
架构图
特征外卖同步接口负责和第三方外卖平台对接,它主要是针对不同的外卖平台做接口适配,POS 接口负责和门店的收银系统对接,系统对接。这两个模块都是使用同一个外卖订单数据库从数据模型上看,系统的订单模型也是完全按照外卖订单的需求设计的,订单状态管理也相对比较简单,因为这些订单都是用户在第三方外卖平台已经完成支付的从系统架构上看,外卖系统从外卖平台接单,然后把订单推送给后面的收银系统,只需要一个应用、一个数据库、两套接口就可以支持,使用单体架构就能很好地满足外卖的接单需求
小程序下单架构
架构图
特征不同于三方外卖订单,小程序下单平台是一个完整的业务,它包括小程序用户注册、商品和菜单浏览、商品加购物车、在线支付等等方案是一个比较务实的选择,通过复用外卖订单的履单通路,我们也实现了小程序订单的闭环处理。
优缺点我们节省了重新搭建系统的成本,也快速落地了小程序交易这条新业务线。我们把小程序的订单硬生生地套在了外卖订单的模型里,这样限制了小程序订单能力的扩展小程序订单处理链路过长,一共包含了 8 个处理环节,系统整体的性能和可用性都存在很大问题,存在消息队列堵塞使用了消息队列在两个库之间同步订单数据,这降低了系统整体的稳定性。实践中,也发生过多起消息队列故障导致的线上事故
统一订单服务架构
架构图
特征原来外卖和小程序各自有一个订单库,现在合并为了一个订单库,由这个订单服务统一对外提供订单数据的访问和状态管理原来外卖系统的两个模块“外卖同步接口”和“POS 接口”,升级为了两个独立的应用。外卖同步接口变成外卖同步服务,对接外卖平台;POS 接口变成 POS 服务,对接门店的收银系统。它们都是通过统一订单服务存取订单数据。经过升级,新的架构具备了明显的层次结构,自上而下分为三层;这里就不仅仅是一个外卖订单和小程序订单的处理平台,而是升级成了一个完整的全渠道交易平台订单处理的链路大大缩短统一订单服务实现了统一的订单属性定义、统一的订单状态管理,以及订单数据的集中存储,这对后续的 BI 分析和数据中台建设非常有帮助
中台架构
架构图
形成方法按照上述订单服务逻辑,可以形成商品服务,促销服务,库存服务通过构建这样一系列的共享服务,我们就实现了各个渠道业务规则和业务数据的统一管理,最终我们落地了一个强大的业务中台,可以很方便地扩展各个业务,实现企业整体业务能力的复用。
特征前端有 3 个业务场景,分别是小程序点单、App 商城下单、外卖平台下单,每个业务场景都有相应的服务端负责对接在各个服务端下面,还有一些辅助的应用,如购物车、秒杀、拼团等等。同时这里还有一个订单控制服务(Order Control Service,OCS),负责订单逻辑的编排以及前后台之间的状态同步,可以把它看作是基础服务之上的聚合服务。再底下就是核心的业务中台,它由 9 大服务中心组成,这些中心和商户内部系统进行对接。其中,商品中心和库存中心对接 ERP 系统,会员中心对接 CRM 系统,订单中心对接 POS 收银系统,这里的对接分别由对应的适配插件负责中台由各个通用的基础服务构成,它是相对标准的;而插件是定制的,具体和每个企业的后台系统有关
技术架构
概念
高可用架构
概念
系统的故障点资源不可用,包括网络和服务器出故障,网络出故障表明节点连接不上,服务器出故障表明该节点本身不能正常工作资源不足,常规的流量进来,节点能正常工作,但在高并发的情况下,节点无法正常工作,对外表现为响应超时。节点的功能有问题,这个主要体现在我们开发的代码上,比如它的内部业务逻辑有问题,或者是接口不兼容导致客户端调用出了问题;另外有些不够成熟的中间件,有时也会有功能性问题
系统故障解决思路避免发生:我们可以通过 UPS(Uninterruptible Power System,不间断电源)来避免服务器断电,可以通过事先增加机器来解决硬件资源不足的问题。故障转移:比如说,我们可以通过冗余部署,当一个节点发生故障时,用其它正常的节点来代替问题节点。降低影响:比如说流量太大,我们可以通过限流,来保证部分用户可以正常使用,或者通过业务降级的手段,关闭一些次要功能,保证核心功能仍旧可用。快速恢复:我们要尽快找到问题的原因,然后修复故障节点,使系统恢复到正常状态。ps:处理线上事故的首要原则是先尽快恢复业务,而不是先定位系统的问题,再通过解决问题来恢复系统。
高可用架构原则
正面保障
冗余无单点我们要保证系统的各个节点在部署时是冗余的,没有单点
水平扩展很多时候,系统的不可用都是因为流量引起的:在高并发的情况下,系统往往会整体瘫痪,完全不可用。
减少损失
柔性事务在很多业务场景中,系统的可用性比数据的实时一致性更重要
系统可降级限流:让部分用户流量进入系统处理,其它流量直接抛弃降级:系统抛弃部分不重要的功能,比如不发送短信通知,以此确保核心功能不受影响。熔断:我们不去调用出问题的服务,让系统绕开故障点功能禁用:针对具体的功能,我们设置好功能开关,让代码根据开关设置,灵活决定是否执行这部分逻辑。比如商品搜索,在系统繁忙时,我们可以选择不进行复杂的深度搜索。
做好监控
系统可监控通过监控,我们可以实时地了解系统的当前状态
高可用手段
客户端 -> 接入层首先要解决网络的可用性问题:可以拉多条线路,比如在企业私有的 IDC 机房和公有云之间,同时拉移动和电信的线路可以选择 Nginx、HAProxy、LVS 等负载均衡软件,它们都能很好地支持双节点 +Keepalived 部署通过冗余和自动切换避免了单点的故障
接入层 ->Web 应用Web 应用通常是无状态的,我们可以部署多个实例,很方便地通过水平扩展的方式,提升系统的处理能力接入层的负载均衡设备,可以通过各种算法进行多个 Web 实例的路由,并且对它们进行健康检测,如果某个实例有问题,请求可以转发到另一个实例进行处理,从而实现故障的自动转移。还可以在接入层做限流,比如,在 Nginx 中设置每秒多少个并发的限制,超过这个并发数,Nginx 就直接返回错误同时支持了 Web 节点的水平扩展、自动故障转移以及系统的可降级(限流)
Web 应用 -> 内部服务服务通常也是无状态的,我们也可以通过部署多个实例进行水平扩展可以使用传统的代理服务器方式,进行请求分发很多的微服务框架本身就支持服务的直接路由,比如在 Spring Cloud 中,我们就可以通过 Eureka 进行服务的自动注册和路由我们可以为不同服务配置不同的线程池,实现资源隔离,避免因为一个服务响应慢,而占用所有的线程资源;如果某个服务调用失败,我们可以对它进行熔断操作,避免无谓的超时等待,影响调用方的整体性能。针对具体的功能,我们还可以做一些功能开关。开关实际上是一个标志变量,它的值可以是 on/off, 我们在代码中可以根据它的值,来确定某一段逻辑是否要执行。开关的值可以在数据库或配置系统里定义,这样我们就能够通过外部的开关值,控制应用内部的行为,同时支持了服务节点的水平扩展、自动故障转移以及系统的可降级(熔断和业务开关)。
访问基础资源
关系数据库主从部署,一方面通过读写分离,提升数据库读的性能,减轻主库压力另一方面,数据库有成熟的 MHA 方案,支持主库故障时,能够自动实现主从切换,应用可以通过 VIP 访问数据库物理的水平分库方式,对数据进行分片,这样就有多个主库支持写入
缓存数据读写比很高的情况下,我们可以利用缓存优化数据库的访问性能,包括进程内部缓存和分布式缓存,缓存是应对高并发的有效武器。通过多节点支持处理能力的水平扩展,通过数据的多副本来支持故障转移
消息系统通过多节点部署来支持处理能力的水平扩展,也能通过数据的多分区,实现故障的自动切换
案例一:实现O2O平台24小时在线
案例二:如何第一时间知道系统哪里有问题
案例三:打造一体化监控系统
高性能和可伸缩架构
概念
系统性能的挑战系统的 TPS 很低,只要流量一大,系统就挂,加机器也没用;机器的资源利用率很低,造成资源严重浪费。
常用的性能数据
总结图内存的数据读取是 SSD 磁盘的 10 倍,SSD 磁盘又是普通磁盘的 10 倍,一个远程调用的网络耗时是机房内部调用的 1000 倍,一个分布式缓存访问相对于数据库访问,性能也有数十倍的提升。
高性能策略
背景对于一个实际的业务系统来说,情况就会复杂很多。一个外部请求进来,需要经过内部很多的软硬件节点处理,用户请求的处理时间就等于所有节点的处理时间相加。只要某个节点性能有问题(比如数据库),或者某项资源不足(比如网络带宽),系统整体的 TPS 就上不去。这也是在实践中,很多系统 TPS 只有个位数的原因。
策略
加快单个请求处理优化处理路径上每个节点的处理速度。比如说,我们可以在代码中使用更好的算法和数据结构,来降低算法的时间和空间复杂度;可以通过索引,来优化数据库查询;也可以在高读写比的场景下,通过缓存来代替数据库访问等等并行处理单个请求。我们把一个请求分解为多个子请求,内部使用多个节点同时处理子请求,然后对结果进行合并。
同时处理多个请求当有多个外部请求进来时,系统同时使用多个节点来处理请求,每个节点分别来处理一个请求,从而提升系统单位时间内处理请求的数量。在同一个节点内部,我们还可以利用多进程、多线程技术,同时处理多个请求。
请求处理异步化系统处理请求不一定要实时同步,请求流量的高峰期时间往往很短,所以有些时候,我们可以延长系统的处理时间,只要在一个相对合理的时间内,系统能够处理完请求就可以了,这是一种异步化的处理方式。我们在处理核心业务时,把相对不核心的逻辑做异步化处理,也是这个思路。比如说下单时,系统实时进行扣库存、生成订单等操作,而非核心的下单送积分、下单成功发消息等操作,我们就可以做异步处理,这样就能够提升下单接口的性能。
总结首先,我们要加快单个请求的处理。单节点性能提升是系统整体处理能力提升的基础,这也是我们作为技术人员的基本功。但这里的问题是,节点的性能提升是有瓶颈的,我们不能超越前面说的基础操作的性能。至于把请求分解为多个小请求进行并行处理,这个在很多情况下并不可行,对多个请求进行同时处理是应对海量请求的强有力手段,如果我们能够水平扩展每一个处理节点,这样在理论上,系统处理请求的能力无限的。而这里的问题是,对于无状态的计算节点,我们很容易扩展,比如说 Web 应用和服务;但对于有状态的存储节点,比如说数据库,要想水平扩展它的处理能力,我们往往要对系统做很大的改造。异步化处理,在某些场景下是很好的提升系统性能的方式,我们不用增加机器,系统就能够完成请求的处理。但问题是,同步调用变成异步的方式,往往会导致处理结果不能实时返回,有时候会影响到用户体验,而且对程序的改造也会比较大。
可伸缩策略
背景我们经常说,业务是可运营的,而实际上,系统也是可运营的。我们可以动态地调整系统软硬件部署,在业务高峰期增加软硬件节点,在业务低谷期减少软硬件节点,这就是系统的可伸缩能力。
策略
节点级别的可伸缩对于无状态的节点,我们直接增减节点就可以了。比如说订单服务,白天我们需要 10 台机器来提供服务,到了半夜,由于单量减少,我们就可以停掉部分机器。如果做得好,我们还可以实现弹性伸缩,让系统根据硬件的负载情况,来确定机器的数量。比如说,当服务器的 CPU 或内存使用率在 10% 以下了,系统就自动减少服务实例的数量。而对于有状态的服务,我们需要能够支持状态数据的重新分布。比如进行水平分库的时候,要从 4 个库增加到 8 个库,我们需要把原先 4 个库的数据,按照新的分库规则,重新分布到 8 个库中。如果这个调整对应用的影响小,那系统的可伸缩性就高。存在的问题:系统是一个整体,如果只是节点级别的伸缩,我们可能要对多个节点分别进行操作,而且不同节点的资源配置会相互影响,这样对各个节点的调整就非常复杂,影响了系统的可伸缩能力。如果能实现系统端到端的伸缩,同时对多个节点进行伸缩处理,那系统的可伸缩能力就更高了。
系统级别的可伸缩把多个处理节点打包在一起,形成一个处理单元。通过单元化处理,我们把相关的节点绑定在一起,同进同退,更容易实现系统的可伸缩。
举例
总结如果我们把单元扩大到系统的所有节点,这就是一个虚拟机房的概念。我们可以在一个物理机房部署多个虚拟机房,也可以在不同的物理机房部署多个虚拟机房,这样,部署系统就像部署一个应用一样,系统的可伸缩性自然就更好。
高性能和可伸缩架构原则
可水平拆分和无状态这意味着节点支持多实例部署,我们可以通过水平扩展,线性地提升节点的处理能力,保证良好的伸缩性以及低成本。
短事务和柔性事务短事务意味着资源锁定的时间短,系统能够更好地支持并发处理柔性事务意味着系统只需要保证状态的最终一致,这样我们就有更多的灵活手段来支持系统的高性能,比如说通过异步消息等等。
数据可缓存缓存是系统性能优化的利器,如果数据能够缓存,我们就可以在内存里拿到数据,而不是通过磁盘 IO,这样可以大大减少数据库的压力,相对于数据库的成本,缓存的成本显然也更低。
计算可并行如果计算可并行,我们就可以通过增加机器节点,加快单次请求的速度,提高性能。
可异步处理异步处理给系统的处理增加了弹性空间,我们可以利用更多的处理时间,来降低系统对资源的实时需求,在保证系统处理能力的同时,降低系统的成本。
虚拟化和容器化虚拟化和容器化是指对基础资源进行了抽象,这意味着我们不需要再依赖具体的硬件,对节点的移植和扩容也就更加方便。虚拟化和容器化对系统的资源切分得更细,也就说明对资源的利用率更高,系统的成本也就更低
高性能架构案例
背景在 2014 年的时候,1 号店作为网上超市类电商,经常在线上举行各种大促活动。比如进口牛奶促销活动,每次促销的牛奶有几十万盒,促销价格非常优惠,一般这样的促销活动会在某个整点的时间进行开卖(如上午 10 点)这本质上是一种秒杀活动,但商品数量非常大,一瞬间会有大量的用户流量涌入,流量可以高达平时的几十倍。而且和少量商品的秒杀不同,这些都是有效流量,最终会生成订单。而在正常情况下,系统因为资源有限,只能处理 10% 的流量,无法处理剩下的 90% 流量,瞬间高并发的流量涌入,很大程度上会引起后台系统超时报错,导致用户下单不成功。最终的结果就是,系统往往由于过载,整体处理能力下降,甚至瘫痪,导致所有用户都无法购买。
为什么加机器不行我们对峰值流量的预估以及要加多少机器都是拍脑袋的,和实际出入往往很大,一旦估计少了,系统同样会面临过载的风险;为了短暂的几分钟促销,我们需要增加大量的机器,事先要做很多的运维准备工作,不但浪费资源,而且效率很低;最为关键的是,有些处理节点,系统不是通过加机器就能扩展处理能力的,比如商品库存数据库,下单时,我们需要扣库存,而为了防止库存更新冲突,我们需要锁定库存记录,导致系统的并发处理能力有限,这个问题单靠加机器是解决不了的。
解决方案
总结该活动特点短期 只有1-2分钟短期无法处理大量订单,可以延长处理订单时长用户还是愿意为买到等待一会得出结论:可以利用异步处理的思路来应对秒杀活动
方案架构图
关键点用户在商品详情页提交订单后,这个订单会作为预订单进入排队区,同时排队系统会返回用户一个排队编号,这个编号用于跟踪后续的订单处理进度;用户被引导到一个等待页,这个页面会根据排队号,定时地查询排队系统,排队系统会返回预订单在队列中的位置信息,包括它前面还有多少未处理的预订单,以及后台系统大概还要多久会处理这个预订单,这样用户就不会焦虑;在排队系统的处理区,有很多消费者,它们依次从排队区的队列里获取预订单,然后调用后台下单系统生成实际的订单;随着预订单变成正式的订单,队列里的预订单会逐渐变少,如果当前的预订单已经从队列里被移除了,用户的等待页就会检测到这个情况,页面自动跳转到订单完成页,这就和常规的购物流程一样了,用户进行最后的支付,最终完成整个前台下单过程。这种方式比较适合瞬时有高并发流量的场景,单高峰会持续一段较长的时间,而用户对订单处理又有比较高的时间要求,那就不适合采用这种异步削峰的方式。
内部设计针对队列的技术选型,排队系统使用的是 Redis,而不是 MQ。因为相对于 MQ 来说,Redis 更轻量级,性能更好,它内置了队列数据结构,除了和 MQ 一样支持消息的先进先出以外,我们还可以获取队列的长度,以及通过排队号获取消息在队列中的位置,这样我们就可以给前端反馈预订单的处理进度。关于队列的调度问题,也就是消费者优先从哪个队列里拿预订单,排队系统会结合下单时间和队列的长度来确定,以保证用户合理的时间体验。关于队列长度,为了保证用户能够买到商品,我们并不是把所有前台的下单请求都会放到队列里,而是根据参与活动的秒杀商品数量,按照 1:1 的比例,设置队列初始长度,这样就保证了进入队列的请求最终都能生成订单。这里我们为每个秒杀商品提供一个单独的队列,这样就可以分散数据在 Redis 中的存取,多个队列可以提供更好的性能。
其他优化点对于秒杀活动来说,参与活动的商品种类是有限的,但这些商品库存的扣减非常频繁,因此我们建立了活动库存的概念,把少量参与促销的商品种类单独放在一个库里,避免和大量常规的商品放在一起,这样也大幅度地提高了库存数据库的读写性能。
可伸缩架构案例
一个综合案例:互联网平台技术架构演变一个实际的电商系统很复杂,在案例介绍中,为了简化,我用比较有代表性的交易系统和账户系统来代表整体的电商系统,并具体分析这两个系统在电商平台发展过程中,它们都碰到了什么瓶颈,以及我们在技术架构上是如何解决的。
单体系统
架构图
特点所有的代码都打包在一个应用里,部署的时候会有多个实例,我们通过负载均衡,把用户请求分发到具体的实例中所有的数据表还在一个数据库里。
存在问题单体应用的所有代码都放在一起,代码编译需要很长时间,应用启动也需要很长时间,并且代码相互依赖,开发效率低,并行开发困难。
SOA架构
架构图
特点针对单体应用体量过大带来的问题,我们对系统进行拆分,把整体系统分为多个子系统底层数据库还没有拆分,两个系统还是访问同一个数据库。系统整体就变成了 SOA 架构,这样,我们减少了代码冲突,系统的开发也变得更加高效,部署的时候,我们也更容易针对各个系统的处理能力进行水平扩展。
存在问题内部服务通过中心化的负载均衡进行访问,中心化的负载均衡增加了服务的调用时间。在电商场景下,内部的服务很多,服务调用的频率很高,每秒可能有上百万次,导致了负载均衡的连接能力不够而且负载均衡是单点,如果它出了问题,很容易引发系统整体的可用性问题(即使负载均衡是多实例,当系统流量很大时,也会因为某台负载有问题,导致其他节点压力增大而引起雪崩效应)。
服务调用去中心化
架构图
特点针对内部服务路由中心化的问题,我们去掉了内部的负载均衡,加入了服务注册中心,比如 ZooKeeper。当服务实例启动或退出时,它们会自动在注册中心进行注册或销毁,服务的客户端和注册中心保持长连接,可以实时地获取可用的服务列表然后在客户端,根据相应的算法选择服务实例,直接调用服务。每次调用无需经过注册中心,如果注册中心有问题,也只是新的服务实例无法注册,或者是已有的服务实例无法注销,这对客户端调用服务的影响是非常有限的。通过注册中心和更体系化的微服务框架,我们还可以实现完善的服务治理,包括服务隔离、熔断、降级,这些都是对原来简单的负载均衡方式的加强,能够进一步提升服务的可用性。
存在问题单个数据库(比如 Oracle)的性能和储存容量已经无法满足需求了。这个时候,我们就需要对数据库进行改造,提升它的处理能力。
垂直分库
架构图
特点对于单个数据库性能和容量瓶颈,解决的办法就是,我们对数据库进行垂直拆分,按照业务拆分为交易数据库和账户数据库,这样就可以满足它们各自的容量和性能需求,同时也避免了不同业务数据表之间的相互耦合
存在问题不过,新的问题又来了,垂直分库后,每个数据库都是单实例。随着业务的发展,和原来系统只有单个数据库类似,现在交易系统也只有一个数据库,它的性能和容量还是有问题,并且数据库单实例也带来了可用性的问题,如果数据库挂了,相应的系统也就不可用。
水平分库及高可用部署
架构图
特点针对单个数据库的可用性问题,我们可以采用 MHA 高可用(Master High Availability)方式部署。比如数据库部署一主多从,通过 MHA 机制,我们可以实时检测主库的可用性,如果主库有问题,系统会自动 Failover(故障转移)到最新的从库。另一方面,我们还可以利用多个从库支持读写分离,减轻主库的访问压力。针对单个数据库的性能和容量问题,首先我们可以引入缓存,在高读写比的场景下,让应用先访问缓存,大大减轻对底层数据库的压力。然后,我们可以对数据库按照某个维度(比如用户维度),进行水平拆分,把数据记录分布到多个实例中,最终分散主库的写压力以及数据存储的瓶颈我们还可以提供多套水平分库。比如说,针对交易数据,我们可以同时按照用户维度和商户维度进行水平分库,用户维度的库用于前台用户下单的场景,商户维度的库用于后台商家履单的场景。这里,只有用户维度的分库会支持写,我们通过数据同步机制,把用户维度分库的更新同步到商户维度的分库里。
存在问题当系统体量发展到了一定程度,我们又碰到了新的问题:单个机房的服务器不够用,无法在同一个机房找到更多的机器部署交易系统和账户系统。
多机房部署
架构图
特点对于单机房服务器不够的问题,我们可以在新的机房部署交易系统和账户系统,为了落地方便,所有服务还是注册到旧机房的注册中心,数据还是存放在旧机房的交易数据库和账户数据库。 这样,我们通过在新机房部署应用,对应用节点进行水平扩展,从而解决了单机房机器不足的问题。
问题产生了跨机房访问的问题:首先,我们只有一个服务注册中心,服务实例一部分部署在老机房,一部分部署在新机房,对于服务调用者来说,它会同时访问新旧机房的服务实例;其次,数据库部署在老机房,新机房的应用会访问旧机房的数据库。根据机房物理距离的不同,跨机房访问的网络延时在数十毫秒到数百毫秒之间,是机房内部通信耗时的上千倍,这会对应用的性能产生很大影响,而且跨机房的网络可用性也经常是一个问题。
服务调用本地化
架构图
特点在新机房也单独部署了服务注册中心,让每个机房的服务注册到同机房的注册中心。这样,客户端的服务调用会路由到同机房的服务端,实现了服务调用的本地化,大大降低了跨机房通信带来的延时和不可用性问题。
问题随着业务越来越复杂,新的问题又来了:交易系统会依赖很多周边服务。比如下单后,我们需要给用户送积分,交易系统会同步调用积分服务。但是同步调用积分服务,一方面会影响下单的性能,另一方面如果积分服务不可用,会导致核心的下单功能失败。
依赖分级管理
架构图
特点对于外部服务依赖的可用性问题,我们的解决办法是,针对这些外部依赖进行分级管理,根据依赖功能的重要性不同,把它们分为强依赖和弱依赖。对于强依赖,我们实时同步调用,比如在用户下单时调用库存服务,由于库存非常重要,必须实时扣减,如果调用库存服务失败,下单也失败。对于大量的弱依赖,我们以异步消息的方式进行信息同步,比如对于积分服务,可以通过柔性事务来保证数据的最终一致性,这样大大提升了核心系统的性能和可用性。
问题新机房的交易系统和账户系统都在访问老机房的数据库,有跨机房数据库访问的性能问题,以及老机房整体故障带来的可用性问题。比如说,机房断电,通信光纤有问题或者发生自然灾害,导致老机房整体不可用,这就会导致所有系统都不可用。
多机房独立部署
架构图
特点针对机房整体不可用的问题,解决方案是,我们在多个机房做对等的部署,这样每个机房的系统可以形成内部闭环,包括服务、注册中心和数据库,机房之间不产生直接的相互依赖,从而实现了机房级别的水平部署。如果系统的单元化做得完善,我们还可以进一步支持虚拟机房的概念,一个物理机房可以部署多个虚拟机房,每个虚拟机房包含了一个完整的系统。通过多机房独立部署,我们极大地提升了系统的可用性、处理能力和可伸缩性,可以应对系统面临的各种异常情况。
总结篇
架构原则汇总
汇总图
可回滚/可禁用可回滚原则确保了系统可以向后兼容,当系统升级出现问题的时候,我们可以回滚到旧版本,保证系统始终可用。如果数据库的新旧表结构差异很大,除了回滚代码,我们还要回滚数据库,这样操作起来往往需要很长时间,系统的可回滚性就比较差。可禁用原则要求我们提供功能是否可用的配置,在系统出现故障时,我们能够快速下线相应的功能。比如说,新的商品推荐算法有问题,我们可以通过程序开关禁用这个功能。
使用成熟的技术一方面,要从系统的稳定性出发,尽量选择成熟的技术,避免因为新技术的坑而导致系统可用性出现问题;另一方面,选择成熟的技术也意味着选择了团队熟悉的技术,这样学习成本低,落地快。
使用同质化硬件我们选择同样的 CPU 和内存配置,以及同样的操作系统版本,这样我们更容易通过统一的自动化脚本,对节点进行配置,对系统做水平扩展时也会更加容易。
架构落地过程
架构师职责构师的职责就是负责设计架构,并跟踪架构的实施过程,解决过程中出现的疑难问题,确保架构顺利落地
架构师工作阶段架构师要和产品经理或者业务人员沟通,了解业务;和开发人员沟通,了解系统。
设计方案架构师针对业务需求,分解相应功能到现有的各个系统,把系统的各个部分串起来,这个第一版的方案至少要能够在表面上解决当前的问题,这样就形成一个草根的方案。架构师要进一步深入思考业务的本质,对现有的草根方案进行升华,比如说,通过抽象,让方案更加通用,可以解决多个类似的或潜在的业务需求,这样,草根的方案就变成了一个高大上的方案,这里很考验架构师的透过问题看本质和抽象总结的能力基于现有的各项约束,比如时间、资金和人员技术能力等因素,架构师要对方案进行简化,把高大上的方案变成一个接地气的方案,以最小的代价实现最大的价值,这里很考验架构师的平衡取舍能力进行宣讲,架构师需要说服相关的人员接受方案,并且在后续的方案执行中,负责跟踪架构的落地,如果过程中有疑难问题,架构师还要协助解决。
架构师知识结构
知识结构图
具体技术第一部分是开发相关的基本知识,比如数据结构和算法、具体的开发语言、常用的设计模式以及开发框架等等,这样你就具备了基本的开发能力第二部分是各种中间件知识,常用的中间件包括数据库、缓存、消息系统、微服务框架等等,对于这些核心中间件,我们不但要了解具体的用法,还要深入理解它们的适用场景。这样你就能写出高效健壮的代码,能够独立承担一个子系统的开发。你还要学习分布式系统相关的知识,包括底层网络和分布式通信技术,这样你就可以了解系统是怎么连接在一起的。除此之外,你还要了解一些周边的系统,比如大数据平台、运维监控系统、接入系统等等,这样,你就可以了解系统端到端的运行过程,从技术架构上保证系统的稳定可用。
架构师成长路径
java为例的学习内容
初级开发阶段你需要深入学习数据结构和算法,并且一定要深入掌握单体应用的分层架构,因为这是架构设计的基础。对 JDK 的一些核心类,你不能仅仅停留在使用层面,而是要深入研读源代码,了解它的内部设计。这样你就知道如何开发一个高效的程序,如何进行各种代码级的调优
高级开发阶段你需要非常了解设计模式,每个设计模式都可以看做是一个小型的架构设计,这里面有很好的设计原则和抽象思维,你在做系统设计时可以借鉴它们。你需要非常了解核心的中间件,包括 DB、微服务框架、缓存和消息系统,要清楚地了解它们的适用场景(比如消息系统的削峰、解耦和异步),知道如何对它们进行调优,以及了解它们都有哪些常见的坑等等,核心中间件是我们做技术选型的基础。你要深入掌握数据库设计和服务接口设计,了解它们的最佳设计实践,它们承载了系统核心的业务数据和业务逻辑。你需要进一步研读源码,源码是活的教材,它包含了大量实用的设计原则和技巧。这里我建议你选择一些开源的开发框架和 RPC 通信框架,去深入了解它们内部的实现原理,比如 Spring 和 Netty。
架构师阶段你需要深入了解网络通信,比如说网络分层和 HTTP/TCP 协议,还有各种常见的 RPC 通讯框架,了解它们的特性和适用场景,这样你在设计分布式系统时,就能够进行合理的技术选型。了解底层系统,包括 JVM、操作系统和硬件原理,再往上延伸到系统的接入部分,了解常见的负载均衡特性和用法需要熟练掌握各种设计工具和方法论,比如领域驱动设计和 UML,了解常用的架构设计原则
大师阶段还要对运维和监控有深入的认知了解业界的架构实践,跟踪技术的发展趋势,如果出来一项新技术,你可以比较准确地对它进行定位,把它纳入到自己的能力体系当中
架构师境界
第一层看山不是山刚接手项目的时候,你对业务还不太了解,经常会被业务方冒出的术语弄得一愣一愣的,如果把现有问题比作山,那就是横看成岭侧成峰,你根本摸不透,此时看山不是山。
第二层看山是山经过业务梳理和深入了解系统以后,你能够设计出一个简单的方案,把各个系统串起来,能解决当前的问题,对当前的这个“山”能够看清楚全貌,此时就做到了看山是山。
第三层看山不是山通过进一步抽象,你能够发现问题的本质,明白了原来这个问题是共性的,后续还会有很多类似的问题。然后你就对设计进行总结和升华,得到一个通用的方案,它不光能解决当前的问题,还可以解决潜在的问题。此时,你看到的已经是问题的本质,看山不是山
第四层看山是山最后回到问题本身,你能够去除过度的抽象,给出的设计简洁明了,增之一分嫌肥,减之一分嫌瘦,既能解决当前的问题,又保留了一定的扩展能力,此时问题还是那个问题,山还是那个山

浙公网安备 33010602011771号