news 2026/10/13 13:27:42

Linux下Django商城部署全链路解析与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux下Django商城部署全链路解析与避坑指南

简介:面向希望在 Linux 环境部署 Django 电商项目的开发者,这份源码包提供了一套可运行的大型前后端商城基础架构,重点覆盖框架核心逻辑与部署流程,未集成支付宝和短信接口,更适合聚焦业务实现、正在做课程设计或自主练习的初中级开发者。压缩包共 204 个文件、约 1.69MB,包含 99 个 Python 源文件、30 个 HTML 模板、18 个 JS 脚本以及 CSS、字体、图片等静态资源,其中 py 负责后端业务、html 用于页面渲染、js/css 完善前端交互,还附带 SQL 数据库脚本与 Nginx 配置,目录结构清晰,适合按模块对照学习。目前已有 230 人学习下载。围绕商品、订单、用户认证等电商核心模块,资源展示了 Linux 下的环境配置、数据库选型、视图与模板配合、用户权限控制以及后续部署优化等关键环节,能帮助读者快速理解 Django 的 MVC 组织方式,并掌握大型商城项目从开发到上线的基本套路。

1. Linux下部署Django商城:先理清链路再动手

开发机上跑得好好的 Django 商城项目,一部署到 Linux 服务器就各种翻车:静态文件 404、上传的图片打不开、接口超时、内存告警。这不是代码问题,是部署链路没理清。linux django 部署不是把代码传上去跑 runserver 就完事,它涉及 Python 环境、数据库、应用服务器、反向代理、守护进程五层配合。这篇笔记适合谁?刚把商城项目写完准备上线的开发者,以及被部署折磨过的前后端工程师。我会把每一层的选型理由、参数设置和踩坑记录拆开讲,照着做能少走两三天弯路。

2. 环境选型与 Python 运行环境:版本锁定是第一道坎

2.1 为什么不能用系统自带 Python:版本与 pip 的隐藏冲突

很多 Linux 发行版自带的 Python 版本偏老,某个发行版自带 2.7,某个自带 3.8,而新一点的商城项目依赖的 Django 版本对 Python 有最低版本要求,跑起来直接报语法错误或依赖编译失败。更麻烦的是,直接用系统 Python 装包会把 site-packages 目录搞脏,后面再装别的项目依赖时互相覆盖,排查起来像在解一团乱麻。

常见做法是装一个干净的 Python 版本,再用 venv 虚拟环境把商城项目的依赖隔离到独立目录。这样系统自带的 Python 不管怎么动,都不影响项目运行。我先装 Python 3.10 和 venv 模块,然后为项目建独立虚拟环境:

# 以某 Debian 系发行版为例,安装 Python 3.10 与 venv 模块 sudo apt update sudo apt install -y python3.10 python3.10-venv python3-pip # 为商城项目创建独立虚拟环境,/opt/shop 是项目根目录 python3.10 -m venv /opt/shop/venv # 激活后确认解释器路径,避免误用系统 python source /opt/shop/venv/bin/activate which python python --version

第一段命令安装的是 Python 解释器和 venv 标准库模块;第二段创建的虚拟环境会把所有第三方包装进 /opt/shop/venv/lib/python3.10/site-packages,不会动系统目录。激活后执行which python能看到路径指向虚拟环境,这点很重要,很多人部署时忘了激活,后面 pip 装的包和跑项目的解释器不是同一个,功能全对但就是报 ModuleNotFoundError,属于最常见的玄学问题之一。

2.2 依赖锁定:pip freeze 的脏数据与 pip-tools 的用法

商城项目依赖的包往往有几十个,直接pip freeze > requirements.txt会把当前环境里所有包全部锁进去,包括你本机装过但与商城无关的工具包。这些多余包在内网服务器上可能根本装不上,或者版本互相冲突,最后线上环境跟开发环境不一致,跑出诡异行为。我一般用 pip-tools 做依赖锁定,从顶层依赖出发编译出完整锁定文件。

先在项目里维护一份 requirements.in,只写商城项目直接依赖的包,版本用兼容区间,不写死。再用 pip-compile 生成完全锁定的 requirements.txt,最后在服务器上按锁定文件安装:

# requirements.in 内容示例 django>=4.2,<5.1 mysqlclient>=2.2,<3 redis>=5,<6 # 在本机编译出锁定文件,锁定所有传递依赖的精确版本 pip-compile requirements.in --output-file requirements.txt # 在服务器虚拟环境里按锁定文件安装 pip install -r requirements.txt

pip-compile会解析每层依赖并写出精确版本号,还会标注每个包被谁依赖,后续升级某个主版本时能一眼看出影响范围。requirements.txt 第一行通常有--index-url注释,内网部署时把它改成内部镜像源即可。注意 requirements.in 里版本区间不要写太宽,Django 主版本升级带来的 ORM 行为变化可能让商城订单查询逻辑出问题,大版本跳跃我一般会单独开分支验证后再合并。

2.3 离线安装与 pip check:没有公网时怎么装依赖

内网服务器装依赖是另一个常见场景。服务器连不上公网,直接 pip install 会卡在连接超时。做法是在有网的机器上用 pip download 把依赖包下载到目录,拷到内网后用 --find-links 离线安装。这里有个坑:下载时要指定平台参数,否则可能下载到源码包或错误平台的二进制包。

# 在有网的机器上下载依赖,指定兼容 manylinux 的 x86_64 平台 pip download -r requirements.txt -d /opt/packages/ \ --platform manylinux2014_x86_64 \ --only-binary=:all: # 拷贝到内网服务器后离线安装,--no-index 禁止访问公网源 pip install --no-index --find-links=/opt/packages/ -r requirements.txt # 最后校验所有包的依赖关系是否完整 pip check

--only-binary=:all:强制只下载预编译的 wheel 包,避免内网机器缺编译工具链导致源码包安装失败;如果你的服务器架构是 ARM,要把平台参数换成对应的 manylinux aarch64。装完务必执行pip check,它会逐一核对已安装包的依赖声明,报出缺失或版本冲突。这个命令经常被忽略,但部署商城这种百级依赖项目时,它能在五分钟内帮你排除掉一半的环境类疑难杂症。

3. 数据库与配置分离:从 SQLite 切到 MySQL 的迁移方案

3.1 数据库选型与字符集:utf8mb4 和排序规则的坑

商城项目的商品、订单、用户表都有大量并发读写,开发环境用的 SQLite 撑不住生产流量,必须切到独立的数据库服务。常见选择是 MySQL 8.0,稳定且运维资料多。建库时字符集必须用 utf8mb4,而不是 utf8,因为 utf8 在 MySQL 里最多存 3 字节,商品标题里一旦出现 emoji 或生僻字就存不进去,报错你根本想不到是字符集问题。

-- 建库:指定字符集与排序规则,utf8mb4_unicode_ci 兼容性最好 CREATE DATABASE shop_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 建应用账号,只给必要权限,生产库千万不要用 root 跑业务 CREATE USER 'shop_user'@'localhost' IDENTIFIED BY '强密码'; GRANT ALL PRIVILEGES ON shop_db.* TO 'shop_user'@'localhost'; FLUSH PRIVILEGES;

注意 MySQL 8.0 默认排序规则是 utf8mb4_0900_ai_ci,如果后续要把库迁移回 5.7 或对接老系统,这个规则不兼容,建议建库时显式指定 utf8mb4_unicode_ci。另外'shop_user'@'localhost'表示只允许从本机连接,如果 Django 和数据库分机器部署,要将 localhost 换成应用服务器的 IP,否则应用连库时报 access denied,会误导你去查密码对错。

3.2 settings.py 生产配置:环境变量统一管理与 DEBUG=False

开发时 settings.py 里写死 SECRET_KEY、数据库密码没问题,但部署到服务器后这些配置绝不能留在代码里。一旦代码仓库泄露,数据库密码和密钥全暴露。我习惯在项目里加一个 .env 文件,由 systemd 或启动脚本注入环境变量,settings.py 只从os.environ里取值,没有硬编码。

# settings.py 生产片段 import os from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # 所有敏感配置走环境变量,缺失直接抛异常,避免带病启动 SECRET_KEY = os.environ["DJANGO_SECRET_KEY"] DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true" ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") DATABASES = { "default": { "ENGINE": "django.db.backends.mysql", "NAME": os.environ.get("DB_NAME", "shop_db"), "USER": os.environ.get("DB_USER", "shop_user"), "PASSWORD": os.environ.get("DB_PASSWORD", ""), "HOST": os.environ.get("DB_HOST", "127.0.0.1"), "PORT": os.environ.get("DB_PORT", "3306"), # 连接层字符集必须带,否则读写中文和 emoji 可能乱码 "OPTIONS": {"charset": "utf8mb4"}, } } STATIC_URL = "/static/" STATIC_ROOT = "/opt/shop/static" MEDIA_URL = "/media/" MEDIA_ROOT = "/opt/shop/media"

DEBUG 在线上必须为 False,否则浏览器访问任何不存在的 URL 都会把完整报错栈打到页面上,等于把服务器目录结构、代码行号、依赖版本全暴露给攻击者。ALLOWED_HOSTS 用逗号分隔从环境变量读入,把商城域名加进去,比如 shop.example.com。STATIC_ROOT 和 MEDIA_ROOT 需要指向服务器上的绝对路径,Nginx 会从这里读文件,后面第 4 章细讲。

3.3 数据迁移:dumpdata/loaddata 的边界与 makemigrations 顺序

从 SQLite 切到 MySQL,除了改数据库连接,还要处理两个层面的迁移:表结构迁移和业务数据迁移。表结构迁移靠 Django 自己的迁移体系,顺序很重要。先确认模型定义能生成合法迁移文件,再看迁移计划,最后执行:

# 只检查模型与迁移文件是否一致,不生成新文件 python manage.py makemigrations --check --dry-run # 预览将要执行的 SQL 语句,确认没有意外的表删除 python manage.py migrate --plan # 正式执行迁移 python manage.py migrate

--check --dry-run在无模型变更时返回退出码 0,有变更返回非 0,适合写进发布脚本做前置检查。--plan会列出所有待执行迁移及每个迁移的依赖顺序,重点看有没有反向迁移或删表操作,商城项目的订单表、支付记录表一旦被误删没有后悔药。业务数据迁移用 Django 自带的 dumpdata/loaddata 就够,但有两个边界要清楚:一是 contenttypes 和 auth.Permission 的表数据容易因跨环境 ID 冲突,导出时排除掉;二是大表数据在几百 MB 以上时,dumpdata 性能很差,这种情况建议直接用数据库工具做物理导出导入。

# 导出业务数据,排除框架内部表,避免环境 ID 冲突 python manage.py dumpdata --natural-foreign --natural-primary \ -e contenttypes -e auth.Permission -o backup.json # 在新库里导入 python manage.py loaddata backup.json

--natural-foreign和--natural-primary会把外键和主键转成自然键而非数据库自增 ID,这样两个环境的记录 ID 不一致时也能正确关联。经验是:订单、支付这类业务表别用这种方式反复导入导出,多次 loaddata 可能产生重复记录,生产环境数据迁移建议在停服窗口做,并先备份目标库。

3.4 静态文件收集与媒体目录权限:collectstatic 的三件事

Django 开发时静态文件由 runserver 自动处理,部署后 DEBUG 关闭,Django 不再托管静态文件,需要先把所有静态文件收集到一个目录,再交给 Nginx 服务。collectstatic 命令就是干这个的,它会按 app 的 static 目录和 STATICFILES_DIRS 配置把文件复制到 STATIC_ROOT。媒体文件是运行时上传的商品图、头像,不归 collectstatic 管,但要提前建目录并给对权限。

# 收集静态文件,--noinput 跳过交互确认,适合发布脚本 python manage.py collectstatic --noinput # 创建媒体目录,并授权给运行用户 mkdir -p /opt/shop/media chown -R www-data:www-data /opt/shop/media chmod 750 /opt/shop/media

这里为什么用 www-data 这个用户?因为 Nginx 通常以 www-data 运行,Django 上传的图片落在 /opt/shop/media 下,Nginx 要能读到这个目录才能把图片返回给浏览器。如果目录属主是 root,上传功能正常但访问图片时永远 403,这就是典型的权限坑。collectstatic 收集到的静态文件在 /opt/shop/static 下,同样要确认 Nginx 进程有读权限,静态文件目录一般不需要写权限,设为 755 即可。

4. 应用服务器与反向代理:Gunicorn + Nginx 的组合

4.1 Gunicorn 还是 uWSGI:worker 类型与并发参数怎么定

Django 自带 runserver 不支持并发生产请求,必须用应用服务器。商城项目常见的选型是 Gunicorn 或 uWSGI,Gunicorn 配置简单、调试直观,纯 Python 应用用它足够;uWSGI 能做到的细粒度调优更多,但配置复杂度也上来了,部署维护成本高。我一般项目先用 Gunicorn,真遇到性能瓶颈再考虑替换,不在一开始把架构搞复杂。

Gunicorn 的 worker 数量和类型直接决定并发能力和内存占用。sync worker 每个 worker 同时只能处理一个请求,简单但占用高;gthread worker 用线程池处理并发,内存占用更低,适合商城这种大量 IO 密集型请求。worker 数量的经验公式是 CPU 核数乘以 2 加 1,但服务器内存有限时不能照搬,需要按内存反推:

参数建议值说明
workers32 核 4G 内存的服务器建议 3 个 worker
worker-classgthread线程模型,比 sync 更省内存
threads4每个 worker 4 个线程,峰值并发约 12
timeout120请求超时秒数,商城支付回调要放宽
max-requests1000处理满 1000 个请求后重启 worker,防内存泄漏
# 启动 Gunicorn,监听本机 8000 端口,等待 Nginx 转发 gunicorn shop.wsgi:application \ --workers 3 \ --worker-class gthread \ --threads 4 \ --bind 127.0.0.1:8000 \ --timeout 120 \ --max-requests 1000 \ --max-requests-jitter 100 \ --access-logfile - \ --error-logfile -

timeout 是一个容易被忽略的参数:商城下单流程里如果包含外部支付回调或短信发送,同步请求可能超过默认 30 秒,Gunicorn 会把还没完成的请求掐断,前端收到 502。max-requests 加 jitter 是为了防止多个 worker 同时达到上限集体重启造成服务抖动,jitter 随机偏移让重启动作错开。bind 绑 127.0.0.1 而不是 0.0.0.0,是为了让 Nginx 和应用走内网回环,不把应用端口直接暴露到公网。

4.2 Nginx 配置:反向代理与静态文件分离

Nginx 在部署链路里承担三层职责:接收公网请求转发给 Gunicorn、直接服务静态文件和媒体文件、挂载 TLS 证书。商城项目的 Nginx 配置里,静态文件走 alias 指到 STATIC_ROOT,媒体文件走 alias 指到 MEDIA_ROOT,其他动态请求才转发给 Gunicorn。如果静态文件也走反向代理,Nginx 每次都要把请求转发给 Django,性能浪费至少一个数量级。

server { listen 80; server_name shop.example.com; # 商城上传商品图可能超过默认 1M,这里放宽到 20M client_max_body_size 20m; # 静态文件由 Nginx 直接返回,7 天浏览器缓存 location /static/ { alias /opt/shop/static/; expires 7d; access_log off; } # 商品图片所在目录,Nginx 直接返回 location /media/ { alias /opt/shop/media/; } # 其余请求转发给 Gunicorn location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 30; proxy_read_timeout 120; } }

alias /opt/shop/static/;结尾的斜杠不能丢,否则 URL 拼接时会多出一层路径。proxy_set_header Host $host;这行决定了 Django 收到的 Host 头是什么,ALLOWED_HOSTS 校验和 CSRF 校验都依赖这个头,漏掉这行你会看到后台登录一直报 CSRF 错误。X-Forwarded-Proto告诉 Django 原始请求是 http 还是 https,配合后面第 6 章的安全配置使用。client_max_body_size 根据商城业务调整,涉及商品多图上传时 2M 默认值太小,但也不要盲目调大到 1G,会占用大量 Nginx 和 Gunicorn 内存。

4.3 systemd 守护:崩溃自动重启与日志轮转

Gunicorn 进程不能靠 nohup 裸跑,服务器重启或 worker 崩溃时没人拉起服务,商城就静默挂掉了。标准做法是写一个 systemd 服务单元,让系统管理生命周期,加上崩溃自动重启策略。服务单元的核心是 ExecStart 和环境变量注入,EnvironmentFile 指向 .env 文件,这样 settings.py 里读 os.environ 的配置都能生效。

# /etc/systemd/system/shop.service [Unit] Description=Django Shop Service After=network.target mysql.service [Service] User=www-data Group=www-data WorkingDirectory=/opt/shop # 从 .env 文件读入环境变量,目录需保证 systemd 可以访问 EnvironmentFile=/opt/shop/.env ExecStart=/opt/shop/venv/bin/gunicorn shop.wsgi:application \ --workers 3 --worker-class gthread --threads 4 \ --bind 127.0.0.1:8000 --timeout 120 \ --max-requests 1000 --max-requests-jitter 100 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target
# 重新加载 systemd 配置,启用并启动服务 sudo systemctl daemon-reload sudo systemctl enable --now shop # 查看运行状态和最近日志 sudo systemctl status shop sudo journalctl -u shop -n 100 --no-pager

Restart=always让服务无论什么原因退出都会自动拉起,RestartSec=5设置重启间隔,避免进程崩溃后疯狂重启占满 CPU。After=mysql.service声明依赖关系,服务器开机时先起数据库再起应用。日志统一由 journald 收集,排查问题时journalctl -u shop -n 100看最近 100 行即可,不用去翻文件。注意 EnvironmentFile 的权限:.env 里是数据库密码和 SECRET_KEY,建议设为 600 属主 root,避免其他系统用户读到。

5. 部署避坑:五类高频故障的现象、原因与解决

5.1 静态文件 404:页面有内容但彻底没样式

现象:商城首页能打开,但 HTML 里引用的 CSS、JS 全部 404,页面像上世纪网页,后台管理界面完全错乱。

原因基本就三个:一是部署后忘了跑collectstatic,STATIC_ROOT 目录是空的;二是 DEBUG=False 后 Django 不再自动服务静态文件,请求被转发到 Gunicorn 时 Django 直接返回 404;三是 Nginx 的 alias 路径跟 STATIC_ROOT 配置不一致,比如配置里写 /opt/shop/static 而实际文件在 /opt/shop/staticfiles。

解决:先确认静态文件确实收集到位,再验证 Nginx 路径。我一般按顺序排查:

python manage.py collectstatic --noinput ls /opt/shop/static/ | head curl -I http://127.0.0.1:8000/static/app/css/main.css sudo nginx -t

ls看文件是否真实存在;curl -I带返回头测试直接访问 Gunicorn;nginx -t检查配置语法。如果三者都正常但浏览器仍然 404,检查 Nginx error.log 里的路径拼写,重点看 alias 末尾斜杠,这是 Nginx 配置里最容易翻车的位置。

5.2 上传图片打不开:商户传图成功但前端访问 403

现象:后台商品管理里上传商品图提示成功,前台商品详情页图片裂开,直接访问图片 URL 返回 403 或 404。

原因:403 通常是媒体目录权限问题,Nginx 进程没有读权限;404 则是 MEDIA_URL 和 Nginx location 没对上。另一个隐藏原因是 SELinux 开启的发行版上,Nginx 被强制访问控制拦截,即使文件权限全对也读不了。

解决:先调权限,再看 SELinux 审计日志。

sudo chown -R www-data:www-data /opt/shop/media sudo chmod 750 /opt/shop/media # 查看 SELinux 是否拦截了 Nginx 读取 sudo ausearch -m avc -ts recent | grep nginx

如果权限和属主都正确,且看到 AVC 拒绝记录,用下面的命令放行,或者把 SELinux 调到宽松模式但要谨慎:

sudo setsebool -P httpd_read_user_content 1

上传成功但读不到,是因为 Django 进程写文件时用的用户有写权限,而 Nginx 读取时用的用户没有对应权限。这就是为什么前面强调要统一属主为 www-data,不止是媒体目录,上传过程中临时文件所在的目录也要检查。

5.3 ALLOWED_HOSTS 或 CSRF 验证失败:线上后台登录直接被拒

现象:商城前台接口偶尔返回 400 Bad Request,后台管理登录提交时提示 CSRF 验证失败,刷新页面再试偶尔能过,稳定复现时让人抓狂。

原因:400 是 Django 的 ALLOWED_HOSTS 校验拒绝了 Host 头,你没把线上域名加进白名单;CSRF 失败是 Nginx 转发时丢了 Host 头,或者站点启用了 HTTPS,而 Django 的 CSRF_TRUSTED_ORIGINS 里还是 http。这两个问题表象不同,根源都在反向代理层的头信息传递。

解决:先确认 Nginx 的 proxy_set_header 完整,再同步管理端配置。

proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme;

然后在 settings.py 里加上 HTTPS 来源:

ALLOWED_HOSTS = ["shop.example.com"] CSRF_TRUSTED_ORIGINS = ["https://shop.example.com"]

如果已经启用 HTTPS,CSRF_TRUSTED_ORIGINS 里的协议必须写成 https,否则 Django 校验 Origin 头时比对失败,现象就是登录表单永远报 CSRF 错误。这个错误信息往往把注意力往 cookie 上引,实际上就是来源协议不匹配,属于部署层最容易被误判的一类问题。

5.4 OOM:服务器内存告急,Gunicorn worker 被内核杀掉

现象:白天流量正常,晚上数据抓取任务一跑,服务器开始卡顿,systemctl status 显示 Gunicorn 服务不断重启,dmesg 里有 Killed process 记录。

原因:Gunicorn 的 worker 数量配多了。每个 worker 的虚拟内存可能占到几百 MB,3 个 worker 加系统其他进程,4G 内存的服务器很容易触发内核 OOM killer。另一个次因是商城后台导出报表或跑对账任务时一次性加载大量对象到内存。

解决:先看当前内存占用和日志,再按内存反推 worker 数。

free -h sudo journalctl -k | grep -i oom ps aux --sort=-%mem | head -n 10

2G 内存的服务器建议 workers 配 2、threads 配 4,同时给系统加 2G swap 作为缓冲。另外在 Gunicorn 启动参数里保留--max-requests 1000,可以定期回收泄漏内存。如果商城有定时任务,考虑把任务拆到单独的 worker 进程,和应用服务隔离,避免集中跑任务时拖垮主服务。

5.5 时区与中文乱码:订单时间差 8 小时,商品名变问号

现象:订单创建时间显示比实际慢 8 小时,数据库中存的中文商品名变成一堆问号,日志里还有 Incorrect string value 报错。

原因:时间问题通常是 settings.py 没设 TIME_ZONE,默认用了 UTC;中文乱码是数据库表字符集不是 utf8mb4,或 Django 连接数据库时没带 charset 参数。前者好理解,后者隐蔽在多处:建库时字符集对,但表是从旧库导入的遗留表,还是 latin1。

解决:统一配置时区,并修复表和连接两层的字符集。

# settings.py LANGUAGE_CODE = "zh-hans" TIME_ZONE = "Asia/Shanghai" USE_TZ = True
-- 把剩余遗留表转成 utf8mb4 ALTER TABLE shop_sku CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

排查字符集时先确认 Django 连接参数里带了 charset,再查表字符集,顺序不能反。如果连接层已经带了 utf8mb4 而表还是 latin1,写入时数据已经按 utf8mb4 编码进入 latin1 列,转表前最好先备份,转换后逐个确认数据无乱码,这个操作在生产环境要放在低峰窗口做,并经过完整回归。

6. 上线前的压测与安全基操:先让自己跑一遍生产流程

部署完成不等于能上线,我见过太多项目在测试环境"通的",一上真实流量就崩。商城这种重读写的业务,上线前至少要做一次基础压测,并过一遍安全自检。压测工具用 ab 就够,不用上复杂工具;安全自检就查三个最容易漏的配置。

先压测一个高频接口,比如商品列表接口或购物车接口。ab 的并发参数要低于 Gunicorn 实际承载能力,不然压测本身就制造了大量 502:

ab -n 1000 -c 50 http://127.0.0.1:8000/api/products/

看三个指标:Failed requests 必须为 0;Requests per second 在 2 核 4G 机器上稳定过百就算合格;Time per request 不要超过 300 毫秒。如果 Failed requests 不为 0,优先查 Gunicorn error log 和 Nginx error log,多半是 timeout 或 worker 数不够。

安全自检方面,第一确认 DEBUG=False 且 SECRET_KEY 换成了新生成的随机值;第二用 curl 看响应头,确认没有暴露 Django 版本信息,并加上基本安全头;第三确认 HTTPS 已经配置。响应头检查命令很简单:

curl -I https://shop.example.com/

检查响应头里是否有 Strict-Transport-Security、X-Content-Type-Options 等字段。没有的话在 Nginx server 块里加:

add_header Strict-Transport-Security "max-age=31536000" always; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY;

HSTS 头只应在 HTTPS 完全配置好后开启,否则会强制浏览器走 https 导致访问失败。这一步做完,商城部署链路基本闭环:代码上线、静态资源分离、数据库可靠、进程托管、压测通过、安全头到位。

从那以后我每次部署完商城项目,都强制自己走一遍压测加响应头检查,逐项核对才敢把流量切过去。这条流程帮我挡掉过好几次线上事故,希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 13:27:37

插入排序算法详解:从扑克牌思路到工程实战优化

有人问我算法入门该从哪里入手&#xff0c;我一般会先推荐排序。理由很简单&#xff1a;排序算法覆盖了循环、数组操作、边界条件、复杂度分析这些最基础的东西&#xff0c;一门语言学到会用循环和数组之后&#xff0c;排序就是最自然的进阶练习。而在所有排序算法里&#xff0…

作者头像 李华
网站建设 2026/10/13 13:27:09

爬虫效率翻倍:从API接口获取结构化JSON数据全攻略

做Web自动化爬虫这几年&#xff0c;我见过太多人拿到目标站点后&#xff0c;第一反应就是抓HTML、写CSS选择器、处理嵌套标签。不能说是错的&#xff0c;但如果动手之前先花半小时问自己一句"这个页面背后的数据是不是通过接口返回的"&#xff0c;后面能省下大量时间…

作者头像 李华
网站建设 2026/10/13 13:26:03

嵌入式启动例程深度解析:从复位向量到main的完整链路

1. 启动例程到底在干什么&#xff1a;从复位向量到main的完整链路很多人做嵌入式开发&#xff0c;习惯性地把注意力全放在main函数里——初始化外设、跑业务逻辑、调各种库。但真正决定系统能不能稳定跑起来的&#xff0c;恰恰是main之前那段“看不见的代码”。这段代码就是启动…

作者头像 李华
网站建设 2026/10/13 13:23:50

基于Python和Flask的个性化餐饮管理系统:从推荐算法到工程实践

把“个性化餐饮管理”这个题目真正落地成一套能跑的系统&#xff0c;你会发现难点其实不在代码量&#xff0c;而在怎么把“个性化”三个字做实。市面上大多数餐饮管理项目只做到了菜品增删改查和订单录入&#xff0c;顶多算个电子菜单&#xff1b;而这篇要拆解的&#xff0c;是…

作者头像 李华
网站建设 2026/10/13 13:23:34

C语言单链表例程深度拆解:从指针操作到内存管理

1. 从一个单链表例程说起&#xff1a;为什么它值得反复拆解但凡学过数据结构的人&#xff0c;几乎都绕不开单链表。它不像数组那样直观&#xff0c;也不像栈和队列那样有明确的操作限制&#xff0c;但它是理解“指针操作”和“动态内存”的第一道门槛。我见过太多人在单链表这里…

作者头像 李华