互联网系统架构演变
1. 程序三高
2. 传统架构
- 2.1 提高服务器性能(单机)
- 2.2 增加服务器数量(DNS 负载均衡)
- 2.3 负载均衡
- Apache + JK
- Nginx
- Keepalived
- LVS
- 2.4 数据库解决方案
3. 云计算架构
4. 微服务架构
1. 程序三高
1)高并发
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一。当多个进程或线程同时(或着说在同一段时间内)访问同一资源时会产生并发问题,因此需要通过专门的设计来保证系统能够同时(并发)正确处理多个请求。
2)高性能
简单地说,高性能(High Performance)就是指程序处理速度快、耗能少。与性能相关的一些指标如下:
- 响应时间:系统对请求做出响应的时间。例如系统处理一个 HTTP 请求需要 200ms,这个 200ms 就是系统的响应时间。
- 吞吐量:单位时间内处理的请求数量。
- TPS:每秒响应事务数。
- 并发用户数:同时承载能正常使用系统功能的用户数量。
高并发和高性能是紧密相关的,提高应用的性能,可以提高系统的并发能力。
应用性能优化时,对于计算密集型和 I/O 密集型还是有很大差别,需要分开来考虑。
水平扩展(Scale Out):只要增加服务器数量,就能线性扩充系统性能。通常增加服务器资源(CPU、内存、服务器数量),大部分时候是可以提高应用的并发能力和性能 (前提是应用能够支持多任务并行计算和多服务器分布式计算才行)。但水平扩展对系统架构设计是有要求的,难点在于:如何在架构各层进行可水平扩展的设计。
3)高可用
高可用性(High Availability)通常用来描述一个系统经过专门的设计,从而减少停工时间,保证服务的持续可用。
如高可用性集群就是保证业务连续性的有效解决方案。
2. 传统架构
2.1 提高服务器性能(单机)
硬件/系统级别的解决方案:
- 增强单机硬件性能(优先):如增加 CPU 核数如 32 核;升级更好的网卡如万兆;升级更好的硬盘如 SSD;扩充硬盘容量如 2T;扩充系统内存如 128G。
- 换掉免费的 Tomcat,使用商用 weblogic(Oracle 公司出品)。
- 聘请系统架构师优化 Linux 内核。
- 甚至花高价直接购买高性能服务器。
应用级别的解决方案:
- 网页 HTML 静态化。
- 图片服务器分离。
- 缓存: 提高响应速度;减少 I/O 次数。
- 使用异步来增加单服务吞吐量。
- 使用无锁数据结构来减少响应时间。
随着业务的不断增加,服务器性能很快又到达瓶颈。不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网系统架构对高性能的解决方案还是水平扩展。
2.2 增加服务器数量(DNS 负载均衡)
DNS(Domain Name System,域名系统),因特网上作为域名和 IP 地址相互映射的一个分布式数据库,能够使用户更方便地访问互联网,而不用去记住能够被机器直接读取的 IP 数串。通过主机域名得到该域名对应的 IP 地址的过程叫做域名解析。DNS 协议运行在 UDP 协议之上,使用端口号 53。
循环复用 DNS 是一个普遍使用的在 Web 服务器上负载均衡的解决方案。
http://www.company.cn : 192.168.1.100
192.168.1.101
192.168.1.102
弊端:循环复用 DNS 将传入的 IP 请求映射到定义的一系列循环形式的服务器。一旦其中的服务器发生故障,循环复用 DNS 会继续把请求发送到这个故障服务器,直到把该服务器从 DNS 中移走为止。在这之前,许多用户必须等到 DNS 连接超时以后才能成功地访问目标网站(正常运行的服务器)。
2.3 负载均衡
由于现有系统的各个核心部分随着业务量、访问量和数据流量的快速增长,其处理能力和计算强度也需要相应地增大,使得单一的服务器设备根本无法承担。在此情况下,如果扔掉现有设备去做大量的硬件升级,这样将造成现有资源的浪费,而且如果再面临下一次业务量的提升时,这又将导致再一次硬件升级的高额成本投入,甚至性能再卓越的设备也不能满足当前业务量增长的需求。
针对此情况而衍生出来的一种廉价、有效、透明的方法以扩展现有网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性的技术就是负载均衡(Load Balance)。
负载均衡的功能总结
- 转发请求
- 故障移除
- 恢复添加
负载均衡种类
- 一种是通过硬件来解决,常见的硬件有 NetScaler、F5、Radware 和 Array 等商用的负载均衡器,但是它们是比较昂贵的。
- 一种是通过软件来解决,常见的软件有 LVS、Nginx、apache 等,它们是基于 Linux 系统并且开源的负载均衡策略。
负载均衡——主流的软件解决方案
- apache + JK
- nginx + keepalived
- lvs + keepalived
Apache + JK
Apache 是世界使用排名第一的 Web 服务器软件。它可以运行在几乎所有广泛使用的计算机平台上,由于其跨平台和安全性被广泛使用,是最流行的 Web 服务器端软件。
JK 则是 apache 提供的一款为解决大量请求而分流处理的开源插件。
Nginx
Nginx 是一款轻量级的反向代理服务器,由俄罗斯的程序设计师 Igor Sysoev(伊戈尔·西索夫)所开发,供俄国大型的入口网站及搜索引擎 Rambler(漫步者)使用。
Nginx 特点是占有内存少,并发能力强,事实上 Nginx 的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用 Nginx 的网站用户有腾讯、新浪、网易等。
优点
- 可运行在 Linux 上,并有 Windows 移植版。
- 在高连接并发的情况下,Nginx 是 Apache 服务器不错的替代品。Nginx 在美国是做虚拟主机生意的老板们经常选择的软件平台之一。能够支持高达 50,000 个并发连接数(顶级服务器的基础上)。
Nginx 配置
修改 nginx.conf 的配置项:
配置反向代理
server { listen 80; server_name nginx-01.itcast.cn; # nginx 服务器的主机名 # 反向代理的配置 location / { root html; proxy_pass http://192.168.0.21:8080; # 代理走向的目标服务器(tomcat) } }
动静分离
动态资源:
location ~ .*\.(jsp|do|action)$ { proxy_pass http://tomcat-01.itcast.cn:8080; }
静态资源:
location ~ .*\.(html|js|css|gif|jpg|jpeg|png)$ {
expires 3d;
}
轮询机制
在 http 配置项中,跟在 upstream 后面的名字可以随意取,但是要和 location 下 proxy_pass http:// 后的组号保持一致。
http { upstream tomcats { server shizhan02:8080 weight=1; # weight 表示轮询权重 server shizhan03:8080 weight=1; server shizhan04:8080 weight=1; } # 动态资源配置 location ~ .*\.(jsp|do|action) { proxy_pass http://tomcats; #tomcats是后端服务器组的逻辑组号 } }
Keepalived
Keepalived 是一个基于 VRRP 协议来实现的 WEB 服务高可用方案,可以利用其来避免单点故障。
Keepalived 主要用作 Web 服务器的健康状态检查,以及负载均衡主服务器和备服务器之间 Failover(失效转移)的实现。Keepalived 通常部署在 2 台服务器上,分为一主(Master)一备(Backup),但是对外表现为一个虚拟 IP。Keepalived 可以对本机上的进程进行检测,一旦 Master 检测出某个进程出现问题,就会将自己切换成 Backup 状态,然后通知另外一个节点切换成 Master 状态。
LVS
LVS 的英文全称是 Linux Virtual Server,即 Linux 虚拟服务器,是一个虚拟的服务器集群系统,本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的开源软件之一。在 Linux 内核 2.6 中,它已经成为内核的一部分,在此之前的内核版本则需要重新编译内核。
优点:
- 抗负载能力强:因为 LVS 工作方式的逻辑非常简单,而且工作在网络 4 层仅做请求分发之用(四层面向的是网络连接而不是数据包),几乎没有流量,保证了均衡器的 I/O 性能不会受到大流量的影响,所以在效率上基本不需要过多考虑。仅出现过一次问题:在并发最高的一小段时间内均衡器出现丢包现象,据分析为网络问题,即网卡或 Linux 2.4 内核的承载能力已到上限,内存和 CPU 方面基本无消耗(对机器性能要求不高,因此可运行在廉价机器上)。
- 配置性低:这通常是一大劣势,但同时也是一大优势,因为没有太多可配置的选项,所以除了增减服务器,并不需要经常去触碰它,大大减少了人为出错的几率。
- 工作稳定:因为其本身抗负载能力很强,所以稳定性高也是顺理成章,另外各种 LVS 自身都有完整的双机热备方案(如 LVS + Keepalived 和 LVS + Heartbeat),所以一点不用担心均衡器本身会出什么问题,节点出现故障的话,LVS 会自动判别,所以系统整体是非常稳定的。
- 基本上能支持所有应用:因为 LVS 工作在网络 4 层,所以它可以对几乎所有应用做负载均衡,包括 HTTP、数据库、聊天室等。
缺点:
- 软件本身不支持正则处理,不能做动静分离,这就凸显了 Nginx/HAProxy + Keepalived 的优势。
- 如果网站应用比较庞大,LVS/DR + Keepalived 就比较复杂了,特别是后面有 Windows 机器的话,实施及配置还有维护过程就比较麻烦,相对而言 Nginx/HAProxy+Keepalived 更简单。
LVS 对比 Nginx:
- 负载度:LVS > Nginx
- 功能多少:Nginx > LVS
- 稳定性:LVS > Nginx
- 服务器性能要求:LVS > Nginx
为什么说 LVS 几乎无流量产生?
LVS 总共有三种代理方式:
- NAT(网络地址映射):请求和响应产生流量
- IP Tunnelling(IP 隧道):请求产生流量
- Direct Routing(直接路由):请求产生流量
NAT(网络地址映射)工作原理图
所有的连接都要经过 LVS,所以这种负载方式是有流量限制的,具体能撑起多大的流量跟机器有关。
IP Tunneling(IP 隧道)
我们可以发现 LVS 只对 Request 连接进行了负载,而 Response 直接通过 Real Server 发送到 Client(LVS 通过修改源/目标 IP 实现)。但是,Request 的时候应该也有流量啊,为什么说它没有流量产生呢?等介绍完第三种负载策略,我们放在后面一起讨论。Direct Routing(直接路由)
我们发现它的工作原理图和 IP Tunneling 很像:Request 通过 LVS 进行转发,然后 Real Server 直接将 Response 发送给 Client。
只是有一点不同,图中说了 LVS 和 Real Server 需要在同一个网段上(Must be in a physical segment)。
总结
- 对于 NAT(网络地址映射):因为 Request 和 Response 都要经过 LVS,所以这种负载策略是产生流量的,具体能够撑起多大的流量更硬件配置有关(如网卡)。
- 对于 IP Tunneling(IP 隧道)和 Direct Routing(直接路由):这两种负载策略几乎是不产生流量的(Client 发送的第一个数据包需要经过 LVS,所以会产生少量的流量),而后 Client 就直接与 Real Server 通信了,不会再经过 LVS,所以后面这两种负载策略能够扛住更大的连接。也就是说,后面这两种负载策略会在 Client 和 Real Server 之间直接建立起连接而不需要经过 LVS,所以除了前面几个数据包会产生流量意外,后面的数据包根本不经过 LVS 了也就没有产生流量了。
负载均衡解决方案示意图
注意上图中的三角链路:由 LVS 转发的用户请求,再经 Nginx 转发 Real Server 处理完请求后,直接通过 Nginx 将响应返回给用户。
CDN:全称是 Content Delivery Network,即内容分发网络,也称为内容传送网络。CDN 是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
CDN 的基本原理是广泛采用各种缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器上,由缓存服务器直接响应用户请求。
CDN 的基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息,将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet 网络拥挤的状况,提高用户访问网站的响应速度。
2.4 数据库解决方案
1)主从复制、读写分离
Mysql
- 缓存:Memcached(纯内存)、Redis(纯内存、纯内存+持久化)
- 消息队列:MQ、Kafka
- 主从复制(针对高可用问题;双主解决备份问题)+ 读写分离(针对高并发问题):此方案的每台数据库仍是全量数据
- 分库分表
- 分布式
Oracle
- 读写分离 + 主从复制
- Oracle Partition(分区)
- 分库分表
- Oracle RAC 集群(终级解决方案):此方案成本非常昂贵,即使是淘宝,京东这样的大公司,也是很难受的
示例图
2)分库分表
当访问用户越来越多,写请求暴涨,对于上面的单 Master 节点肯定扛不住,那么该怎么办呢?多加几个 Master?不行,这样会带来更多的数据不一致的问题,且增加系统的复杂度。那该怎么办?就只能对库表进行拆分了。
常见的拆分类型有垂直拆分和水平拆分。
以拼夕夕电商系统为例,一般有订单表、用户表、支付表、商品表、商家表等,最初这些表都在一个数据库里。后来随着砍一刀带来的海量用户,拼夕夕后台扛不住了!于是紧急从阿狸粑粑那里挖来了几个 P8、P9 大佬对系统进行重构。
- P9 大佬第一步先对数据库进行垂直分库,根据业务关联性强弱,将它们分到不同的数据库,比如订单库,商家库、支付库、用户库。
- 第二步是对一些大表进行垂直分表,将一个表按照字段分成多表,每个表存储其中一部分字段。比如商品详情表可能最初包含了几十个字段,但是往往最多访问的是商品名称、价格、产地、图片、介绍等信息,所以我们将不常访问的字段单独拆成一个表。
由于垂直分库已经按照业务关联切分到了最小粒度,但数据量仍然非常大,于是 P9 大佬开始水平分库,比如可以把订单库分为订单 1 库、订单 2 库、订单 3 库……那么如何决定某个订单放在哪个订单库呢?可以考虑对主键通过哈希算法计算放在哪个库。
分完库,单表数据量任然很大,查询起来非常慢,P9 大佬决定按日或者按月将订单分表,叫做日表、月表。
分库分表同时会带来一些问题,比如平时单库单表使用的主键自增特性将作废,因为某个分区库表生成的主键无法保证全局唯一,这就需要引入全局 UUID 服务了。
经过一番大刀阔斧的重构,拼夕夕恢复了往日的活力,大家又可以愉快的在上面互相砍一刀了。
在代码层面实现分库分表逻辑:
3)分布式
使用分布式/分库分表的中间件:
使用分布式数据库:
3. 云计算架构
传统方式:On-Premise(本地部署)
- 采购硬件、装系统、组网、安装 Java、安装数据库、安装软件、配置软件、使用软件。
IaaS(Infrastructure as a Service):基础设施即服务
- 安装 Java、安装数据库、安装软件、配置软件、使用软件。
PaaS(Platform as a Service):平台即服务
- 安装软件、配置软件、使用软件。
SaaS(Software as a Service):软件即服务
- 配置软件使用或者直接使用软件。
4. 微服务架构
什么是微服务?
传统架构(单体架构)的劣势:
- 生产效率低:开发效率低、维护难、部署不灵活。
- 稳定性不高:一个服务出现故障则会导致整个应用下线。
- 扩展性不够:无法满足高并发情况下的业务需求。
微服务设计演化
1)模块化
将单个大型系统,解耦拆分成各子模块系统。
2)服务化
- 左图:各子模块系统之间的调用仍通过负载均衡。
- 右图:传统的网关可以认为就是负载均衡,而注册中心可以理解为存储了各系统/服务的调用信息。如交易系统从注册中心获取到调用财务系统的方式是 RPC,而无需再走负载均衡。
3)数据拆分/分布式事务化
- 左图:数据库跟随业务系统的模块拆分而拆分。
- 右图:各业务模块中的数据库再实现分库分表分布式。
4)单元化
继续将系统架构进行分层,且下图中每一列实时链路分别属于所在地的数据中心,假如此时用户从 A 地(如北京市)跑到了 B 地(如深圳市)使用服务,那么两地间的用户数据同步及跨地访问服务将产生效率问题。
策略:如下图所示,用户每一次访问服务仅使用所在地的实时链路。这样虽然牺牲了用户在短暂逗留地区的访问效率,但保证了用户在主要居住地的服务/数据访问效率。
如某用户主要居住在北京市,平时在查看个人历史数据时访问效率快,而在其他区域(如在深圳市)查看个人历史数据时则可能需要等待多转几个圈的加载时间。
5)大型微服务系统架构
第二代的微服务架构:服务网格
服务网格是一种将服务治理能力(比如:服务路由、安全、可观测性、限流熔断等)下沉到基础设施的架构。让业务更专注于业务开发,而由微服务架构引入的问题,交给基础设施去解决。
第一代微服务的常见架构(SpringCloud/Dubbo)如下图所示:
在黄色的容器内有服务 A、服务 B。A 和 B 都包含自己的业务逻辑,如果想要 A 调用 B,同时试图对这个服务进行治理,通常会在业务的内部集成一个 SDK,来实现服务发现、负载均衡、服务路由、重试、熔断限流等功能。
但是,这个架构存在三个主要问题:
- 开发成本。因为 A 和 B 的服务已经是微服务了,如果希望把绿色的部分进行升级或者提供新的功能,就需要重复地对每个服务进行迭代和开发,而每个服务可能存在不同语言开发、各自的框架不同等复杂问题。
- 升级成本。因为 SDK 的部分跟业务耦合在一起,在需要新增一些 SDK 能力或升级 SDK 时,就需要重新部署业务的模块。
- 部署成本。由于相关治理的功能需要耦合在业务的配置里面,所以很难做到实时的下发配置,服务间拓扑关系和治理配置无法统一管理。
ServiceMesh(服务网格)解决了当前架构的哪些痛点 ?
- 如下图左侧所示,它通过将 SDK 、开发框架提供的服务治理能力下沉到一个和业务进程独立的轻量级网络代理中,由这个网络代理作为微服务通信的基础设施层,它可以提供业务无关、语言无关、独立演进、透明升级的特性。这个轻量级的网络进程被称作 Sidecar 代理,是服务网格的数据面。
- 同时如右侧所示,通过一个对 Sidecar 进行统一控制和管理的服务控制平面,来提供对微服务治理和运维的统一入口。
这种架构实现了服务治理技术和业务逻辑的解耦,是云原生时代微服务治理技术的发展方向,也得到了越来越多的公司的关注。