亿级流量系统架构设计与实战(一)

单机房的内部架构

典型的单机房架构

DNS 服务器:负责将用户访问的域名解析为公网 IP 地址(机房的入口IP),让后用户的请求通过 IP 协议进入机房。
LVS :提供对外的公网 IP 地址作为机房的入口;同时作为四层负载均衡器将用户的请求分发到 Nginx 集群
Nginx :七层负载均衡器,根据请求的协议头或URL将请求转发到对应的服务器
业务服务层的 HTTP 服务:真正处理请求,根据不同的URL向 RPC 服务发送业务逻辑处理请求
业务服务层的 RPC 服务: 执行核心核心业务逻辑

如果业务逻辑涉及到读写数据,则会进一步访问数据层;
如果业务逻辑需要与其他RPC服务交互,则调用其他 RPC 服务;

存储层:存储系统的集合,包括分布式缓存、关系型数据库、NoSQL 数据库等。

分布式缓存:提供缓存,应对高并发请求。
关系型数据库:持久化保存数据。
NoSQL 数据库:应用于 关系型数据库 无法发挥作用的场景。

服务发现:保存每个服务的动态地址列表(以便其他服务可以快速查找,类似 DNS)
消息中间件:请求异步执行、服务之间解耦、请求削峰填谷

客户端连接机房的技术1:DNS

IP 地址功能

标识设备:设备必须有位移的 IP ,这样才能在网络中区分。
网络寻址:数据包传输过程中,需要携带源IP、目标IP,通过TCP/IP的路由寻址功能,最终打到目的地。

DNS意义

公司可以使用更简短、易于记忆的域名,注册到 DNS 服务器。
维护域名与多个IP的映射:可以针对同一个域名、不同用户的请求解析到不同的 IP 地址,进行分流。
公司在需要改变机房公网 IP 时,只需在 DNS 中重新配置即可。
基于 DNS 可以实现灵活的负载均衡策略。

DNS 服务器可以对 IP 地址进行监控:DNS 发现某公网 IP 对应的服务器宕机,DNS 可以及时将这个 IP 摘除掉,防止用户无法访问。

域名结构

mail.google.com

根 :.
顶级域名:com
二级域名:google.com
三级域名:mail.google.com

域名服务器

按照域名结构分为 4 类

根域名服务器

全球 13 台根域名服务器

顶级域名服务器

管理在顶级域名下注册过的二级域名解析的工作(根据二级域名找到二级域名服务器的地址)

权威域名服务器

对特定的域名进行解析。管理二级域名、三级域名、四级域名等的服务器。是DNS中最核心的部分。
决定域名最终要解析成哪个 IP 地址。

本地域名服务器

任何一台主机进行网络配置时,都需要配置本地域名服务器。
域名解析的缓存。
由运营商提供,作为主机访问网络时域名解析的代理,将解析结果缓存到本地。

域名解析过程

客户端连接机房的技术2:HTTP DNS

DNS 存在的问题

域名解析要递归查询,会带来更高延迟。
本地域名服务是分地区、分运营商的,不同运营商的 DNS 解析策略不同。

权威域名服务器获取的是本地域名服务器的 IP 地址,不是客户端的 IP 地址,所以定位客户端地址并不准确,域名解析出来的 IP 地址对于客户端可能不是最近的节点。
还存在某些运营商威力节约资源,直接将域名解析的请求转发到其他运营商的本地域名服务器(域名转发)
这些后果是用户请求变慢

DNS 劫持问题。

干预域名服务器把域名解析出错误的 IP 地址,达到用户无法访问目标网站/访问恶意网站的目的。

HTTP DNS原理

HTTP DNS 是基于 HTTP, 具有 DNS 解析能力。

HTTP DNS 服务器不通过域名提供服务 ( 否则又需要域名解析 ) , 只通过一个固定的 IP 地址提供服务 。


客户端指定 HTTP DNS 服务器的 IP 地址 ( 如 5.5.5.5 ) , 使用 HTTP 调用域名解析接口 , 并传入待解析的域名和客户端的 IP 地址 。 HTTP DNS 服务器负责向权威 DNS 服务器发起域名解析请求 , 并将最优 IP 地址返回 。
客户端获取到域名 IP 地址后 , 直接向此 IP 地址发送请求 。

如果客户端需要访问业务请求 http://go.friendy.com/feed ,则在 HTTP header 中指定 Host 字段为此 IP 地址即可。

好处

降低域名延迟解析:直接访问 HTTP DNS 服务器,缩短域名解析链路,不需要递归查询。
防止域名劫持: 将域名解析请求直接通过 IP 地址发送至 HTTP DNS 服务器 , 绕过运营商本地 DNS 服务器 , 避免了域名劫持问题 。
调度精准性更高: HTTP DNS 服务器获取的是真实客户端的 IP 地址 , 而不是本地DNS 服务器的 IP 地址 ,即能够基于精确的客户端位置 、 运营商信息 , 将域名解析到更精准的 、 距离更近的 IP 地址 , 让客户端就近接入后台服务节点 。
快速生效: 当与域名关联的 IP 地址发生变更时 , HTTP DNS 服务不受传统 DNS 技术多级缓存的影响,域名更新能够更快地覆盖到全量客户端 。

HTTP DNS实践

虽然 HTTP DNS 有优势 , 但是我们并不能认为它可以完全取代 DNS 。

鉴于域名解析的重要性 , 客户端网络 SDK 在实现接入机房的逻辑时**应该设计足够强大的容灾策略 **,尽力保证域名解析的可用性 。

客户端使用 HTTP DNS 尝试解析域名
如果 HTTP DNS 服务器返回空数据或者网络错误 , 那么客户端继续向本地 DNS 服务器发起域名解析请求 , 即降级到使用 DNS 技术 。
如果 DNS 解析也遭遇失败 , 那么客户端将直接使用预留的域名兜底 IP 地址作为域名解析结果 。 我们可以通过客户端约定写死或服务端下发配置的方式为业务核心域名设置对应的兜底 IP 地址 。 访问兜底 IP 地址意味着可能增加用户请求延迟 , 但是至少能保证用户不会被域名解析挡在门外 。

另外 , 在 HTTP DNS 的实践中可以引入如下策略 。

安全策略: HTTP DNS 是基于标准的 HTTP 的 , 为了保证数据安全 , 可以更进一步使用 HTTPS。
IP 地址选取策略 : HTTP DNS 服务可以将最优 IP 地址按照顺序多个下发 , 客户端默认选取第一个 IP 地址优先校验连通性 , 如果连通性不佳 , 则再选取下一个 IP 地址 。
批量拉取策略 : 在客户端应用冷启动或网络切换 ( 如将 Wi-Fi 切换到蜂窝网络 )时 , 客户端自动批量拉取域名和 IP 地址列表的映射数据并缓存 , 以便在后续请求中使用 , 预期提升精准调度能力和域名解析性能 。

接入层的技术演进

简单架构出现的问题

业务后台 HTTP 服务器直接绑定公网 IP 地址与客户端建立网络连接 , DNS 服务器对其域名的解析结果就是HTTP 服务器的一个公网 IP 地址 。

这种架构非常轻量 、 清晰 , 但是存在如下问题

可用性低: 如果某个业务服务实例宕机 , 那么 DNS 服务器将无法高效地感知到其 IP 地址已不可用 , 导致被 DNS 解析到此 IP 地址的用户请求均不可用 。
可扩展性差: 当业务服务需要扩容时 , 总需要额外配置 DNS ; 而且受限于 DNS 解析的生效周期 , 扩容后的服务新地址难以实时生效 。
安全风险高: 业务服务的 IP 地址都是公网 IP 地址 , 这相当于后台的所有网络地址都暴露在公网环境中 , 存在网络安全隐患。

如何解决这些问题呢 ?

在软件架构领域有一句很经典的话:计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决 ” 。

我们也可以在客户端和业务服务的连接之间引入一个中间层 :

一方面 , 它需要负责提高业务服务的可用性和可扩展性 , 这要求中间层有丰富的功能
另一方面 , 它还要充当客户端访问业务服务的总代理 , 将用户流量按规则转发到某个业务服务实例上 , 这又要求中间层有非常高的性能。


Nginx

Nginx是 开源、高性能的HTTP 服务器 反向代理服务器

Nginx 可以作为 HTTP 服务器进行网站的发布处理,也可以作为反向代理实现负载均衡功能。

正向代理、反向代理

正向代理:代理客户端

反向代理:代理服务器

Nginx 的反向代理

基于域名HTTP URL 的路由转发功能。

例子:假设 Friendy 应用内置了内容推荐 、 点赞和评论功能 , 这三个功能在服务端对应于三个 HTTP 业务服务: feed 服务 、 like 服务 、 comment 服务 。 每个 HTTP 业务服务都有两个服务器实例 。

Nginx 服务器作为三个后台服务的反向代理 , 提供公网 IP 地址 122.14.229.192,对域名 api.friendy.com 进行 DNS 解析会得到这个se用户请求将通过网络被传递到这台 Nginx 服务器 。

这台 Nginx 服务器的配置文件 nginx.conf 的内容如下 :

worker_processes 3;

events {
  worker_connections 1024;
}

http {
  keepalive_timeout 60;
  
  upstream api_friendy_feed {
    server 10.1.0.1:8080;
    server 10.1.0.2:8080;
  }
  
  upstream api_friendy_like {
    server 10.2.0.1:8081;
    server 10.2.0.1:8081;
  }
  
  upstream api__friendy_comment {
    server 10.3.0.1:8082;
    server 10.3.0.1:8082;
  }
  
  server {
    listen 80;
    
    server_name api.friendy.com;
    
    location /feed {
      proxy_pass http://api_friendy_feed;
    }
    
    location /like {
      proxy_pass http://api_friendy_like;
    }
    
    location /comment {
      proxy_pass http://api_friendy_comment;
    } 
  }
}
配置解释

worker_processes: Nginx 将启动几个工作进程来处理用户请求 ;
events 中的 worker_connections :每个工作进程允许同时最多建立多少个网络连接。
server

listen: HTTP 服务器监听的端口号
server_name:设置虚拟主机服务名称, 用户请求的 HTTP 请求头会根据 Host字段与 server_name 进行匹配,如果匹配成功,则用户请求被对应的 server 配置块处理 。 这里的匹配支持正则表达式。
location:指定一个 HTTP URL 相对路径的处理规则 。上面的 nginx.conf 文件配置了 /feed、/like、/comment 请求的处理规则 。

location 中的 proxy_pass:决定了某个HTTP URL 需要被谁代理 , 代理者可以是一个明确的网络地址 , 也可以是一个服务池名称 。(对于本节的 Nginx 配置而言, /feed、/like、/comment 的 location 配置块分别指定了这三类请求应该被代理到服务池 api_friendy_feed、api_friendy_like 、api friendy comment)。
upstream:表示一个服务池,其中的 server 配置项表示这个服务池中有哪些服务器实例, 以及这些服务器实例的地址。 (上面的 nginx.conf 文件分别为 feed 服务 、 like 服务 、comment 服务配置了对应的服务池。 当用户请求被代理到某个服务池后 , Nginx 会默认以轮询的方式从服务池中选择一个服务器实例作为目标服务器来转发请求)。

Nginx 服务器先以 80 端口启动对外服务,当收到请求头 Host 为 api.friendy.com 的 HTTP 请求后,根据请求的 HTTP URL 相对路径做进一步处理 。

如果相对路径是 /feed, 则在 api_friendy_feed 服务池中选择一个服务器实例作为目标服务器转发请求 。
如果相对路径是 /like , 则在 api_friendy_like 服务池中选择一个服务器实例作为目标服务器转发请求 。
如果相对路径是 /comment, 则在 api_friendy_comment 服务池中选择一个服务器实例作为目标服务器转发请求。

Nginx 负载均衡策略

轮询:请求按时间顺序逐一分配到不同的服务器。

如果某服务器死机,则自动剔除系统故障,不影响用户访问。这是默认策略。

加权轮询:每个服务器都有个权重,权重越大的越容易被访问。例如:

upstream backend (
  server 192.168.1.5:80 weight=10;
  server 192.168.1.6:81 weight=l ;
  server 192.168.1.7:82 weight-3;
}

ip_hash:请求按照 IP 地址的哈希值选择服务器,使得来自同一 IP 地址的请求一直访问同一个服务器。(不能保障负载均衡,会存在某些服务器访问量很大或很小的情况)。

upstream backend {
  ip_hash;
  server 192.168.1.6:81;
  server 192.168.1.7:82;
}

least_conn: 此策略将请求转发给连接数最少的服务器实例

轮询、加权轮询的策略会把请求按照一定的比例分发到各服务器,但是_有些请求响应时间长_,如果把这些响应时间长的请求大比例地发送到某台服务器,那么随着时间的推移,这台服务器的负载会比较大。在这种情况下,适合采用 least_conn 策略,它能实现更好的负载均衡

url_hash :与 ip_hash 类似, 不同之处是对请求的 URL 做哈希运算。

这种策略可以有效提高同一个用户请求的缓存命中率 。(来自第三方模块 nginx-upstream-hash)

fair:此策略根据每个服务器实例的请求响应时间、请求失败数、当前总请求量,综合选择一台最为空闲的服务器 (来自第三方模块 nginx-upstream-failr)

Nginx 的负载均衡功能决定了一个 HTTP 请求最终被路由到哪个服务器实例 , 而 HTTP 位于 OSI 七层模型的第七层 ( 应用层 ) , 所以 Nginx 作为反向代理也常被称为 “ 七层负载均衡器 ” 。

上述配置文件描述的架构:

Nginx 服务器作为 feed 服务 、 like 服务 、 comment 服务的反向代理服务器 , 是后台服务器机房的总入口 , 通过公网 IP 地址直接与客户端进行通信 。
Nginx 根据收到的 HTTP 请求的 URL 相对路径 , 决定将请求转发到哪个服务以及哪个服务器实例 , 最后完成客户端与后台服务器中业务服务的交互

Nginx 服务器需要将每个业务服务的网络地址设置在 nginx.conf 配置文件中,才能实现与业务服务的通信。 但是业务服务会频繁地迭代与扩容 , 这意味着 Nginx upstream 服务池中的服务器实例地址列表会频繁地变更 。 频繁地更新 nginx.conf 配置文件并不现实 , 所以我们需要找到一种 Nginx 实时感知业务服务地址列表变更的方案。(服务发现)

Nginx 2个模块

ngx_lua : 这个模块将 Lua 语言嵌入 Nginx, 从而允许开发人员编与 Lua 脚本并部署到 Nginx 中执行 。
ngx_http_dyups_module : 这个模块使得 Nginx 不用重新启动就能热更新 upstream 配置并生效。

使用 lua 脚本实现服务发现流程

每隔一段时间(如 3s) 就从服务注册中心获取一次 nginx.conf 配置文件中所有 upstream 配置的服务地址列表 。
获取服务地址列表成功,生成最新的 upstream 配置 , 通过 ngx_http_dyups_module模块将其更新到 Nginx 工作进程中。

使用 Nginx 作为反向代理服务器的好处

DNS 服务器指向 Nginx 服务器 , 业务服务器网络地址切换无须配置 DNS 。
在用户请求和业务服务器之间实现了负载均衡,更便于控制业务服务流量调度 。
对外只暴露一个公网 IP 地址,节约了有限的 IP 资源 Ngiiix 服务器与业务服务器之间通过内网通信 。
对业务服务器起到了保护作用,外网看不到业务服务器,只能看到不涉及业务逻辑的 Nginx 反向代理服务器
增强了系统的可扩展性 , 业务服务器扩容能做到准实时生效 。
提高了业务服务器的可用性 。 任何一个业务服务实例挂掉 , Nginx 服务器都可以将用户请求迁移到其他服务实例 。

LVS

Nginx 是高性能服务器,性能远高于处理业务的服务器。但 Nginx 毕竟是应用层的软件,单个 Nginx 承受的请求是有上限的,当请求达到一定规模时,需要 Nginx 集群顶住压力。

那么谁来决定请求要转发到哪台 Nginx 上? 这个中间层需要比 Nginx 性能更高。 —— LVS

LVS ( Linux Virtual Server, Linux 虚拟服务器 ) 是一个虚拟的服务器集群系统 , 从 Linux 2.6 版本开始它已经成为 Linux 内核的一部分 , 即LVS 运行于操作系统层面

LVS 和 Nginx 在转发请求时的区别

运行的层面:Nginx 运行于第七层(应用层);LVS 运行于第四层(网络层)。
转发形式:Nginx 异步转发;LVS 同步转发。
工作方式:

Nginx 在保持客户端连接的同时新建一个与业务服务器的连接 , 等待业务服务器返回响应数据 , 然后再将响应数据返回给客户端 。 Nginx 主要强调的是“代理 ” 。
当 LVS 监听到有客户端请求到来时 , 会直接通过修改数据包的地址信息将流量转发到下游服务器 , 让下游服务器与客户端直接连接 。LVS 主要强调的是“转发 ”。

Nginx 选择异步转发的好处是可以进行失败转移 ( failover ) , 即:”如果与某台业务服务器的连接发生故障 , 那么就可以换另一个连接,提高了服务的稳定性 。

LVS 四种转发模式

LVS相关名词:

DS ( Director Server ): 四层负载均衡器节点 , 也就是运行 LVS 的服务器 。 DS 和LVS 作为角色时是一个意思 。
RS (Real Server): DS 请求转发的目的地 , 即真实的工作服务器 。
VIP ( Virtual Server IP): 客户端请求的目的 IP 地址 , 实际指的是 DS 的公网 IP 地址 。
DIP ( Director Server IP ): 用于 DS 与 RS 通信的 IP 地址 , 实际指的是 DS 的内网IP 地址 。
RIP ( Real Server IP ): 后端服务器的 IP 地址 。
CIP ( Client IP ): 客户端 IP 地址 。

这里讨论的转发模式实际上就是指客户端向 DS 公网 VIP 发起请求,然后** DS 负责将请求转发给 RS **的过程 。

NAT模式

NAT 模式:通过修改请求报文的目标 IP 地址和目标端口号实现 DS 到某个 RS 的请求转发。在此模式下,网络报文的请求与响应都要经过 DS 的处理 , DS 是 RS 的网关。

FULLNAT 模式

FULLNAT 模式是 NAT 模式的优化版 , 它不要求 DS 与 RS 处于同一个局域网内且作为网关 。 DS 在NAT 模式的基础上又做了一次源 IP 地址转化 , 这样一来 , 当 RS 返回响应数据时 , 根据 IP 地址即可将其正常路由到 DS, 而不需要强行指定 DS 为网关 。

FULLNAT模式的主要缺点是请求到达 RS 后会丢失客户端 IP 地址 。

TUN ( IP 隧道 ) 模式

DS 通过 IP 隧道加密技术将请求报文封装到一个新的数据包中 , 并选择一个 RS 的 IP 地址作为新数据包的目的 IP 地址 , 然后将它发送到对应的 RS ; RS 基于 IP 隧道解密技术解析出原数据包的内容 , 查看 RS 本地是否绑定了原数据包的目的 IP 地址 , 如果是 , 则处理请求并将响应结果通过网关返回给客户端。

DR 模式

TUN 模式类似 ( DS 仅转发请求并不转发响应数据 ) , DR 模式通过改写请求报文的MAC 地址将请求转发到 RS, 然后 RS 将响应数据通过网关返回给客户端 。

四种模式比较

如果我们希望 LVS 有更强的网络环境适应性 , 则可以选择 FULLNAT 模式
如果希望 LVS 有更高的性能 , 则可以选择 DR 模式

LVS+Nginx 接入层的架构

LVS 的性能更高,便于 Nginx 构建集群 , LVS 作为 Nginx 集群的四层负载均衡器 , 可以提高Nginx 的可扩展性 ;
Nginx 的功能更为强大 , 用它作为业务 HTTP 服务器的七层负载均衡器, 能将不同的 HTTP URL 调度到不同的业务服务并提高业务服务的高可用性和可扩展性。

将机房配置的 LVS 的公网 IP 地址 ( 即 VIP ) 作为域名 , 客户端请求经过 DNS 解析后进入 LVS, 然后 LVS 使用如 FULLNAT 模式将客户端请求转发到任意一台 Nginx 服务器, Nginx 服务器再根据 HTTP URL 将客户端请求转发到某个 upstream 服务池的任意一个服务器实例上 。

存在的问题

LVS 是单点: 如果 LVS 宕机 , 则整个机房将无法对外提供服务 。 因此 , 我们需要解决 LVS 的高可用问题

解决方案 (主从热备方案解决 单点高可用问题

主从热备方案:为原本单点的节点( 主节点 ) 配置一个从节点 , 在主节点正常对外提供服务期间从节点并不工作 , 而在主节点发生故障后会自动切换到从节点继续对外提供服务。

Keepalived+VIP

业界常见的实现主从热备的技术方案是 Keepalived+VIP, 例如在主节点 A 和从节点 B 均安装了 Keepalived 并启动后,主节点 A 就会通过 ARP 响应包告知局域网 VIP 对应的 MAC 地址为 MAC-A ( 主节点 MAC 地址 ),之后所有收到这个 ARP 响应包的网络设备在访问 VIP 时 , 就会根据 MAC-A 访问到主节点 A 。

当从节点 B 监听到主节点 A 宕机后 , 它就会代替主节点 A 向局域网回复 ARP响应包:“ VIP 对应的 MAC 地址为 MAC-B ”。于是,之后所有收到 ARP 响应包的网络设备再次访问 VIP 时 , 就会根据 MAC-B 转而访问到从节点 B, 从节点 B 自动代替了主节点A。

为单点 LVS 引入 Keepalived+VIP 方案后实现的高可用架构如图

这样的机房接入层架构完美吗 ?

其实并不完美 。LVS 虽然性能极高 , 但也是有上限的

假设我们的互联网产品已经拥有亿级用户 , 那么单台 LVS 就会遇到性能瓶颈 。 想要痛快地解决某个系统的高并发性能问题 , 就要为这个系统增加水平扩展能力。

我们可以使用多台 LVS 对外提供服务 。 如果有 N 台 LVS 对外提供服务 , 那么就要配置 N 个 VIP, 这些 VIP 都绑定了同一个域名 , 客户端依赖 DNS 轮询来决定访问哪台 LVS 。

最终机房接入层架构已经趋于完备 ,此时

通过 DNS 轮询方式扩展 LVS 的性能 ;
通过 Keepalived 保证 LVS ;
通过 LVS 扩展 Nginx 的性能 ;
Nginx 作为业务 HTTP 服务器的七层负载均衡器 , 提高了业务服务的高可用性与可扩展性。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容