如何在香港服务器上搭建跨境电商平台,用 Nginx + 缓存加速提升海外买家访问体验

周五晚上 10 点,我们的跨境电商站要在周末做一次东南亚 + 拉美的联合促销。下午的预压里,我看着巴西和智利的买家加载首页要 5~7 秒,结账页更是卡在 3 秒以上,眼看着 A/B 组转化差了 18%——原因很简单:跨海链路抖动 + 源站没做“适配跨境”的缓存策略。
我当场拍板:不再只做“静态资源 CDN + 源站直出”那一套,而是在香港源站前面加上 Nginx 反向代理 + 分层缓存 + 微缓存,把源站变成“Origin Shield”,让全世界的边缘(或者没有边缘时的直连买家)都更稳更快。下面是我在机房那一夜的完整笔记。
场景与目标
- 场景:电商前台(Next.js/Node 或 PHP)+ 后台 API(Go/Java/PHP)+ MySQL/Redis,源站部署在 香港(CN/HK/SEA/ANZ/美西/拉美都要访问)。
- 痛点:跨洋 RTT 高、链路波动大;动态页面占比高;源站计算昂贵;静态资源已上 CDN 但命中率有限。
目标:
- 首页/类目/商品详情实现 < 2 秒首屏(海外主流地区);
- 结账等敏感页面保持一致性与正确性;
- 高并发下源站稳定,避免“回源风暴”;
- 方案可回滚、可观测、可扩展。
拓扑与分层缓存设计
[海外买家] ──(公网/或CDN)──> [HK Nginx 反代+缓存层] ──> [应用集群]
├── PHP-FPM/Node
├── API(Go/Java)
├── MySQL/Redis
策略关键词:
- 静态:强缓存 + 指纹化(1 年),Brotli/Gzip 压缩。
- 动态:微缓存 1~5 秒(列表/详情),严格绕过缓存(购物车、结账、用户态接口)。
- 回源保护:cache lock、stale-while-revalidate、use_stale on error/timeout、限流。
- 键设计:按 Host + URI + 语言/货币 + 设备类型 + 参数白名单。
- 观测:$upstream_cache_status、RTT、TTFB、命中率。
现场硬件与系统参数
硬件(HK 机柜内)
| 角色 | 型号/CPU | 内存 | 存储 | 网卡 | 备注 |
|---|---|---|---|---|---|
| Nginx 缓存层 x2 | Xeon Silver 4314 | 64 GB | 2 x 1.92TB NVMe (RAID1) + 64GB tmpfs | 2 x 10GbE | Keepalived 做 VIP |
| 应用 x4 | AMD EPYC 7313P | 128 GB | 2 x NVMe 1.92TB | 2 x 10GbE | Node/PHP/Java |
| DB/Cache | EPYC 7443P | 256 GB | 4 x NVMe | 2 x 25GbE | MySQL/Redis |
说明:热数据放 tmpfs(命中极热 Key),温数据走 NVMe,长尾回源。
操作系统与版本
| 组件 | 版本/来源 | 关键能力 |
|---|---|---|
| CentOS 7 | 最小化安装 | 兼容老业务(用户曾要求用 7) |
| Nginx 1.24+(nginx.org repo) | 链接 OpenSSL 1.1.1 | TLS1.3、http_v2、动态模块 |
| OpenSSL 1.1.1 | Nginx 官方包自带 | TLSv1.3/OCSP Stapling |
| ngx_brotli(可选) | 动态模块编译 | Brotli 压缩 |
| Keepalived | VRRP | 双机漂移 |
| GoAccess/Promtail | 日志/指标 | 可视化与告警 |
预热数据与“前后对比”
样本数据(不同网络会有差异;仅作参考)
| 地区 | 优化前首页 TTFB | 优化后首页 TTFB | 优化前首屏 | 优化后首屏 |
|---|---|---|---|---|
| 新加坡 | 420 ms | 120 ms | 1.9 s | 1.1 s |
| 东京 | 380 ms | 110 ms | 1.7 s | 1.0 s |
| 悉尼 | 520 ms | 180 ms | 2.2 s | 1.3 s |
| 洛杉矶 | 680 ms | 220 ms | 2.6 s | 1.5 s |
| 圣保罗 | 950 ms | 310 ms | 3.4 s | 1.9 s |
步骤 1|系统与内核调优(CentOS 7)
文件句柄与 backlog:
# /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
# /etc/sysctl.d/99-tuning.conf
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
# 可选:TFO(内核支持情况下)
net.ipv4.tcp_fastopen = 3
BBR 提示:CentOS 7 默认内核不带 BBR,如需启用建议用 ELRepo 新内核(谨慎评估变更窗口)。
步骤 2|安装 Nginx(开启 http_v2/TLS1.3/Brotli)
cat >/etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
EOF
yum install -y nginx
# 可选:编译并加载动态 ngx_brotli 模块(略,按环境而定)
systemd:
systemctl enable nginx
systemctl start nginx
步骤 3|缓存目录与分层(tmpfs + NVMe)
# 极热缓存(1~5 分钟):内存 tmpfs
mkdir -p /dev/shm/ngcache_hot
chmod 700 /dev/shm/ngcache_hot
# 常温缓存:NVMe
mkdir -p /data/ngcache_warm/{meta,body}
chown -R nginx:nginx /data/ngcache_warm
步骤 4|Nginx 核心配置(/etc/nginx/nginx.conf)
user nginx;
worker_processes auto;
worker_rlimit_nofile 1048576;
events {
use epoll;
worker_connections 65535;
multi_accept on;
accept_mutex off;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
# 压缩
gzip on;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript application/xml+rss image/svg+xml;
# 可选:Brotli(装了模块才生效)
# brotli on;
# brotli_comp_level 5;
# brotli_types text/plain text/css application/json application/javascript application/xml+rss image/svg+xml;
# 日志:记录缓存状态
log_format main '$remote_addr - $host [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'rt=$request_time urt=$upstream_response_time '
'u_status=$upstream_status cache=$upstream_cache_status';
access_log /var/log/nginx/access.log main;
# 缓存键:按语言/货币/设备拆分
map $http_accept_language $lang {
default en; ~*zh zh; ~*es es; ~*pt pt; ~*ja ja; ~*ko ko;
}
map $cookie_currency $currency { default USD; ~*CNY CNY; ~*JPY JPY; ~*BRL BRL; }
map $http_user_agent $device {
~*mobile|iphone|android mobile;
default desktop;
}
# 会话判断(示例:有登录/购物车 Cookie 则绕过缓存)
map $http_cookie $bypass_cache {
default 0;
~*"(session|cart|sid|auth|token)" 1;
}
# 缓存区(热缓存在内存,常温在 NVMe)
proxy_cache_path /dev/shm/ngcache_hot levels=1:2 keys_zone=HOT:128m max_size=48g inactive=10m use_temp_path=off;
proxy_cache_path /data/ngcache_warm levels=1:2 keys_zone=WARM:1g max_size=1200g inactive=1h use_temp_path=off;
# 避免回源风暴
proxy_cache_lock on;
proxy_cache_lock_timeout 10s;
proxy_cache_revalidate on;
# 上游连接池
upstream app_pool {
zone upstream_zone 64k;
server 10.0.0.11:8080 max_fails=3 fail_timeout=10s;
server 10.0.0.12:8080 max_fails=3 fail_timeout=10s;
keepalive 256;
}
# HTTPS 终止(TLS1.3)
server {
listen 443 ssl http2 reuseport;
server_name shop.example.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_stapling on; ssl_stapling_verify on;
# 静态资源:强缓存
location ~* \.(css|js|woff2?|ttf|otf|svg|gif|png|jpg|jpeg|webp|avif)$ {
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
# 一般直接走 CDN;若仍经由源站,也可用 proxy_cache 做二级缓存
proxy_pass http://app_pool;
proxy_set_header Host $host;
proxy_cache WARM;
proxy_cache_key "$scheme://$host$uri?ver=$arg_v";
proxy_cache_valid 200 206 301 302 365d;
add_header X-Cache-Status $upstream_cache_status always;
}
# 动态页面:微缓存(列表/详情)
location / {
proxy_pass http://app_pool;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
# 缓存选择:登录/购物车绕过;其他命中
proxy_no_cache $bypass_cache;
proxy_cache_bypass $bypass_cache;
# 分层:极热->HOT,次热->WARM
proxy_cache HOT;
proxy_cache_background_update on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# Key 设计:语言/货币/设备/参数白名单
set $cache_key "$scheme://$host$uri|l=$lang|c=$currency|d=$device|q=$arg_page,$arg_sort,$arg_filter";
proxy_cache_key $cache_key;
# 微缓存时长(动态)
proxy_cache_valid 200 301 10s;
proxy_cache_valid 404 1s;
add_header X-Cache-Status $upstream_cache_status always;
add_header Vary "Accept-Encoding, Accept-Language, User-Agent, Cookie" always;
# 超时
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# 结账、用户态 API:强制绕过
location ^~ /checkout/ {
proxy_pass http://app_pool;
proxy_set_header Host $host;
proxy_no_cache 1;
proxy_cache_bypass 1;
add_header X-Cache-Status BYPASS always;
}
location ^~ /api/ {
proxy_pass http://app_pool;
proxy_set_header Host $host;
proxy_no_cache 1;
proxy_cache_bypass 1;
add_header X-Cache-Status BYPASS always;
}
}
# HTTP -> HTTPS
server {
listen 80 reuseport;
server_name shop.example.com;
return 301 https://$host$request_uri;
}
}
要点
- proxy_cache_lock 抑制“回源风暴”。
- proxy_cache_background_update 让用户继续拿旧副本,后台悄悄更新。
- proxy_cache_use_stale 在上游抖动时兜底。
- 利用 $bypass_cache 精准绕过用户态。
- cache_key 控制“语言/币种/设备/参数”维度,既保证命中,又避免串缓存。
步骤 5|图片与自适应(可选)
如果业务图片多、体积大,建议上游引入图服务(如 imgproxy/thumbor),在 Nginx 前端做二级缓存:
location /img/ {
proxy_pass http://img_service;
proxy_cache WARM;
proxy_cache_key "$scheme://$host$uri|w=$arg_w|q=$arg_q|fmt=$arg_fmt";
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
add_header X-Cache-Status $upstream_cache_status always;
}
前端以 ?w=&,fmt=webp/avif 请求,Nginx 缓住不同变体。
步骤 6|清缓存与回滚
6.1 PURGE(有条件)
生产上我尽量避免第三方模块,但如果必须做精准清理,可编译 ngx_cache_purge,或以“版本号参数”替代(推荐):
- 推荐:静态资源文件名指纹(app.abcd1234.js)或 ?v=timestamp,不需要 PURGE,发版即“新 Key”。
- 动态页面:发布后将影响页的 版本号写入白名单参数(如 ?ver=20250910),由 Nginx proxy_cache_key 携带。
6.2 回滚
Nginx 配置用 原子切换(/etc/nginx/conf.d + symlink),nginx -t && nginx -s reload。
观察命中率、500/timeout、RTT,异常则回滚上一个 symlink。
步骤 7|防御与限流
# 限制爬虫/恶请求
map $http_user_agent $is_badbot {
default 0;
~*(crawler|spider|scrapy|curl) 1;
}
limit_req_zone $binary_remote_addr zone=req_perip:20m rate=10r/s;
server {
# ...
location / {
if ($is_badbot) { return 429; }
limit_req zone=req_perip burst=100 nodelay;
# ...
}
}
步骤 8|观测与日志
关键字段:$upstream_cache_status(MISS/HIT/BYPASS/STALE/UPDATING)、rt/urt/u_status。
GoAccess 快速看命中率与慢请求;Promtail/Tempo/Loki + Grafana 做看板。
建议监控:缓存命中率、TTFB、后端 5xx、回源 QPS、锁等待/超时。
真实坑位 & 现场解决
结账页被缓存
症状:个别买家看到别人的购物车。
解决:增加 bypass_cache 规则,匹配 session/cart/auth Cookie;关键页面设置 Cache-Control: private, no-store 并在 Nginx 中 proxy_no_cache 1。
多语言串缓存
症状:西语买家看到英文。
解决:cache_key 引入 $lang;同时保证应用对 Accept-Language 的解析稳定(避免随机化)。
币种切换不生效
症状:BRL 被缓存成 USD。
解决:取 $cookie_currency 进入 Key;对于未设置的用户,以 Geo/IP 决定默认,落 Cookie。
参数爆炸导致命中低
症状:?utm_* 导致每个 URL 都是新 Key。
解决:参数白名单入 Key(page/sort/filter/ver),其他参数全部忽略;或 proxy_ignore_headers 配合。
CDN 与源站缓存冲突
症状:CDN HIT 但源站 MISS,或相反。
解决:让 CDN 以源站 Cache-Control 为准;源站作为 Origin Shield,时长稍长于 CDN;对比 Age 与 X-Cache-Status 校准。
回源风暴
症状:节点重启后某热点详情页 QPS 飙升打爆应用。
解决:proxy_cache_lock on + 热点页预热(脚本批量 curl),并设置 use_stale updating。
TLS1.3 在 CentOS 7
症状:老 OpenSSL 不支持。
解决:使用 nginx.org 官方包(已链接 1.1.1);如自编译,务必链至 1.1.1。
压测与验证
工具与命令
静态/动态:wrk、h2load(HTTP/2)。
示例:
# HTTP/2
h2load -n 20000 -c 200 -m 100 https://shop.example.com/collection/sale
# HTTP/1.1
wrk -t8 -c200 -d60s --latency https://shop.example.com/product/12345
结果样本(优化前 vs 优化后)
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 平均 TTFB(SG) | 420 ms | 120 ms |
| 99 线 TTFB(SG) | 980 ms | 260 ms |
| 源站 QPS 峰值 | 5.2k | 1.3k(命中率上升) |
| 缓存命中率(首页/类目) | 35% | 78% |
| 结账页错误率 | 0.4% | 0.12%(绕缓存后稳定) |
FAQ|几个常被问到的决策
要不要上 HTTP/3?
如果前面有全球 CDN,CDN 负责 H3 即可;源站用 H2 足够。若直连海外且链路抖动大,可评估 Nginx QUIC 分支,但 上线成本高。
Nginx 和 Varnish 选哪个?
我偏向 Nginx 一体化(TLS 终止 + 反代 + 缓存 + 压缩),维护面更小;极限场景 Varnish 也非常强,但要多一层 TLS 代理。
微缓存是否会“缓存错内容”?
核心在 Key 设计 + 绕过规则。把“用户态、价格、库存、个性化”一律绕过或缩短 TTL,列表/详情的“无状态部分”微缓存 5~10 秒,一般就够了。
收尾|凌晨 3 点的日志
夜里 3 点,我在机房的走道坐下,看着面前的 GoAccess 仪表盘:HIT 从 30% 爬到了 75%,圣保罗用户的首页 TTFB 从 900 多毫秒落到 300 出头。促销页面刚刚推送完新版本,参数版本号一生效,缓存里“旧世界”立刻退场——没有清理命令,没有全站刷新,只有冷静的 Key 变更。
我合上笔记本,给同事发了条消息:“跨境电商不是只拼 CDN,源站也要有脑子。”Nginx + 分层缓存,把香港这台“心脏”变成了更强的 Origin Shield。等周一复盘时,运营把转化的曲线放出来,所有人都笑了——而我只想回机房把那台风扇调小一点。
可复用清单(Checklist)
- CentOS 7 基础调优(limits/sysctl/TFO)。
- Nginx 1.24+(TLS1.3、http2、Brotli 可选)。
- 分层缓存:HOT=tmpfs、WARM=NVMe。
- proxy_cache_lock / use_stale / background_update。
- Key 设计:Host+URI+语言+币种+设备+参数白名单。
- 绕过规则:Session/Cart/Auth/API。
- 静态资源强缓存 + 指纹化,以版本号驱动刷新。
- 观测:命中率、TTFB、回源 QPS、5xx。
- 回滚与灰度:symlink + nginx -t && reload。
- 压测校验:wrk/h2load,对比前后数据。