网站架构是如何一步步演变为今天的分布式架构的,以及为什么需要分布式架构
1. 架构设计演变过程
核心:业务驱动着技术的发展
架构设计的目标:高并发、高可用、可伸缩、可扩展、够安全
几个影响因素:用户量、业务量、数据量、请求量
1.1. 应用与数据一体模式
一整个业务应用和数据部分只部署在同一个服务器上
1.2. 应用与数据分离模式
实现了硬件层面的分离:业务应用和数据库分别部署在两台不同的服务器上
应用服务器处理大量请求,需要CPU和内存
数据服务器处理存储和IO操作,需要磁盘性能
1.3. 缓存与性能提升
用户对某些数据访问量特别大时,数据库成为了性能瓶颈,于是使用了缓存技术
客户端浏览器缓存
客户端将每次http请求的内容缓存下来,以减小应用服务器压力
应用服务器缓存
是进程内缓存,又称托管堆缓存。如 Java中这种缓存存在JVM的托管堆上
缓存服务器缓存
专门的缓存服务器,对内存有要求
所以用户请求访问数据的顺序变为:客户端浏览器缓存–> 应用服务器缓存–> 缓存服务器缓存–> 数据库
缓存技术的加入提高了响应请求的速度,在可用性方面也有改善,即当数据库暂时出现问题时,缓存服务器中保存的热点数据依然可以满足用户的暂时访问
总结:以上发展,都是解决访问数据量大的问题
1.4. 服务器集群处理并发
将同一个应用复制到多个服务器上,解决多个用户同时请求的问题
增加应用服务器的个数,形成服务器集群,在用户请求与应用服务器之间加入负载均衡器以便将用户请求路由到对应的服务器
采用负载均衡器后,重点关注:负载均衡器的均衡算法,要保证用户请求均匀的分布在不同服务器上、属于同一会话的所有请求都在同一个服务器上处理、能针对不同应用服务器的性能优劣进行动态调整流量
负载均衡器位于互联网与应用服务器之间,负责用户流量的接入,因此可以对用户的流量进行监控,同时对提出访问请求的用户进行身份和权限验证
1.5. 数据库读写分离
从读写层面对数据库资源进行分配
缓存的大小有限,非热点数据的读取仍然要在数据库中读取。数据库对于读、写操作的性能是不一样的。写数据时,会造成锁行、锁表,并发写操作会出现排队现象。而读操作可以通过索引、数据库缓存等加快读取速度。所以出现了数据库读写分离
负责写的数据库需要向负责读的数据库进行数据同步。涉及到的问题:数据同步技术(同步复制技术、异步复制技术、半同步复制技术)、可靠性问题(如果主库挂了,从库如何接替工作,之后主库恢复了,是继续当主库还是当从库,以及主从库如何同步数据)
1.6. 反向代理和CDN
1.6.1. 反向代理
相当于外网与内网间的缓冲
反向代理服务器接收用户请求,然后再将请求转发到内网的应用服务器,全程只对请求进行转发,自身不运行任何应用
提高了应用服务器的安全性、还可以在互联网与内网间起适配和网速转换的作用
1.6.2. CDN
Content Delivery Network,内容分发网络
用户的请求需要跳转多个互联网中的节点,才能到达应用服务器获取资源,在离用户客户端最近的节点中存放信息,可以减少跳转次数,提高访问速度
CDN技术减轻了应用服务器的压力、提高了访问速度
但是CDN只对静态资源有效,且需要定期更新CDN服务器上的资源
1.7. 分布式数据库与分表分库
从业务和数据层面对数据库进行分配
1.7.1. 分布式数据库
将数据库资源分别放在不同的数据库服务器中
由于数据存储在不同的表、库、服务器上,所以需要引入数据库中间件来实现数据同步,从而消除不同存储载体间的差异
从软件工程的角度看,数据库中间件的最佳实践:MyCat、Sharding JDBC。此外从数据治理的角度看,需要考虑数据扩容和数据治理的问题
1.7.2. 分表分库
分表
一个1000万数据的表拆为两个500万的表或者按业务逻辑进行列的拆分,然后通过外键关联到主表,注意被拆分出去的列一定是不常访问的列
分库
每个数据库能承受的最大连接数和连接池是有限的。可以让不同业务访问不同数据库
1.8. 业务拆分
将一个应用拆分成多个部署在不同的服务器上,拆分后的各个应用间存在互相调用、通信、协调问题,因此引入队列、服务注册发现、消息中心等中间件
业务拆分后会形成一个个应用服务,既有基于业务的服务,如 商品服务、订单服务。也有基础服务,如 消息推送、权限验证
1.9. 分布式与微服务
1.9.1. 微服务
更小的业务模块、模块间高内聚低耦合、每个模块可以用自己特有的技术实现、模块通过容器部署运行、各模块通过接口和协议实现调用、可以对热点模块进行水平扩展以提高性能
1.9.2. 微服务与分布式架构的区别
拆分目的不同
分布式是为了解决单体资源有限的问题,将一个应用拆分部署到不同服务器上,以分担高并发的压力
微服务是对服务组件精细化,目的是更好的解耦,让各个服务间实现高性能、高可用、可伸缩、可扩展
拆分方式不同
分布式架构按照业务和技术分类进行拆分,目的是让拆分后的服务负载原来单一服务的业务
微服务是在分布式的基础上进行更加细致的拆分,将服务拆分为更小的模块,且每个模块可以独立运行
部署方式不同
分布式架构通常会把拆分后的各部分部署到不同服务器上
微服务既可以将不同服务模块部署在不同服务器上,也可以在同一台服务器上部署多个微服务或者同一个微服务的多个水平扩展
都是基于分布式架构的思想构建的,微服务是分布式架构的进化版,是分布式架构的子集
2. 分布式架构
2.1. 分布式架构的组成
2.1.1. 客户端
客户端与CDN,涉及客户端对http请求资源的缓存(常见的缓存方式:强制缓存、对比缓存)、CDN的缓存
2.1.2. 负载均衡器(接入层)
客户端请求服务器的过程:
- 客户端向DNS服务器发出URL请求
- DNS服务器向客户端返回应用服务器入口的IP地址(并非应用服务器本身的IP地址,而是反向代理服务器的IP地址)
- 客户端向服务器发送请求
- 负载均衡器接收请求后,根据负载均衡算法找到对应的服务器,并将请求转发给服务器
2.1.3. 应用服务器(应用层)
该层包含具体的业务应用服务
API网关
API网关和负载均衡器在原理上是相同的,区别是API网关更多是在服务器内部服务之间实现,而负载均衡更多是在互联网与服务器之间实现
-
路由请求:API网关可以根据请求的URL、HTTP方法、请求头等信息,将请求路由到相应的后端服务上。
-
协议转换:API网关可以将客户端发送的请求转换成后端服务所支持的协议,从而使得不同协议的服务可以无缝地协同工作。
-
负载均衡:API网关可以将请求分发到多个后端服务上,从而实现负载均衡,提高系统的可用性和性能。
-
安全控制:API网关可以对请求进行验证、鉴权、加密等处理,从而保障系统的安全性。
-
监控和日志:API网关可以对请求进行监控、统计和日志记录,从而实现对系统的监控和管理。
-
缓存:API网关可以对请求结果进行缓存,从而减少后端服务的负载,提高系统的性能。
服务协同与通信
各个服务如 订单服务和支付服务,可能在不同的进程、容器、服务器中,它们之间如何发现对方并通信
之前的调用模式:一个模块调用另一个模块,需要在代码中耦合,在代码中描述调用条件,并调用对应的方法或模块。属于进程内调用
分布式的调用模式:引入注册中心,被调用服务先在注册中心注册自己,调用模块在注册中心获取可用服务列表,找到要调用的模块,进行RPC调用
分布式互斥
多个节点同时对共享资源进行访问时,需要保证资源的互斥访问,避免数据的冲突和重复操作
通过引入Zookeeper的DataNode保证两个进程的访问顺序
分布式事务
一个事务要完成的操作,跨越了多个不同的应用(服务器)
引入事务协调器
2.1.4. 数据服务器(存储层)
分布式存储会将数据存放在不同的数据表、数据库、服务器上面,单体应用是直接访问数据库获取数据,但是分布式数据库获取数据的方式就要复杂一些
分布式存储
如 1000万条商品信息,可能分为两张500万条数据表存储。这两个表可能不在同一个数据库、不在同一个服务器。所以需要在代码中建立对两个数据库的连接,分别做两次查询,这样效率低下
引入数据库中间件MyCat 解决由数据分片带来的数据路由、SQL解析等问题
读写分离与主从同步
2.2. 分布式架构的特征
分布性
拆分部署,将服务拆分,并部署在不同的硬件资源上,这些硬件资源可能分布在不同的网络中
如 电商系统针对客户浏览商品信息并下单这个过程拆分为:商品服务、订单服务、库存服务、支付服务
自治性
每个应用服务都有管理和支配自身任务和资源的能力。对内它可以采用自己的技术实现,并不受其他业务的影响
并行性
当某个业务是热门业务时,可将该应用进行水平拓展,该应用与拓展出的应用完成的功能相同,它们并行处理大量请求
全局性
分散的资源要共同完成一件事,需要沟通和协作
2.3. 分布式架构的问题
2.3.1. 应用服务拆分
先划分业务,再针对划分后的业务进行技术实现
利用领域驱动设计(Domain-Driven Design,DDD)的方法定义领域模型(Domain Model),确定业务和应用服务的边界
- 领域驱动设计的模型结构
- 分析业务需求形成应用服务
- 领域驱动设计分层架构
2.3.2. 分布式调用
分布式调用可总结为两部分:
第一部分:感知对方,包括负载均衡、API网关、服务注册与发现、消息队列
第二部分:信息传递,包括RPC、RMI、NIO通信
- 负载均衡分类
- API网关
- 服务注册与发现
- 服务间的远程调用
2.3.3. 分布式协同
- 分布式系统的特性与互斥问题
- 分布式锁
- 分布式事务
- 分布式选举
- 分布式系统的实践
2.3.4. 分布式计算
分布式架构通常采用水平拓展的方式应对海量数据的计算,不同计算场景下的计算方式可分为两种:针对批量静态数据计算的MapReduce 模式、针对动态数据流进行计算的Stream 模式
- MapReduce 模式
- Stream 模式
2.3.5. 分布式存储
从数据类型划分:结构化数据、非结构化数据
一个好的数据存储方案,需要关注数据均匀性、数据稳定性、节点异构性、故障隔离
- 数据存储面临的问题和解决方案
- 分布式存储概念
- 分布式关系数据库
- 分布式缓存
2.3.6. 分布式资源管理与调度
将任务(用户请求)与资源进行匹配
- 分布式调度的由来与过程
- 资源的划分和调度策略
- 分布式调度架构
- 资源调度的实践:Kubernetes 的最佳实践
2.3.7. 高性能与可用性
- 缓存的应用:HTTP缓存、CDN缓存、负载均衡缓存、进程内缓存、分布式缓存
- 可用性的策略:请求限流、服务降级、服务熔断
2.3.8. 指标与监控
- 性能指标:延迟、流量、错误、饱和度
- 分布式监控系统
- 流行监控系统的最佳实践:Zabbix、Prometheus
3. 架构设计思路和要点
3.1. 架构设计思维方式
3.1.1. 过程设计模式
3.1.2. 协作式设计模式
3.1.3. 扩展立方设计模式
3.2. 重构
3.2.1. 何时重构
频繁使用的组件
添加功能时
修复bug时
审核代码时
3.2.2. 重构难题
数据库重构(在数据库和业务层之间加入一个中间层Mapper,保证业务层的数据结构发生变化时,无需修改数据库的结构)
接口与实现
重构不如重写
3.3. 测试
性能测试
为了得到系统的基准线
压力测试
为了得到系统的高压线
4. 职业发展
4.1. 技术路线
初级程序员–>中级–>高级–>技术经理
4.2. 管理路线
程序员–>中级工程师–>系统架构师–>项目经理
4.3. 产品路线
程序员–>产品助理–>产品设计师–>产品经理
参考 《分布式架构原理与实践》崔皓