【软件工程】Web后端架构基础知识
核心组件
Web服务器(HTTP Server)
- 作用:接收HTTP请求,返回响应
- 代表:nginx、Apache、Caddy、Lighttpd
- 特点:擅长静态文件服务、反向代理、负载均衡,不处理业务逻辑
Web框架(Application Framework)
- 作用:写核心业务逻辑的工具
- Python系:Flask(轻量级)、Django(重量级)、FastAPI(异步)
- 其他语言:Express.js (Node.js)、Spring Boot (Java)、Rails (Ruby)、Laravel (PHP)、Gin (Go)
- 特点:处理业务逻辑(数据库、API、认证),自带简易HTTP服务器(仅开发用)
WSGI/ASGI服务器(应用服务器)
- 作用:连接Web服务器和Python应用的桥梁
- 代表:Gunicorn(多进程)、uWSGI(功能强大)、Uvicorn(异步)、Waitress(跨平台)
- 为什么需要:Flask自带服务器只适合开发,生产环境需要多进程/多线程,更好的性能和稳定性
类比
- Web服务器(nginx)= 专业服务员(只负责端菜,速度快100倍)
- Web框架(Flask)= 厨师(会做菜,也能端菜,但端菜慢)
- 应用服务器(Gunicorn)= 传菜员(协调厨师和服务员)
关键概念对比
1. 静态文件 vs 动态内容
| 对比维度 | 静态文件 | 动态内容 |
|---|---|---|
| 内容类型 | HTML、CSS、JS、图片 | API响应、个性化内容 |
| 是否需要计算 | 否,内容固定 | 是,需要查数据库/计算 |
| 适合的服务器 | nginx / CDN 直接服务 | Flask、Django、Spring 等应用框架处理 |
| 性能 | 极快(直接读取文件) | 较慢(需要业务逻辑) |
| 可缓存性 | 非常适合缓存(内容不变) | 通常不能缓存,或缓存时间极短 |
| 典型URL示例 | /css/style.css |
/api/patient/123 |
2. 反向代理 vs 正向代理
| 对比维度 | 反向代理 | 正向代理 |
|---|---|---|
| 流程 | 用户 → nginx → 后端服务器 | 用户 → 代理 → 目标网站 |
| 谁不知道真实地址 | 用户不知道真实服务器 | 目标网站不知道真实用户 |
| 代表谁 | 代表服务器接收请求 | 代表用户发送请求 |
| 部署位置 | 部署在服务器侧 | 部署在用户侧 |
| 隐藏的是 | 后端服务器的真实 IP 和结构 | 用户的真实 IP |
| 使用场景 | 负载均衡、隐藏后端 | VPN、翻墙 |
| 典型工具 | nginx、Cloudflare | Shadowsocks、公司代理服务器 |
| 举例 | 访问淘宝,背后有几百台服务器你不知道 | 挂代理访问 Google,Google 不知道你在哪 |
3. 同步 vs 异步
| 对比维度 | 同步(Flask默认) | 异步(FastAPI) |
|---|---|---|
| 处理方式 | 一次处理一个请求,处理完再接下一个 | 遇到等待就先去处理别的请求 |
| 等待IO时 | 阻塞,线程挂起什么都不做 | 不阻塞,切换去处理其他请求 |
| 代码写法 | 普通函数 def |
需要 async def + await |
| 并发能力 | 低,依赖多线程/多进程扩展 | 高,单线程可处理大量并发 |
| 适用场景 | 简单应用、逻辑复杂的CPU密集型任务 | 高并发、大量IO操作(调用API、数据库查询) |
| 典型框架 | Flask、Django | FastAPI、aiohttp |
典型架构演进
阶段1:开发阶段
graph LR
A[浏览器] --> B[Flask内置服务器<br/>端口50001]
B --> C[业务逻辑 + 静态文件]
问题:单进程,并发能力差、静态文件服务慢、不适合生产环境
阶段2:小型生产环境
graph LR
A[浏览器] --> B[nginx<br/>端口80/443]
B --> C[静态文件<br/>直接服务]
B --> D[Flask<br/>端口5000]
D --> E[业务逻辑]
改进:nginx处理静态文件(快100倍)、Flask只处理API、职责分离
阶段3:中型生产环境
graph LR
A[浏览器] --> B[nginx<br/>端口80/443]
B --> C[静态文件]
B --> D[Gunicorn<br/>4个worker]
D --> E[Flask应用]
改进:Gunicorn多进程、并发能力提升、自动重启失败进程
阶段4:大型生产环境
graph TB
A[浏览器] --> B[Cloudflare CDN]
B --> C[nginx<br/>负载均衡]
C --> D[静态文件<br/>CDN缓存]
C --> E[Gunicorn实例]
E --> F[服务器1<br/>4 workers]
E --> G[服务器2<br/>4 workers]
E --> H[服务器3<br/>4 workers]
F --> I[数据库集群]
G --> I
H --> I
改进:水平扩展、高可用、CDN加速
项目实战:内网AI智能体的公网部署
项目背景
基于LangGraph的AI智能体系统,核心组件包括:
- Web前端(HTML/JS)
- FastAPI服务器:等待LLM响应期间异步可处理其他请求、流式输出
- LangGraph Agent:工具增强型智能体
- 内网部署的LLM服务:Qwen、Kimi等
- 智能体需要查询MySQL数据库
- 有域名,但无公网ip,也没有https证书
- 所有服务都部署在内网,需要通过Cloudflare隧道让公网用户访问
开发阶段架构
graph TB
User[公网用户] --> CF[Cloudflare CDN\n解析域名]
subgraph 边缘层
CF
end
CF -->|"Cloudflare隧道"| nginx
subgraph 接入层
nginx["nginx:80\n反向代理 + 静态文件\n外部流量唯一入口"]
end
nginx -->|"/api/...\n转发到127.0.0.1:8000"| app
nginx -->|"/chat_agent.html\n(静态文件)"| static[前端文件\nHTML/JS/CSS]
subgraph 应用层
app["FastAPI:8000\n仅接受本机nginx转发"]
end
app --> agent
subgraph 服务层
agent[LangGraph Agent\n工具调用编排]
agent -->|"http://192.168.x.x:port/v1\n内网IP直连"| llm[内网LLM服务器\nQwen / Kimi]
agent -->|"http://127.0.0.1:port/api/...\nlocalhost HTTP调用"| toolserver[工具服务\n独立HTTP服务]
end
subgraph Agent工具层
toolserver --> db[MySQL]
toolserver --> search[搜索等其他工具]
end
要点
一、网络入口
只需一条 Cloudflare 隧道指向 nginx:80,作为唯一对外入口。内网所有其他端口对公网完全不可见,无需 HTTPS 证书(Cloudflare 自动处理用户侧的 HTTPS)。
二、nginx 的路由职责
所有外部流量先进 nginx,再由 nginx 分发给内网各服务。新增服务只需修改 nginx 配置,安全控制、限流、日志也都可以在此集中处理。
三、各层的地址写法
不同位置的代码,写法逻辑不同:
- 浏览器 JS:使用相对路径(
/api/...),浏览器自动以当前域名补全为完整 URL,无需关心实际服务地址 - nginx 配置:负责将浏览器请求的路径路由到实际服务地址:
1
2
3location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
} - 服务器端 Python(LangGraph Agent):运行在服务器上,正常写完整地址即可:
- Agent工具服务在本机,用 http://127.0.0.1:port/api/…
- LLM服务在其他内网机器,用 http://内网IP:port/…
四、FastAPI 的安全绑定
FastAPI 应监听 127.0.0.1:8000 而非 0.0.0.0:8000,确保只接受来自本机 nginx 的转发,外部无法直接访问:
1 | uvicorn main:app --host 127.0.0.1 --port 8000 |
Q&A
Q:架构图中 nginx:80 是什么意思?
服务名:端口号 是常见的简写,表示某个服务监听的端口。nginx:80 就是 nginx 监听本机的 80 端口。类似的写法还有 FastAPI:8000、MySQL:3306 等。
80 是 HTTP 的默认端口,用户访问 http://your-domain.com 时默认就是访问 80 端口,不需要在 URL 里写出来。
Q:为什么叫”监听”端口?
服务启动后会一直等待incoming的连接请求,就像守在门口一样——这个”守着等待”的行为在网络编程里就叫监听(listen)。
这不是比喻,而是实际的系统调用。nginx服务启动时会执行 listen(端口号) 这个操作,告诉操作系统:”有请求来敲 80 端口这扇门,就交给我(nginx)来处理。”
Q:是不是除了 80 端口,其他端口都得在域名后面写出来?
不完全是,80 和 443 都可以省略,因为它们分别是 HTTP 和 HTTPS 的默认端口:
例如:
http://your-domain.com等价于http://your-domain.com:80https://your-domain.com等价于https://your-domain.com:443http://your-domain.com:8000→ 必须写出 8000,否则浏览器默认去找 80 端口,找不到服务
这也是为什么对外的服务都习惯部署在 80 或 443——用户访问最方便,不需要记端口号。
Q:使用 80、443 这两个常用端口号会不会不安全?
不会,端口号本身不影响安全性,安全性取决于该端口背后运行的服务以及传输是否加密。
80 和 443 的区别不是”哪个更安全”,而是背后跑的协议不同:80 对应 HTTP,明文传输;443 对应 HTTPS,TLS 加密传输。
所以现代网站都迁移到 443(HTTPS),不是因为 443 这个数字更安全,而是因为 HTTPS 协议会加密传输内容,防止中间人窃听。
在本项目的架构中这个问题不需要担心:
- 用户到 Cloudflare 这段是 HTTPS,由 Cloudflare 自动处理
- Cloudflare 隧道到 nginx 这段虽然走 HTTP,但经过加密隧道传输,并非公网明文
Q:nginx 一般监听 80 还是 443?
要看部署方式:
- 有 HTTPS 证书的标准部署:nginx 同时监听 80 和 443。80 端口通常只用来将 HTTP 请求重定向到 443,实际服务跑在 443。
- 使用 Cloudflare 隧道(本项目的情况):nginx 只需监听 80。HTTPS 由 Cloudflare 在边缘层统一处理,隧道内部只走 HTTP 即可,无需配置证书。
Q:运行在任何端口上的服务,是否都可以通过 nginx 配置转换成 https://your-domain.com/* 的形式访问?
是的,这正是 nginx 反向代理的核心价值所在。无论后端服务跑在哪个端口,只要在 nginx 中配置对应的转发规则,用户都可以通过统一的域名访问,例如:
1 | # 按路径路由:主域名下不同路径转发到不同服务 |
用户访问的永远是 https://your-domain.com/api/...、https://your-domain.com/app/...,或 https://img.tianlejin.top/...,完全不需要知道背后的端口和 IP。这也是为什么说 nginx 是”唯一对外入口”——所有服务统一收口,内网结构对外完全透明。
扩展阅读
- Nginx基础知识及其配置文件



