起点:Django 只是一个”应用层框架”
Django 本身只负责写业务逻辑(路由、查数据库、渲染页面),它并不知道怎么去”同时接住成百上千个用户的请求”。
真正负责”面对高并发”这件事的,是操作系统的调度机制——进程、线程、协程。所以学 Web 开发,最终绕不开操作系统的这几个基础概念。
一、进程(Process)
计算机的核心是 CPU(负责计算) 和 内存(负责存数据)。
启动一个软件时,操作系统会在内存里给它划出一块独立区域,并分配一个唯一 ID,这就是进程。
特点: 内存空间相互独立、互不干涉。
为什么需要它: 出于安全和稳定性考虑。比如浏览器进程崩溃了,不应该连累微信也崩溃;微信也不应该能直接读到浏览器进程里的私密数据。
代价: 进程之间如果要传数据,必须经过操作系统内核中转,开销比较大。
二、线程(Thread)
进程只是划好了一块内存空间,真正让 CPU 去执行代码的,是线程。
- 一个进程里至少有一个线程(主线程),也可以有多个线程。
- 同一个进程内的多个线程,共享这个进程的全部内存。
为什么需要它: 举例——线程A负责实时接收键盘输入,线程B负责在后台把内容保存到硬盘,两件事可以”同时”进行。
代价:
- 因为线程共享同一块内存,如果线程A和线程B同时修改同一个变量,数据会乱套,所以要加锁。
- CPU 从线程A切到线程B时,操作系统内核要强制保存/恢复寄存器等运行状态,这个切换本身也有开销。
- 线程比进程”轻”,但如果一个程序开出几千个线程,操作系统光是来回切换线程就会把 CPU 累死。
三、协程(Coroutine)
为了解决”线程太多、切换太累”的问题,出现了协程。
核心区别:
| 线程 | 协程 | |
|---|---|---|
| 切换方式 | 抢占式——操作系统随时可能强制打断,换成别的线程 | 协作式——完全由程序员的代码控制切换,操作系统完全不知情 |
| 谁在调度 | 操作系统内核 | 程序自己(用户态) |
协程怎么工作: 比如遇到网络请求或查数据库需要等待 I/O 时,协程会主动说”我要等数据了,CPU你先去跑协程B吧”;等数据返回后,再切回来继续跑。因为是主动让出、主动切回,不需要操作系统内核参与调度,所以开销比线程小得多,可以同时开成千上万个协程也不会把 CPU 累死。
四、Python 代码是怎么”变成”线程的
疑惑点: Django 不是 Python 写的吗?Python 不就是一堆 .py 文件吗?怎么就扯上线程了?
答案: Python 代码本身是静态的,只是硬盘上的文本文件。但代码要执行,必须靠 Python 解释器把它载入内存运行——一旦程序在内存里运行起来,它就变成了操作系统的一个进程;而进程里运行代码的那条”轨迹”,就是线程。
操作系统把 Python 解释器加载进内存、创建一个进程,解释器在这个进程里拉起一个主线程,这个主线程开始按顺序执行 Django 的代码,并在指定端口上死循环监听网络请求。
所以并不是 Python 语言本身自带线程,而是任何 Python 代码在运行的那一刻,都是靠操作系统分配给解释器的线程来一行行执行的。
五、Server 到底是什么
疑惑点: 我理解的 server 是”服务器”,服务器不就是阿里云、腾讯云那种机器吗?
答案: 这里有两个概念容易混:
- 硬件服务器:一台一台放在数据中心里的物理计算机,有 CPU、内存、不间断的运行环境。你租一台阿里云/腾讯云服务器,本质上就是租了一台远程电脑。
- 我们说的 server(软件):运行在这台机器内部的一个软件程序,比如 Nginx、Gunicorn、uWSGI。它的作用是专门守在网络端口上,接收进来的 HTTP 数据包,并分发给你的代码去处理。
也就是说,”server” 更多时候指的不是那台硬件机器本身,而是机器上跑着的负责接电话、收发字节的那个软件角色。
六、最底层:监听是怎么工作的
一个最简单的 Python 监听代码大概长这样:
import socket
# 1. 申请一个网络套接字(就像装个电话机)
server_socket = socket.socket()
# 2. 绑定服务器的 80 端口(给电话分配号码)
server_socket.bind(('0.0.0.0', 80))
# 3. 开启监听(允许电话打进来)
server_socket.listen(5)
# 4. 核心逻辑:无限循环,死等用户连接!
while True:
# 只要没有用户访问,代码就卡在 accept() 这一行死等
# 一旦有浏览器访问(电话响了),立马接通并处理
client_socket, address = server_socket.accept()
# 交给 Django 代码去处理业务...
handle_django_request(client_socket)
关键点在 accept() 这一行:
- 没人访问时:操作系统会让这个进程进入挂起状态,完全不占用 CPU 算力——相当于一个睡着了的门卫。
- 有人访问时:网卡数据包通过网线到达服务器网卡,网卡产生一个硬件中断信号,把 CPU 从睡眠状态唤醒,开始消耗算力去跑 Django 逻辑。
所以监听本身不需要任何额外的第三方服务,占用资源也极小——它只是在”死等”,等来了才干活。
七、Django 自带的 server:为什么存在、为什么不能上线用
Django 项目内置了一个用 Python 写的极简 server。启动方式就是终端里敲:
python manage.py runserver
为什么 Django 要自带一个 server: 是为了方便开发调试。有了它,本地写代码时不需要额外安装 Nginx 之类的软件,直接就能在浏览器里测试网站。
为什么它性能不好,不能用来上线: 因为这个内置 server 设计初衷只是给自己开发调试用的,启动后默认只占用一个线程干活。
如果这时候用户A、用户B同时访问:内置 server 收到 A 的请求,交给 Django 处理(比如查数据库花了 2 秒),这 2 秒里用户B的请求就只能在网络通道里排队,页面一直转圈,直到 A 的请求处理完。如果同时有 100 个人访问,第 100 个人可能要等很久很久。
八、WSGI:Django 代码是怎么”暴露”给外部的
疑惑点: 那要上线部署,是不是还要换一种语言重写?
不是。 关键在于理解一件事:Django 代码和它自带的那个 server,从一开始就是完全独立的两个东西。
Django 项目根目录里一定有一个 wsgi.py 文件,内容大概是:
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
核心就是这一行:它定义了一个叫 application 的变量(本质是一个函数)。你写的路由、视图、数据库逻辑,最终都是通过这个 application 变量暴露给外面的。
Django 的代码只提供了一个入口,谁来调用这个 application 函数,就交给谁处理业务——但 Django 代码本身并不主动去监听网络端口。 监听端口、接收字节、调用 application 这件事,是 server 软件干的活。
九、”换 server” 到底换的是什么
开发时:敲 python manage.py runserver,跑起来的是 Django 内置的 server 模块。这个模块内部会 import 你 wsgi.py 里的 application,用内置的单线程去调用它。
上线部署时:不再敲 runserver,而是在云服务器终端敲一行类似 gunicorn ... 的命令。这个独立的程序(Gunicorn)根本不关心你之前用没用过 runserver——它自己去读取你的 wsgi.py,把 application 导入进来,然后按指定参数在服务器里拉起多个进程(比如 4 个),每个进程都载入一份 Django 代码,请求就由这 4 个进程/多线程并发去调用 application 函数。
所以”换 server” 换的从来不是 Django 代码本身,而是换了”谁来调用 application 这个函数”的外部程序。 从单线程的 runserver,换成多进程/多线程的 Gunicorn,Django 代码一行都不用改。
十、WSGI vs ASGI:为什么还不够,还要再进一步
一个请求进来,Django 要经过路由匹配、查数据库、渲染页面等步骤。这个过程中最容易”卡住”的地方,就是查数据库这类 I/O 操作——发出请求后要等结果返回。
WSGI 方案(多进程/多线程): 为了同时处理 100 个用户请求,就开 100 个线程或进程去处理。
问题: 当并发量到几千几万,或者需要长时间保持连接(比如客服实时聊天、WebSocket 消息推送),服务器要维护几万个线程/进程,CPU 会因为频繁切换而卡死。
ASGI 方案(协程 + 事件循环): 当请求A进来,底层要查数据库时,处理请求A的协程发现要等待,就主动让出 CPU;事件循环立刻切去跑协程B;等请求A的数据库数据返回了,再唤醒协程A继续把结果返回给用户。
同一时间能”挂起等待”的请求数量,协程模型可以远超线程模型能承受的数量,因为协程的调度开销几乎可以忽略不计。
十一、部署实操:命令行里到底在干什么
生产环境服务器绝大多数是 Linux,为了极致的性能、安全性和节省硬盘资源,默认不装图形界面。所有软件安装、配置修改、程序启动,都是通过 SSH 远程连接到服务器,在终端控制台里完成的。
一个很典型的上线命令组合:
git pull # 把最新代码拉到服务器
python manage.py migrate # 数据库迁移
systemctl restart gunicorn # 重启服务,让新代码生效
十二、Shopify(SaaS)vs Django 全栈开发的本质区别
场景: 接过一个电商类的单子,如果用 Shopify,做完直接打成 ZIP 发给客户,客户自己传到平台上就能上线,根本不用操心部署。
本质区别:
- Shopify 属于 SaaS 平台——平台把服务器、部署、运维全都替你做好了,只暴露一个”上传 ZIP 模板”的接口给你。代价是你只能在它规定的框架和逻辑里工作(比如怎么改模板),没办法自由决定底层怎么处理数据、怎么加缓存、怎么操作原生系统。
- Django 是纯正的全栈开发框架——你可以用它搭建任何东西:社交平台、AI系统、金融系统,甚至自己做一个”类 Shopify”的平台。代价是你必须自己掌握服务器:怎么收发请求、用什么数据库、怎么调优并发,都需要自己在 Linux 上配好。
十三、有没有更简单的部署方式
手动敲几十条命令确实麻烦,现代软件工程发展出了几种简化方案:
- 自动部署流水线:比如 GitHub Actions,配置好脚本后,写完代码
git push一下,云端就自动帮你连接服务器、运行部署命令。表面上是”一键部署”,背后其实还是自动化程序在替你敲终端命令。 - 可视化面板:比如宝塔面板,适合不想碰命令行的场景,个人网站/博客很常用。
- PaaS 平台:比如 Render,只需要把代码托管到平台上,点一下按钮,它会自动识别项目、帮你配好 Nginx 和 Gunicorn 之类的组件并启动。






