系统架构之演化
互联网发展的二十多年里,后端的架构也经过了多轮改变来适应不同的场景需求。
- 单体架构
- 分布式架构
- 微服务架构
- 服务网格
单体架构
早期的Web 系统,刚开始的业务简单、用户量不大,业务数据量增长也缓慢,加上并发不大,通常用单一一台性能好的物理服务器,作为Web应用服务器、数据库服务器、文档服务器等,集中部署在一起,就可以解决系统的可用性、数据一致性、实时性等。
优点:
- 调试简单,部署快,适用于个人项目或小公司。
缺点:
- 复杂性高,单体架构中协议解析,业务逻辑,存储管理都在一个程序中,整个项目包含的模块非常多、模块的边界模糊、依赖关系不清晰、代码质量参差不齐。随着项目迭代,人员更迭,项目越来越大,积累的技术债务越来越多,重构越来越难。形成恶性循环。
- 单机性能有限,随着业务扩展、数据扩展、并发增高,服务器性能出现瓶颈,都可能造成应用系统性能低下,用户体验差等。
- 扩展能力受限:单体应用只能作为一个整体进行扩展,无法根据业务模块的需要进行伸缩。例如,应用中有的模块是计算密集型的,它需要强劲的CPU; 有的模块则是IO密集型的,需要更大的内存。由于这些模块部署在一起,不得不在硬件的选择上做出妥协。
分布式架构
业务不断发展扩大以及流量并发变多,数据量不断增大而性能出现瓶颈的时候,单体架构就无法适用了。z这时单机服务变成了集群服务,后端架构进入了分布式时代。
在接入层进行负载均衡,多台服务器通过负载均衡同时向外部提供服务,解决单台服务器处理能力和存储空间上限的问题。水平层面将接入服务器、应用服务器、数据服务器分离,垂直层面将业务拆分成用户服务、商品服务、账单服务等,下面介绍两种经典的分层结构。
- 水平分层
- 垂直分层(SOA面向服务架构)
分层是架构设计中重要的一个原则,层与层之间解耦,让每一层更专注于它本身的职责。大部分的系统架构图都是按分层去画的。分层时最核心的一点就是需要保证各层之间的差异足够清晰,边界足够明显。分层时要保证层与层之间的依赖是稳定的,才能真正支撑快速扩展。
水平分层架构
水平方向上将后端服务分为多个独立的进程:
当然,并不是简单地分层就一定能够实现隔离关注点从而支撑快速扩展,分层时要保证层与层之间的依赖是稳定的,才能真正支撑快速扩展。
网关层
网关层实现的功能包括:
- 请求校验,检查用户是否登录,入参数据的完整性,设置一些通用的检验规则,对不同API设置进行请求的第一步拦截。
- 协议转换,包括通信协议和数据协议的转换,比如将http协议转换成内部的rpc协议, 将Json数据转换成Protobuffer,将内部请求的耗时在网络层次和数据层次进行优化。
- 路由转发,植入网关层服务之后,客户端不知道自己请求的是哪个具体的服务,只需要把请求转发给网关,网关放行之后会把请求路由到指定业务服务上。
- 负载均衡,网关连接的服务实例可能是集群模式存在,所以网关还可以对各个服务实例上执行负载均衡策略,常见的策略就是服务轮询或者按权重路由。
- AB测试,网关从请求中提取设备 ID 、URI 等信息,然后通过URI信息遍历匹配到对应的策略,请求经过分流算法找到当前匹配的AB实验和版本后,将相关数据发送下游。
- 其他的通用基础设施如限流,降级,安全,缓存,日志,监控,重试,熔断等都可以选择放在网关层来做。
在网关实现一些通用的功能,可以有效避免下游的每个服务重复实现。但是增加网关之后也就多了一次转发,会造成性能下降。
同时,在整个网络调用过程中,一定会有一个单点,可能是网关、nginx、dns服务器等。使用网关作为单点,可以对整体服务进行统一控制,但是当网关的稳定性低时会导致系统全面奔溃,所以网关设计要尽量轻。
业务逻辑层
业务逻辑层(Business Logic Layer)是系统架构中体现核心价值的部分。它的关注点主要集中在业务规则的制定、业务流程的实现等与业务需求有关的系统设计,也就是说它是与系统所应对的领域(Domain)逻辑有关,很多时候,也将业务逻辑层称为领域层。
数据访问层
数据访问层实现的功能
- CRUD服务,作为唯一可以与存储介质交互的中间层出现,负责业务对象的增加,修改,删除,加载。同时实现ORM将数据映射成对象。
- 屏蔽底层存储差异,数据持久化的方案常见的有关系型数据库和NoSQL,DAO层屏蔽这些底层差异向上提供同一的接口,屏蔽不同数据库的并发,事务,一致性,分库分表等存储方案。
垂直分层架构SOA
SOA(Service-oriented architecture)从垂直方向基于业务将系统分解成多个独立的服务。服务之间使用企业总线ESB交互。相比于水平分层架构是一种技术层面的隔离,垂直分层的SOA更偏向于是一个业务隔离。如何进行好垂直拆分,需要对业务有深刻的理解与思考。把握好业务的数据边界和拆分粒度至关重要。
ESB服务总线
服务拆分之后就存在内部服务相互调用的问题(后来很多架构的发展都围绕在解决这一问题上),不同子系统之间需要实现相互通信和远程调用,比较直接的方式就是“点对点”的通信方式,但是这样会暴露出一些很明显的问题:系统之间紧密耦合、配置和引用混乱、服务调用关系错综复杂、难以统一管理、异构系统之间存在不兼容等。而基于总线的设计,正是为了解决上述问题。总线则作为中枢系统,提供统一的服务入口,并实现了服务统一管理、服务路由、协议转换、数据格式转换等功能。是的,你也可以把他当成网关,只不过是在特定时期特定场景的另一个名字。
面向服务则是架构演化中另一个重要的思想。
微服务架构
微服务是SOA的进一步发展,相较于SOA,微服务中的服务划分的粒度更小,服务划分更严谨(参考领域模型),同更加重视服务管理,将服务管理的各种基础设施独立出来作为单独的组件。每个服务运行独立的自己的进程中,服务之间互相协调、互相配合,为用户提供最终价值。微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。
微服务特点
- 单一职责,服务粒度小,微服务中每一个服务都对应唯一的业务能力,做到单一职责。每个服务只专注于自己该干的事情,使得开发更简单。微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
- 面向服务:面向服务是说每个服务都要对外暴露统一协议的服务接口。通过协议做到服务与技术隔离,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。
- 自治:自治是说服务间互相独立,互不干扰
- 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
- 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
- 数据分离:每个服务都使用自己的数据源部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护。
- 独立部署:每个微服务都独立部署,单个服务功能上线时不影响其他服务,便于快速迭代。
支撑服务
配置中心
以前,开发人员把配置文件放在开发文件里面,这样会有很多隐患。譬如,配置规范不同,无法追溯配置人员。一旦需要大规模改动配置,改动时间会很长,无法追溯配置历史,从而影响整个产品,后果是我们承担不起的。因此就有配置中心这个喽!现在的开源中心有百度配置中心 Disconf,Spring Cloud Config,Apollo。
监控预警
微服务的监控系统涉及的内容包括系统级别的cpu,memery,network监控,应用层的服务可用性,响应时间,QPS的Metrics监控,程序异常调用栈捕捉,健康检查等。一个基本的监控方案是在每个微服务上启动一个Agent将捕捉到的数据发到kafka,另一边来消费kafka将数据落于ELK,InfluxDB。
服务注册与服务发现
每一个服务对应的机器或者实例在启动运行的时候,都去向名字服务集群注册自己。当服务想要调用其他服务时,只需要知道这个服务的名字,首先向注册集群中心发送请求获取,然后就能收到这个服务相关的信息。服务间的调用不在使用固定的IP。更加便于维护。常见的开源方案有zookeeper,consul,etcd。
持续集成
微服务讲究快速迭代,快速迭代的就要频繁commit,频繁打包,频繁验证,频繁上线,持续集成就是要将这些流程尽可能的自动化,包括安装依赖、运行测试、打包、部署开发服务器、部署生产服务器等流程。你这里push之后,它就自己上线了。常用的CI工具有Jenkins,GITLAB CI。
容器编排
在云时代,云原生的服务都通过docker发布。基于Kubernetes作为容器集群的管理平台被广泛应用。来处理服务的弹性调度。
带来的问题
微服务带来的问题也很明显,更加的碎片化固然会提升分布式系统的复杂性,当服务划分不合理时,一个功能的改动可能会涉及到多个服务,服务见的调用依赖的增多增加了单个请求的耗时,而且当有相互依赖时,测试也面临新的挑战。
服务网格架构
服务网格的架构是CNCF(Cloud Native Computing Foundation,云原生基金会)目前主推的新一代微服务架构。
**服务网格是用于处理服务间通信的专用基础设施层。它负责通过包含现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常通过一组轻量级网络代理来实现,这些代理与应用程序代码一起部署,而不需要感知应用程序本身。
边车模式是一种分布式架构的设计模式。边车就是加装在摩托车旁来达到拓展功能的目的,比如行驶更加稳定,可以拉更多的人和货物,坐在边车上的人可以给驾驶员指路等。边车模式通过给应用服务加装一个“边车”来达到控制和逻辑的分离的目的。
比如日志记录、监控、流量控制、服务注册、服务发现、服务限流、服务熔断等在业务服务中不需要实现的控制面功能,可以交给“边车”,基础设施继续下沉,与业务进一步隔离,原本业务中需要接入的基础设施SDK现在变成了一个单独的进程,来实现可观察性、监控、日志记录、配置、断路器等功能。Sidecar模式中每个SideCar的可以看做是一个微型网关。
Istio项目
目前,最受开发者欢迎的服务网格是Istio项目,该项目最初由谷歌、IBM和Lyft开发。虽然不像Istio那么流行,Buoyant公司的Linkerd 才是“原始的”服务网格,今天仍然被广泛使用着。几个部署了大型微服务的网络级公司已经开发了自己的基于Envoy代理的内部服务网格,巧合的是,Istio也是基于该代理构建的。其他的服务网格也出现了,包括F5的AspenMesh、HashiCorp的Consul Connect、Kong、AWS的AppMesh和微软发起的一项标准化各种服务网格接口的倡议SMI。
ServiceMesh vs API 网关
每个微服务都会提供一个 API,它会作为其他服务与其通信的手段。这引发了 service mesh 与其他更传统的 API 管理形式(如 API 网关)之间的差异问题。API 网关位于一组微服务和“外部”世界之间,它根据需要路由服务请求,以便请求者不需要知道它正在处理基于微服务的应用程序即可完成请求。而 service mesh 调解微服务应用程序内部的请求,各种组件完全了解其环境。
另一方面,service mesh 用于优化集群内东西流量(server-server 流量),API 网关用于进出集群的南北流量(server-client 流量)。但 service mesh 目前依旧处于早期阶段还在不断发展变化中。许多 service mesh(包括 Linkerd 和 Istio)现在已经可以提供南北功能。
最后,软件系统架构没有永远最好的架构,只有合适的架构,没有一成不变的架构,只有不断变化的架构。适合自己的才是最好的,架构要讲求“实用”,而且开发人员普遍可接受,要符合现状的。否则,再“优雅”的设计,都会沦为高成本的“花架子”,生搬硬套和过度设计只会让项目“流产”。
我们要的不是一种固定的架构模式,而是要思考架构设计背后的原则,架构虽然经过了若干演进,但是架构设计背后的哲学却都一样。然后结合现状,去设计最适合的架构。
架构虽然经过了若干演进,但是架构设计背后的哲学却都一样。