采用25M CN2+100M国际带宽方案,香港高配服务器如何部署外贸电商缓存?
目标状态是:海外访客优先从CDN获取商品图片、脚本和样式,匿名商品页由香港服务器的短时页面缓存承接,商品对象与非实时查询结果由Redis复用;登录、购物车、结账、支付及实时库存判断始终进入应用,不从共享缓存返回。这样既能降低应用和数据库负载,也能减少图片等大文件对25M CN2与100M国际带宽的占用。

这套方案的关键不是把所有请求都缓存,而是按业务风险分层:边缘缓存负责减少源站出网,页面缓存负责减少重复渲染,对象与查询缓存负责减少重复计算和数据库读取。 前置条件是确认两类带宽的实际调度方式、站点的个性化边界,以及商品更新后能够触发哪些失效动作。下面按一台香港服务器作为电商源站、Nginx转发到本机应用、Redis仅承担可重建缓存的环境实施。
A5数据提供香港物理服务器租用,覆盖入门建站、Xeon Gold及AMD EPYC等配置,并配备SSD或NVMe存储、不同内存与CN2及国际带宽方案,可为外贸电商的Nginx、应用服务、数据库及Redis缓存提供相应的计算、存储和网络资源基础。针对商品图片、公开页面与业务数据的分层处理,也能结合香港节点及多样化硬件配置,承载网站前台、后台接口和多任务运行需求。
一、准备条件:先核对线路、资源与业务边界
1. 确认25M CN2与100M国际带宽分别承载什么流量
部署前向服务商核对:
- 两类带宽是独立出口、按目的网络分流,还是同一端口上的不同限速规则。
- 标注值对应出站、入站还是双向限制,是否存在共享、突发或公平使用约束。
- 国内访问、海外访问及CDN回源实际使用哪条线路。
- 是否有可分别查看线路用量的监控口径。
25M CN2与100M国际带宽不能直接理解为任意连接都能使用125Mbps。 单次传输受路由、出口限速、链路质量及接收端条件共同约束。服务器CPU和内存再充足,也不能替代出网带宽。
以下使用十进制单位,仅作容量估算:

| 出口 | 标称速率 | 理论字节速率 | 按70%规划的持续业务速率 |
|---|---|---|---|
| CN2出口 | 25Mbps | 3.125MB/s | 约2.19MB/s |
| 国际出口 | 100Mbps | 12.5MB/s | 8.75MB/s |
换算方法是Mbps除以8得到MB/s。70%是本方案的规划参考值,用于给突发流量、协议开销和后台任务留余量,不是线路性能承诺。
例如,某出口每秒收到40次完整页面访问,每次需传输0.6MB资源,总需求为:
40 × 0.6MB = 24MB/s;24 × 8 = 192Mbps
如果CDN按传输字节计算的命中率达到90%,这一部分回源数据约为19.2Mbps。订单API、后台上传、第三方同步等未计入该值,仍需单独预留容量。不要用请求命中率代替字节命中率:命中大量小图标,不一定能缓解大图回源压力。
2. 给缓存资源设定上限
示例资源预算可采用:页面缓存目录上限20GB、Nginx缓存索引128MB,Redis预算根据商品对象数量单独确定。它们只是起始值,应结合空闲内存、磁盘容量和工作集调整。
实施前确认:
- 页面缓存使用本地SSD目录,不与数据库数据目录混放。
- Redis只保存可重建缓存;会话、任务队列或业务状态若使用Redis,应使用独立实例和资源预算。
- 应用、数据库、Redis及操作系统都有内存余量,不能把“空闲内存”全部分配给缓存。
- Nginx、应用和Redis的日志可关联到同一次请求,Redis不暴露到公网。
- 商品、分类、价格和促销变更有可记录、可重试的更新事件。
在已有Nginx环境中先核验版本与配置结构:
nginx -v
sudo nginx -T
后续示例使用常见开源Nginx支持的配置项,不依赖额外的缓存清除模块。修改前备份整个配置目录;备份可能包含敏感配置,应只允许管理员读取:
backup_dir="/root/nginx-backup-$(date +%Y%m%d-%H%M%S)"
sudo install -d -m 700 "$backup_dir"
sudo cp -a /etc/nginx "$backup_dir/"
记录输出对应的备份目录,后续回滚需要使用。
3. 建立允许缓存的业务清单
| 对象 | 缓存层级 | 示例有效期 | 更新或失效方式 |
|---|---|---|---|
| 带内容指纹的图片、JS、CSS | CDN与浏览器 | 30天至1年 | 发布新文件名,旧文件保留过渡期 |
| 匿名商品页、分类页 | Nginx页面缓存 | 15—30秒起步 | 短TTL、缓存版本切换或应用控制禁用 |
| 商品描述、分类关系 | Redis对象缓存 | 5—15分钟 | 数据提交后更新版本或删除关联键 |
| 公开分类列表、聚合结果 | Redis查询缓存 | 30—60秒起步 | 依赖数据变更后失效 |
| 购物车、账户、订单、支付、实时库存接口 | 不做共享缓存 | 不适用 | 每次执行授权和业务校验 |
商品页如果含有会员价、购物车数量、按IP计算的税费或个性化推荐,应拆分动态接口,或整页绕过共享缓存。价格允许短时展示旧值时可以采用短TTL;价格必须立即生效的活动页则应禁用页面缓存。
二、分步操作:从静态资源到页面与数据缓存
步骤1:先部署静态资源边缘缓存
将商品图片、主题脚本和样式放到独立资源域名,或使用明确的资源路径。首批CDN规则只覆盖这些资源,不覆盖整个商城域名。
建议应用按资源类型返回不同的响应头:
# 文件名包含内容指纹,内容变化时更换URL
Cache-Control: public, max-age=31536000, immutable
# 固定URL且会被替换的资源
Cache-Control: public, max-age=300
# 私有订单附件等敏感资源
Cache-Control: private, no-store
内容指纹可采用类似app.7f31c2.js的命名。商品图片更新后生成新URL,并保留旧文件一段时间,避免旧页面引用立即失效。不要给会原地覆盖的文件设置一年缓存,否则即使清除了CDN,浏览器仍可能继续使用旧内容。
CDN规则同时核对:
- 只缓存预期路径及正常成功响应,不长期缓存404和5xx。
- 缓存键保留影响输出的图片尺寸、格式等参数。
- 用户上传的私有文件不进入公开缓存规则。
- CDN回源地址、TLS证书与源站Host匹配。
- 回源出口与服务商约定的线路口径一致。
压缩图片、使用合适尺寸,再配合CDN,才能减少实际传输字节。单纯提高缓存命中率,无法解决首次访问时大图过多的问题。
步骤2:由应用显式批准公开页面缓存
应用仅在请求符合公开页面条件时返回:
Cache-Control: public, max-age=0, s-maxage=30
X-Public-Cache: 1
这里浏览器的新鲜度为0,共享缓存的新鲜度为30秒。对于需要个性化、登录态或实时业务结果的响应,返回:
Cache-Control: private, no-store
同时不返回X-Public-Cache: 1。
示例采用/en-us/USD/products/...这样的路径,将语言和币种显式写入URL。此时同一公开URL必须对应相同的匿名内容,不能再根据IP或其他未进入缓存键的请求信息输出不同税价。
共享页面缓存的准入条件是:路径在允许名单内、方法为GET或HEAD、无Cookie、无Authorization、无查询参数,并且应用确认该响应可公开缓存。 初次上线绕过所有Cookie请求,虽然会降低命中率,但比只识别某一个会话Cookie更容易验证安全边界。

步骤3:配置Nginx页面缓存
以下配置用于已部署HTTPS、应用监听127.0.0.1:8080的环境。域名、证书路径、应用端口和页面路径必须替换为实际值。
map、proxy_cache_path和upstream放在http上下文;server内容合并到现有站点,不要创建同域名的重复虚拟主机。示例只允许英文美元、英文英镑的商品与分类页面,其他市场应在业务核验后扩展。
# 以下内容位于 http 上下文
proxy_cache_path /var/cache/nginx/catalog
levels=1:2
keys_zone=catalog_pages:128m
max_size=20g
inactive=10m
use_temp_path=off;
map $uri $catalog_route {
default 0;
~^/(en-us/USD|en-gb/GBP)/(products|categories)/ 1;
}
map $request_method $catalog_method {
default 0;
GET 1;
HEAD 1;
}
map $http_cookie $catalog_cookie {
default 1;
"" 0;
}
map $http_authorization $catalog_auth {
default 1;
"" 0;
}
map $args $catalog_query {
default 1;
"" 0;
}
map $http_cache_control $catalog_revalidate {
default 0;
~*no-cache 1;
~*no-store 1;
~*max-age=0 1;
}
map "$catalog_route$catalog_method$catalog_cookie$catalog_auth$catalog_query$catalog_revalidate"
$catalog_bypass {
default 1;
"110000" 0;
}
map $upstream_http_x_public_cache $catalog_not_public {
default 1;
"1" 0;
}
upstream shop_app {
server 127.0.0.1:8080;
keepalive 32;
}
server {
listen 443 ssl;
server_name shop.example.com;
ssl_certificate /etc/nginx/tls/shop/fullchain.pem;
ssl_certificate_key /etc/nginx/tls/shop/privkey.pem;
# 调整该值可切换页面缓存命名空间
set $catalog_epoch "v1";
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# 缓存未压缩的上游响应,避免混淆编码变体
proxy_set_header Accept-Encoding "";
proxy_hide_header X-Public-Cache;
location ~ ^/(en-us/USD|en-gb/GBP)/(products|categories)/ {
proxy_pass http://shop_app;
proxy_cache catalog_pages;
proxy_cache_key "$catalog_epoch|$scheme|$host|$request_uri";
proxy_cache_bypass $catalog_bypass;
proxy_no_cache $catalog_bypass $catalog_not_public;
proxy_cache_valid 200 30s;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_lock_age 5s;
add_header X-Cache $upstream_cache_status always;
}
location / {
proxy_pass http://shop_app;
add_header X-Cache "OFF" always;
}
}
这份配置有几个必须保留的边界:
proxy_cache_bypass负责不读取已有缓存,proxy_no_cache负责不写入缓存,敏感请求需要同时满足这两个控制。- 默认情况下,Nginx会根据上游的
Set-Cookie、Cache-Control等决定是否存储。不要加入忽略这些头的配置来强行提高命中率。 - 上游的
s-maxage或X-Accel-Expires等可能影响有效期,因此应用应统一输出预期缓存头,不能认为配置里的30秒永远是最终TTL。 proxy_cache_lock可以减少冷缓存时的并发回源,但不是数据库限流器。- 本例不配置过期内容兜底,避免上游失败时继续展示旧价格。
缓存目录必须由实际Nginx工作进程用户可写。先通过nginx -T确认user指令,再创建专用目录;不要对整个/var/cache递归修改权限。新建目录时只影响该缓存路径,回滚配置后可以保留目录,待确认无服务使用后再处理。
修改后的发布命令为:
sudo nginx -t && sudo systemctl reload nginx
这里假定服务由systemd管理且服务名为nginx。重载影响站点请求处理规则;配置测试失败时不会执行重载,应先修正错误。
步骤4:部署Redis对象与查询缓存
Redis只缓存读取昂贵、允许重建的数据,应用使用旁路缓存流程:

- 读取缓存键。
- 未命中时查询数据库或计算结果。
- 写入带TTL的缓存,再返回给请求。
- 业务写入成功后,触发关联缓存失效。
对象键至少包含租户、语言、币种和数据版本,例如:
product:{tenant}:{sku}:{lang}:{currency}:{version}
category-list:{tenant}:{category}:{lang}:{currency}:{version}
查询缓存还需要包含规范化后的筛选条件、排序和分页参数。不能只用SQL字符串作为键,更不能把不同用户的权限结果放进同一个共享键。
失效操作应在数据库事务成功提交后执行。对于商品更新影响的商品对象、分类列表及促销列表,建立依赖关系;通过可重试事件或事务消息记录处理结果,避免数据库写入成功而失效通知丢失。
对象缓存可增加少量TTL随机偏移,减少集中到期;同一热门键未命中时限制并发重建数量。Redis不可用时允许有限地降级到数据库,并结合连接池和请求限流,不能把所有请求无限制地压向数据库。
库存扣减、优惠资格和最终成交价必须在交易流程中重新校验。 缓存中的库存数量只能用于允许短时偏差的展示,不能成为是否允许下单的唯一依据。
步骤5:选择失效方式,而不是直接清空所有缓存
| 变更情况 | 优先操作 | 主要边界 |
|---|---|---|
| 图片、JS、CSS更新 | 发布新文件名 | 旧URL仍可能被浏览器长期保存 |
| 普通商品描述更新 | 对象失效,页面等待短TTL | 允许十几秒至几十秒展示延迟 |
| 必须立即生效的价格或促销 | 禁用相关页面缓存或使用已验证的精确清除机制 | 验证Nginx与CDN两层都已处理 |
| 页面模板或缓存键错误 | 切换页面缓存版本 | 会造成较大范围冷缓存回源 |
| 错误内容进入CDN | 清除受影响URL,并修正源站规则 | 只清CDN可能再次缓存错误源站响应 |
开源Nginx不能默认认为具备任意URL清除接口。没有额外模块时,本例可通过更改$catalog_epoch切换整个页面缓存命名空间,但不适合每次商品更新都这样做。
版本切换前检查应用和数据库余量,并先验证少量页面。旧缓存文件会按缓存管理规则逐步回收,不会因版本变化立即释放空间。不要在高峰期通过删除整个缓存目录来完成常规失效。
三、结果验证:分别检查正确性、命中与带宽
1. 先绕过CDN验证源站页面缓存
以下使用文档示例IP,实际操作时替换为香港服务器地址:
curl --resolve shop.example.com:443:192.0.2.10 \
-sS -D - -o /dev/null \
https://shop.example.com/en-us/USD/products/sample-sku
curl --resolve shop.example.com:443:192.0.2.10 \
-sS -D - -o /dev/null \
https://shop.example.com/en-us/USD/products/sample-sku
正常情况下,在首个请求完成写入且未过期后,同一URL的后续请求可由MISS变为HIT。若一直未命中,应检查状态码、Set-Cookie、缓存头和公开标记,而不是直接放宽规则。
再验证绕过行为:
curl --resolve shop.example.com:443:192.0.2.10 \
-sS -D - -o /dev/null \
-H 'Cookie: session=test-only' \
https://shop.example.com/en-us/USD/products/sample-sku
curl --resolve shop.example.com:443:192.0.2.10 \
-sS -D - -o /dev/null \
https://shop.example.com/en-us/USD/products/sample-sku?preview=1
这两类请求不应返回共享缓存的HIT。使用测试账户检查会员价、购物车和账户页面时,不要把真实会话令牌写进公开终端记录或日志示例。
还要比较页面正文,确认不同语言、币种和账户没有串数据。命中头只是辅助信号,内容正确才是验收依据。
2. 检查缓存是否真正生效
按以下顺序验证:
- 修改一件测试商品的描述,确认对象缓存更新,并在约定TTL内看到页面变化。
- 修改测试促销价格,验证立即生效规则,以及结账时重新计算的价格。
- 更新一个带指纹资源,确认页面引用新URL,旧资源仍可访问。
- 请求购物车、结账和订单接口,确认它们不进入共享缓存。
- 在预发布环境模拟缓存服务不可用,观察降级是否触发数据库连接激增。
若以后启用CDN页面缓存,必须在CDN侧同步实现Cookie、Authorization、查询参数和动态路径的绕过规则,不能只依赖源站Nginx。无法可靠实现这些条件时,CDN继续只缓存静态资源。
3. 分别观察线路与应用指标
同时记录两类出口用量、CDN字节命中率、Nginx页面命中率、应用响应时间、数据库查询量及缓存磁盘使用率。国内与海外访问分开测试,CDN回源也单独记录。
Nginx页面缓存命中后,HTML仍然需要从香港服务器传给访问方,所以它主要降低计算负载,不一定显著降低源站出网。只有请求由CDN直接响应,才避免这次对应内容的源站传输。
四、失败处理:由外到内定位,不用放宽缓存规则掩盖问题
| 现象 | 优先检查 | 处理方向 |
|---|---|---|
| 页面仍慢,源站出网接近上限 | CDN字节命中率、大图尺寸、回源线路 | 优化资源与边缘命中,增加页面缓存内存通常无效 |
| 页面一直MISS | Cookie、查询参数、响应状态与缓存头 | 修正可公开页面的响应,不忽略安全头 |
| 页面HIT但价格或币种错误 | URL设计、缓存键、隐式个性化 | 立即停用相关共享缓存并核查受影响范围 |
| 更新后持续显示旧内容 | 对象键、页面TTL、CDN规则 | 沿数据到页面再到边缘逐层核查 |
| Redis故障后数据库压力升高 | 降级并发、连接池、重建请求 | 限制回源,优先保障交易链路 |
| 缓存目录报权限或空间错误 | 专用目录权限、剩余空间、错误日志 | 修正该目录或缩减缓存预算,不扩大系统目录权限 |
发现账户内容、订单信息或会员专属价格被公开缓存时,应先关闭受影响路径在Nginx和CDN上的缓存,执行对应清除,再保存日志核查。只缩短TTL不能替代事故处理。
五、回滚:恢复处理链路,避免重新使用错误缓存
本方案采用分层回滚,不要求一次撤销所有优化。
- Nginx页面缓存回滚:恢复变更前的站点配置,或移除相关路径的
proxy_cache设置,保留应用转发。恢复前再次备份当前配置,避免覆盖后丢失排查线索。 - CDN回滚:禁用新增HTML缓存规则,清除受影响URL;已验证正确的静态资源缓存可以保留。
- 对象与查询缓存回滚:通过应用开关关闭缓存读取,并限制数据库回源并发。不要清空与会话、队列或业务状态共用的Redis实例。
- 错误命名空间处理:重新启用页面缓存时使用一个全新的版本值,不回到可能含错误内容的旧版本。
- 验证恢复结果:先在源站确认动态响应及业务正确性,再从国内、海外和CDN路径分别检查。
恢复配置时,只恢复本次涉及的文件,不应直接覆盖整个配置目录,因为备份后可能存在其他有效变更。恢复完成后仍执行:
sudo nginx -t && sudo systemctl reload nginx
关闭缓存会提高应用及数据库负载,应在低峰执行,并持续观察错误率、连接数和两类出口用量。缓存文件可以暂时保留,确认不再使用后再安排清理。
六、上线与验收检查清单
- [ ] 已确认25M CN2与100M国际带宽的出口、方向、限速和监控口径,未按125Mbps单连接能力估算。
- [ ] CDN首先覆盖公开静态资源,私有附件和交易接口不在缓存范围内。
- [ ] 商品页缓存只覆盖明确的匿名路径,Cookie、Authorization和查询参数请求已验证绕过。
- [ ] 语言、币种、市场及租户差异已进入URL或缓存键,不依赖未纳入缓存键的隐式条件。
- [ ] 页面内容、响应头和缓存命中结果均已验证,不存在跨账户或跨币种串数据。
- [ ] 商品更新后对象、查询、页面和边缘缓存的失效责任已明确。
- [ ] 下单时重新校验价格、库存与优惠资格,不依赖展示缓存完成交易判断。
- [ ] Redis、缓存磁盘和冷缓存回源都有资源上限与告警。
- [ ] 已保留配置备份,并验证关闭缓存后的应用与数据库承载能力。
- [ ] 国内、海外及CDN回源分别验收,上线后持续观察带宽余量和缓存字节命中率。



