从输入 juejin.cn 到看到页面:DNS、Nginx、服务器集群与 CDN 到底在做什么?

前面两篇,我们一直站在数据库的视角设计一个博客系统:

sql 复制代码
user
post
comment
tag
file
...

但数据库设计完,并不代表用户就能使用这个网站。

一个真正上线的 Web 系统,还需要解决另一个问题:

用户在浏览器输入 juejin.cn 以后,他的请求到底是怎么到达我们的后端程序的?

这个问题会牵涉:

复制代码
DNS
IP
TCP
Nginx
反向代理
负载均衡
服务器集群
OSS
静态资源服务器
CDN

这些名词第一次一起出现时非常容易乱。

这一篇就把它们连成一条完整链路。


一、先想一个最简单的 Web 项目

假设我们使用 Nest.js 写了一个博客后端:

复制代码
Nest.js

服务器上运行:

复制代码
用户接口
文章接口
点赞接口
评论接口

数据库则存储:

复制代码
用户
文章
评论
点赞关系

如果项目很小,最简单的情况可能是:

复制代码
用户
↓
一台服务器
↓
Nest.js
↓
MySQL

但像掘金这样的线上服务不可能只靠一台服务器。

为什么?

因为如果同时来了:

yaml 复制代码
10 个用户
1000 个用户
100000 个用户

单台服务器的 CPU、内存、连接数都有极限。

而且:

如果这台服务器挂了,整个网站就没了。

所以大型系统通常会演变成:

复制代码
很多台后端服务器

二、但是用户访问的是域名,不是服务器代码

用户输入的是:

复制代码
juejin.cn

而计算机真正进行网络通信时,需要:

复制代码
IP 地址

所以第一个问题就是:

juejin.cn 对应哪个 IP?

这个过程由:

复制代码
DNS

负责。


三、DNS 到底是什么?

DNS:

sql 复制代码
Domain Name System

可以理解为:

域名和 IP 地址之间的查询系统。

例如:

复制代码
juejin.cn

最终需要解析成类似:

复制代码
xxx.xxx.xxx.xxx

这样的 IP。

所以:

复制代码
域名
→ DNS 查询
→ IP

四、DNS 查询并不是每次都从头开始

浏览器准备访问域名时,会优先检查缓存。

例如可能依次查看:

复制代码
浏览器 DNS 缓存
操作系统 DNS 缓存
本地网络配置
DNS 解析器

如果已经知道答案,就没必要重新查。

如果本地没有,则会向配置的 DNS 解析服务发起查询。

后续 DNS 系统会根据需要继续找到:

复制代码
根 DNS
顶级域 DNS
权威 DNS

等信息,最终得到目标域名对应的地址。

因此把 DNS 简单理解为:

从域名找到对应服务器 IP。

已经足够建立第一层认识。


五、拿到 IP 以后发生什么?

浏览器知道目标 IP 后,就可以尝试与服务器建立网络连接。

如果是常见的基于 TCP 的 HTTP/1.1 或 HTTP/2 场景,会涉及 TCP 建立连接。

经典 TCP 建立连接过程就是:

三次握手。

所以可以先粗略理解:

复制代码
juejin.cn
↓
DNS
↓
IP
↓
建立网络连接
↓
发送 HTTP 请求

如果使用 HTTPS,中间还会涉及:

复制代码
TLS

建立安全连接。


六、这个 IP 一定是 Nest.js 服务器吗?

不一定。

大型网站通常不会直接暴露:

arduino 复制代码
Nest Server A
Nest Server B
Nest Server C

让用户自己选择。

用户更可能首先访问的是:

入口层。

这个入口层可能使用:

复制代码
Nginx

或者云厂商提供的:

复制代码
Load Balancer

七、为什么需要很多后端服务器?

假设现在有:

arduino 复制代码
Server A
Server B
Server C
Server D

每台机器都部署同一套 Nest.js 应用。

也就是说:

vbscript 复制代码
Server A
能处理 GET /posts

Server B
也能处理 GET /posts

Server C
也能处理 GET /posts

现在同时来了很多用户。

就不能全部塞给 Server A。

我们希望:

arduino 复制代码
请求 1 → Server A
请求 2 → Server B
请求 3 → Server C
请求 4 → Server A
...

于是需要一个角色:

帮忙分配请求。

这就是负载均衡。


八、Nginx 在这里做什么?

Nginx 可以放在后端服务器集群之前:

arduino 复制代码
                Nginx

          Server A
          Server B
          Server C
          Server D

用户并不知道后面到底有几台业务服务器。

用户只访问:

复制代码
juejin.cn

Nginx 接到请求以后,再决定:

把这次请求交给哪台后端服务器。

例如:

bash 复制代码
GET /api/posts

可能被转给:

arduino 复制代码
Server B

下一次:

bash 复制代码
POST /api/comment

可能被转给:

arduino 复制代码
Server C

九、为什么叫"反向代理"?

先看用户眼中的世界。

用户以为自己访问的是:

复制代码
juejin.cn

但实际上真正处理业务的可能是:

makefile 复制代码
10.0.0.11:3000
10.0.0.12:3000
10.0.0.13:3000

这些服务器隐藏在 Nginx 后面。

Nginx:

复制代码
替服务器接收请求
再把请求转发给真正服务器
最后把结果返回给用户

所以:

Nginx 在服务器这一侧帮后端服务器做代理。

这就是:

复制代码
反向代理

十、反向代理和负载均衡有什么关系?

两者不是完全相同的概念。

反向代理强调的是:

用户请求先到代理服务器,代理服务器再访问真正的后端。

负载均衡强调的是:

当后面有多台服务器时,把流量合理分配出去。

所以 Nginx 可以:

复制代码
只做代理

也可以:

复制代码
代理 + 负载均衡

例如:

arduino 复制代码
用户
↓
Nginx
├── Server A
├── Server B
└── Server C

这里两件事同时存在。


十一、Nginx 自己会处理 Nest.js 业务吗?

通常不会。

比如:

复制代码
注册用户
创建文章
写评论
检查 JWT
查询数据库

这些逻辑仍然由:

复制代码
Nest.js

完成。

Nginx 主要负责入口层工作,例如:

复制代码
反向代理
负载均衡
TLS 终止
静态资源服务
请求限制
缓存

所以不要把 Nginx 和业务后端混为一谈。


十二、为什么服务器集群都运行同一套程序?

假设项目打包后同时部署到:

arduino 复制代码
Server A
Server B
Server C

它们运行的是同一套 Nest.js 服务。

所以任何一个都可以处理:

bash 复制代码
GET /posts/100

真正的数据通常在共享数据库、缓存或其他基础设施中。

例如:

arduino 复制代码
Server A
Server B
Server C
        ↓
      MySQL

因此请求被分配到哪台业务服务器,并不会意味着每台服务器拥有完全不同的数据。


十三、数据库通常放在哪里?

后端业务和数据库一般部署在服务器环境中。

应用服务器可能有多台:

css 复制代码
Nest A
Nest B
Nest C

而数据库可能是:

复制代码
MySQL 主从
数据库集群
云数据库
分片数据库

具体架构会随着规模不断变化。

但对于刚入门来说,先理解:

复制代码
请求
→ 后端应用
→ 数据库

就足够了。

数据库负责:

复制代码
用户
文章
评论
点赞
收藏
标签

这样的结构化业务数据。


十四、那头像和文章图片呢?

这里又出现一个非常重要的问题。

假设一张头像大小:

复制代码
200 KB

一篇文章有:

复制代码
20 张图片

如果所有图片都:

复制代码
请求业务后端
→ Nest.js
→ 再从磁盘找文件
→ 返回

大量带宽和连接就会被静态资源占掉。

而图片本身根本没有什么复杂业务逻辑。

所以大型网站通常会把:

复制代码
图片
CSS
JavaScript
字体
视频
附件

这类资源从核心业务服务中拆出来。


十五、什么叫静态资源?

静态资源可以简单理解为:

服务器不需要根据业务动态计算内容,直接把文件返回即可。

例如:

css 复制代码
logo.png
avatar.webp
main.js
style.css
font.woff2

它们和:

bash 复制代码
POST /login
POST /comment
GET /api/user

完全不是一种请求。

后者需要执行程序。

前者很多时候只是:

把文件给你。


十六、为什么头像链接常常不是主站域名?

你会发现很多大型网站的图片 URL 并不是:

bash 复制代码
juejin.cn/avatar/xxx

而是来自单独的静态资源域名。

例如:

bash 复制代码
xxx.byteacctimg.com/...

这是非常常见的设计。

因为:

复制代码
juejin.cn

主要承担业务访问。

而:

复制代码
静态资源域名

专门提供:

复制代码
图片
头像
JS
CSS

等资源。


十七、OSS 又是什么?

例如用户上传头像:

复制代码
avatar.png

后端接到上传请求以后,可以把文件保存到:

复制代码
阿里云 OSS
AWS S3
腾讯云 COS

这一类服务统称:

对象存储。

数据库可能只保存:

arduino 复制代码
filename
size
mimetype
userId
objectKey

而真正的:

复制代码
图片二进制

存储在 OSS 中。

于是数据库负责:

文件是谁的?

OSS 负责:

文件本身放在哪里?

职责非常清晰。


十八、CDN 又是什么?

如果所有静态资源都只存在北京的一台服务器上:

复制代码
北京静态服务器

北京用户访问可能很快。

但美国用户也要跨越很远的网络去取:

复制代码
avatar.webp

就可能很慢。

CDN 的全称是:

css 复制代码
Content Delivery Network

中文一般叫:

内容分发网络。

它会在不同地区部署大量边缘节点。

例如:

erlang 复制代码
北京
上海
广州
东京
新加坡
洛杉矶
纽约
...

这样用户请求静态资源时,就可以尽量从离自己较近的节点获取。


十九、CDN 可以怎么理解?

假设原始图片保存在:

复制代码
OSS

用户第一次请求:

bash 复制代码
/avatar/abc.webp

某个 CDN 节点本地没有。

它可能去源站获取:

复制代码
CDN
↓
OSS

然后缓存下来。

下一批附近用户继续请求:

bash 复制代码
/avatar/abc.webp

就可以直接:

复制代码
用户
↓
附近 CDN
↓
返回图片

不必每次都访问原始存储。

于是:

复制代码
延迟降低
源站压力降低
带宽利用更合理

二十、动态请求和静态请求应该分开理解

现在访问一个文章页面。

页面可能需要:

复制代码
文章正文
作者资料
点赞数量
评论数据

这些属于动态业务数据。

可能经过:

复制代码
浏览器
↓
Nginx / 负载均衡
↓
Nest.js
↓
MySQL

但是页面里的:

复制代码
头像
文章图片
JS
CSS

可能直接访问:

复制代码
CDN

所以同一个网页,其实背后可能向很多不同服务器发请求。


二十一、现在重新走一次 juejin.cn

假设用户打开:

arduino 复制代码
https://juejin.cn

可以先用下面这套简化模型理解。

第一步:DNS

浏览器需要知道:

复制代码
juejin.cn

对应哪个网络地址。

于是进行 DNS 查询。


第二步:建立连接

得到入口服务器地址以后,建立网络连接。

HTTPS 还会进行 TLS 安全连接相关过程。


第三步:请求到达入口层

请求可能首先来到:

复制代码
负载均衡器 / Nginx

它本身不负责主要业务逻辑。


第四步:挑选后端服务器

例如当前集群:

css 复制代码
Nest A
Nest B
Nest C

负载均衡器选择:

css 复制代码
Nest B

处理此次请求。


第五步:后端执行代码

Nest.js:

复制代码
解析请求
验证用户
执行业务逻辑
查询数据库

例如:

ini 复制代码
SELECT *
FROM post
WHERE id = 100;

第六步:返回业务数据

Nest.js 将:

复制代码
文章
作者
评论
点赞数量

等信息返回。


第七步:浏览器继续加载静态资源

页面里可能还有:

复制代码
头像
图片
JavaScript
CSS
字体

这些资源可能从:

复制代码
CDN

获取。

最终浏览器把它们组合起来,呈现出完整网页。


二十二、可以把整个系统分成三层

现在回头看,会发现其实没那么乱。

第一层:业务数据

复制代码
MySQL

负责:

复制代码
用户
文章
点赞
评论
标签

第二层:业务程序

复制代码
Nest.js

负责:

复制代码
登录
鉴权
发布文章
发表评论
点赞
查询数据库

第三层:网络和基础设施

复制代码
DNS
Nginx
负载均衡
OSS
CDN

负责:

复制代码
找到服务器
分配流量
存储文件
分发静态资源

这样就不会把所有概念混在一起了。


二十三、Nginx 和 CDN 最容易混淆的地方

可以简单做一个区分。

Nginx

更偏向:

复制代码
请求入口
反向代理
负载均衡
转发业务请求

例如:

arduino 复制代码
/api/posts
→ Nest Server B

CDN

更偏向:

复制代码
静态内容分发
就近访问
缓存

例如:

bash 复制代码
/avatar/a.webp
→ 附近 CDN 节点

所以二者解决的核心问题不同。


二十四、DNS 也不是"分布式数据库"

DNS 本身确实是一个全球分布式、分层的系统,但学习初期最好不要简单把它记成:

DNS 就是一个分布式数据库。

更准确的理解是:

DNS 是负责域名解析的分布式、层次化命名系统。

它解决的问题是:

复制代码
域名
→ 网络地址及其他 DNS 记录

而不是业务数据库中的:

sql 复制代码
user
post
comment

二十五、真实大型系统会比这里复杂很多

真正的大型互联网应用还可能存在:

复制代码
Redis
消息队列
服务注册
网关
微服务
数据库读写分离
分库分表
容器编排
Kubernetes
多地域部署
WAF
多级缓存

但现在完全没必要一次性全部学。

先把最核心的一条链路搞明白:

复制代码
域名
↓
DNS
↓
服务器入口
↓
反向代理 / 负载均衡
↓
后端应用
↓
数据库

静态资源则是:

复制代码
浏览器
↓
CDN
↓
OSS / 静态资源源站

这已经足够建立一个完整的 Web 系统认知。


二十六、数据库知识终于和部署知识连起来了

前面我们设计:

arduino 复制代码
avatar

id
filename
mimetype
size
userId

为什么数据库里只保存这些信息?

现在就能解释了。

因为:

复制代码
数据库
负责文件元数据

OSS
负责真正保存图片

CDN
负责高效分发图片

比如:

复制代码
avatar 表

可能记录:

ini 复制代码
userId = 7
filename = abc.webp

真正访问头像时:

vbnet 复制代码
https://static.example.com/avatar/abc.webp

最终可能由 CDN 返回。

于是数据库设计和服务器架构终于串起来了。


总结

当用户在浏览器输入一个网站地址时,背后远不只是:

浏览器找到一个 HTML。

实际系统可能涉及:

复制代码
DNS
找到服务入口

Nginx / Load Balancer
接收并分配请求

Nest.js
执行后端业务代码

MySQL
保存业务数据

OSS
保存图片等文件

CDN
将静态资源就近分发给用户

最重要的是,不要把这些东西混成一个概念。

可以分别记住:

复制代码
DNS
我该去哪里?

Nginx / Load Balancer
这次请求该交给谁?

Nest.js
具体业务怎么办?

MySQL
业务数据存哪里?

OSS
真正的文件放哪里?

CDN
怎样让各地用户更快拿到静态文件?

理解了这六个问题,一个完整 Web 应用从数据库设计到线上访问的基本轮廓,也就真正建立起来了。

相关推荐
小成很成1 小时前
mysql 8.0.21 升级 mysql 8.0.45(经过生产环境验证)
android·mysql·adb
东风破_1 小时前
从 0 设计一个博客数据库:用户、头像与文章表应该怎么建?
后端·mysql
大鸡腿同学2 小时前
黄仁勋点透的 AI 真相:能实现你知道但做不到的,却实现不了你不知道的
后端
FfHUCisI2 小时前
Go 编译过程全景
开发语言·后端·golang
FfHUCisI2 小时前
Golang 语法分析与 AST:Parser 与 go/ast
开发语言·后端·golang
大模型码小白3 小时前
Spring AI Tool 实现自然语言操作 MySQL 数据库详解
服务器·开发语言·数据库·人工智能·python·mysql·spring
++==4 小时前
RESTful详解:核心思想、框架、与HTTP的区别,API的设计风格
后端·http·restful
云计算DevOps-韩老师4 小时前
【MySQL运维DBA】【SQL基础系列002篇】
运维·mysql·dba
放风铃的兔子5 小时前
15 年 DBA 经验:MySQL 迁移到 KES,哪些东西真的不用改?
mysql