核心组件

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:开发阶段

问题:单进程,并发能力差、静态文件服务慢、不适合生产环境

阶段2:小型生产环境

改进:nginx处理静态文件(快100倍)、Flask只处理API、职责分离

阶段3:中型生产环境

改进:Gunicorn多进程、并发能力提升、自动重启失败进程

阶段4:大型生产环境

改进:水平扩展、高可用、CDN加速


项目实战:内网AI智能体的公网部署

项目背景

基于LangGraph的AI智能体系统,核心组件包括:

  • Web前端(HTML/JS)
  • FastAPI服务器:等待LLM响应期间异步可处理其他请求、流式输出
  • LangGraph Agent:工具增强型智能体
  • 内网部署的LLM服务:Qwen、Kimi等
  • 智能体需要查询MySQL数据库
  • 有域名,但无公网ip,也没有https证书
  • 所有服务都部署在内网,需要通过Cloudflare隧道让公网用户访问

开发阶段架构

要点

一、网络入口

只需一条 Cloudflare 隧道指向 nginx:80,作为唯一对外入口。内网所有其他端口对公网完全不可见,无需 HTTPS 证书(Cloudflare 自动处理用户侧的 HTTPS)。

二、nginx 的路由职责

所有外部流量先进 nginx,再由 nginx 分发给内网各服务。新增服务只需修改 nginx 配置,安全控制、限流、日志也都可以在此集中处理。

三、各层的地址写法

不同位置的代码,写法逻辑不同:

  • 浏览器 JS:使用相对路径(/api/...),浏览器自动以当前域名补全为完整 URL,无需关心实际服务地址
  • nginx 配置:负责将浏览器请求的路径路由到实际服务地址:
    1
    2
    3
    location /api/ {
    proxy_pass http://127.0.0.1:8000/api/;
    }
  • 服务器端 Python(LangGraph Agent):运行在服务器上,正常写完整地址即可:

四、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:8000MySQL: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:80
  • https://your-domain.com 等价于 https://your-domain.com:443
  • http://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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 按路径路由:主域名下不同路径转发到不同服务
server {
listen 80;
server_name tianlejin.top www.tianlejin.top;

# 本机 8000 端口的 FastAPI
location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
}

# 本机 3000 端口的另一个服务
location /app/ {
proxy_pass http://127.0.0.1:3000/;
}

# 本机 4000 端口的hexo博客
location / {
proxy_pass http://127.0.0.1:4000/;
}

# 其他内网机器上的服务
location /service/ {
proxy_pass http://192.168.1.50:9000/;
}
}

# 按域名路由:子域名转发到不同服务
server {
listen 80;
server_name img.tianlejin.top;

location / {
proxy_pass http://127.0.0.1:50000/;
}
}

用户访问的永远是 https://your-domain.com/api/...https://your-domain.com/app/...,或 https://img.tianlejin.top/...,完全不需要知道背后的端口和 IP。这也是为什么说 nginx 是”唯一对外入口”——所有服务统一收口,内网结构对外完全透明。

扩展阅读

  • Nginx基础知识及其配置文件