这是一个很复杂的问题,数据请求的过程是根据技术的复杂性而变化,没有统一的一个答案。 最近在看系统设计类的文章,有很大的启发,想要尽可能的来描述下这个问题。
简单架构
基本的是浏览器输入会输入一个域名,域名无论对前端或者后端来说都是一样的逻辑,会将域名解析成一个 IP 地址,解析的过程是由域名系统 DNS 来完成的,域名系统 DNS 是由付费的三方服务所提供的,并不是我们自己的服务所托管的。一般浏览器输入一个域名,首先获取的是前端的资源文件,前端项目打包之后会部署到服务器或者是 CDN 上,假如用户规模很小,可以将所有的东西都部署到一台服务器上,这个时候一般会将构建产物部署到 Nginx 上。后期有一定的用户规模,可以将其进行优化,部署到 CDN 中。
获取到相关的前端静态文件会进行浏览器的渲染,其中会请求后端的接口,一般前端和后端会使用 Json 数据进行交互。还是先进行 DNS 域名解析。解析之后假设后端是最简单的结构,仅有一个后端服务和数据库。后端服务可以是任何语言进行开发部署,假设使用的是 Java 语言进行开发,其运行在 Web 服务器中,比如 Tomcat 或者 Jetty,发送的请求会到 Web 服务器中,Web 服务器接受到请求会转发给 servlet 进行处理,Web 容器与 servlet 是如何交互的?是通过 servlet 接口,只要我们编写的类符合 servlet 规范,就会被 Web 容器识别并管理。数据库有很多种类型,比如关系型数据库、NoSQL 数据库等等。使用关系型数据库一般使用 MySQL。
服务与数据库隔离
上面是最基本的一种情况。假如说用户到达一定的规模,我们可以将后端服务进行优化,首先要做的就是将后端服务进行拆分,因为一个服务的 CPU 是有限的,能处理的请求数也是有限的。所以可以将二者进行隔离拆分,拆分之后可以进行独立的扩展。服务扩展
扩展分为垂直扩展和水平扩展,垂直扩展是指服务在一台服务器上面,通过增加服务器的 CPU、内存等进行扩展,这种扩展治标不治本,且没有办法进行故障转移,比如当服务器挂掉之后服务就无法提供服务。我们一般进行水平拆分,就是部署多个服务。这个时候我们就要用到负载均衡器,一般使用 Nginx,请求会先发送到 Nginx 负载均衡器,由 Nginx 请求内网的两个或者多个服务。生产情况会真是的流量路由是 用户发送请求 -> Nginx(安全网关和负载均衡网关)-> 内部服务。水平拆分会有以下两个好处:
- 提高服务的可用性,当某个服务发生故障下线时,流量自动路由到另一个服务
- 如果网站流量一直增长,当前服务不足以处理这么大的流量,可以添加更多的服务,负载均衡会自动进行流量转发
在生产环境中,一般是使用厂商所提供的 LB,请求 -> 厂商 LB -> Nginx(安全网关和负载均衡网关)-> 内部服务。为什么使用 LB 而不是仅使用 Nginx?
1、分层架构的优势:LB 与 Nginx 分工不同
LB 的定位:
- 四层(传输层)负载均衡:直接转发 TCP/UDP 流量,性能高(如云厂商的 SLB、AWS NLB)。
- 七层(应用层)负载均衡:解析 HTTP/HTTPS 协议,支持路径路由、SSL 卸载(如 AWS ALB、Nginx 七层 LB)。
- 核心能力:高可用、弹性扩缩容、DDoS 防护、全局流量调度(如跨区域容灾)。
Nginx 的定位:
- 七层反向代理:更细粒度的路由(如按 URL 分发)、缓存、限流、静态资源处理。
- 业务层适配:与具体应用逻辑强相关(如动态请求转发到特定微服务)。
用户 → 四层 LB(处理海量并发) → 七层 Nginx(精细化路由) → 应用服务器
2、LB 的核心优势
- 高可用与容灾能力:云厂商提供的 LB 通常可以跨区部署,自动故障转移,自建 Nginx 集群需要额外配置 Keepalived s实现高可用
- 性能与扩展性:云厂商 LB 基于硬件或专用的网络优化,远高于 Nginx 的软件层性能
- 安全与运维简化:云厂商 LB 继承防护模块,Nginx 需额外配置模块,云厂商 LB 免运维
- 全局流量调度:云厂商支持全局 LB,将用户请求路由到最近的区域,而 Nginx 难以实现此类复杂的调度。
数据库扩展
对于数据库来说,仅仅一台数据库不支持数据的冗余,也没有办法进行故障转移,这个时候可以部署多个服务器用来存储数据,主要是用的数据复制,比如 MySQL 中的一主多从,也可以部署多主多从。Master 负责写请求,Slave 进行读请求。服务之间进行通信,使用 binlog 进行同步数据。数据复制的优势如下:
- 高性能:通过写请求 Master 服务器,读请求到 Slave 服务器,可以增加 Slave 服务器来提高并发量
- 高可用:通过数据的冗余,当一台服务器出现故障或者离线之后不会导致数据的丢失,也不会出现网站的不可用
除了 MySQL 这种主从复制之外,还有很多分布式的数据库,比如 TiDB,将数据以 K/V 的形式将数据进行分片,分片后路由到多台服务器上,同时针对于每一条数据做数据冗余。
缓存
为了增加并发量,可以使用缓存组件,缓存的关键在于将重复请求的数据放到内存中,可以更快的进行响应。缓存分为本地缓存和网络缓存,一般的缓存思路是先请求缓存是否有数据,如果有数据的情况下,之间返回给用户。如果没有数据的话,请求数据库,并将获取到的数据存放到缓存中,并将结果存放到缓存中。使用缓存还需要注意缓存一致性、缓存失效时间、缓存穿透等问题。
CDN
前文有提到 CDN,CDN 是由三方厂商所提供的,CDN 从源服务器进行加载资源,源服务器上一般使用 S3。通过访问 CDN 可以提高响应速度。消息队列
消息队列有很多,比如 ActiveMQ、Kafka、RobbitMQ 等等,消息队列主要是由生产者和消费者所组成的,生产者负责生产消息,将消息发送到消息队列中。消费者进行消费消息。消息队列的作用主要有三种:
- 削峰:当流量过大时,可以先将其添加到 MQ 中,后续慢慢的消费。
- 解耦:当服务 A -> 服务 B 时,当服务 B 出现异常时会影响到服务 A,此时使用消息队列可以将其进行解耦。服务 A -> MQ <- 服务 B。 服务 A 发送请求到消息队列中,服务 B 消费消息队列。当服务 B 出现异常时并不会影响服务 A。
- 异步:使用消息队列可以实现异步功能。