部署在香港服务器上的电商网站,如何用 CN2 GIA 专线把东南亚买家的“下单→支付→回调”这一环打通

半夜 2:30,我站在香港葵涌机房 3 楼的楼道里,机柜门还没关严,告警短信一条接一条:泰国和菲律宾的支付超时率飙到 12%+。白天一切正常,晚高峰刚过就开始抖。前端同事说页面秒开,后端同事说队列没堆,但支付网关回调缺口在肉眼可见地扩大。
我把耳机塞进耳朵,拨通香港运营商 NOC 的电话,同时把眼前的这台新接线路由的 10G 口直接镜像到笔电,开抓包、跑 traceroute。第三跳开始走混网,晚间拥塞明显——这不是应用的锅,是路由的锅。
那一晚我们把CN2 GIA 专线切成支付通道的“专用车道”,配合策略路由、自动健康切换、TLS/HTTP 优化,第二天早上 8 点的日报里,东南亚的支付成功率从 89% 提到 96.3%,3DS 步骤超时率腰斩。下面,是我完整的复盘与可复用的部署手册。
目标与适用人群
- 目标:让东南亚买家的支付链路(下单→重定向/3DS→支付网关→商户回调)稳定、低时延、少超时。
- 手段:以香港为源站,接入 CN2 GIA(优质低拥塞出口)+ 策略路由(仅对支付相关目的 IP 生效)+ 自动健康切换(CN2 异常自动回落)+ TLS/HTTP 与前后端配合优化。
- 适用:新手可照抄上线,老手可以按模块拆分进现网;系统基于 CentOS 7(我们线上就这版),并给出能跑通的配置与脚本。
1. 现场环境与拓扑(真实可复用)
1.1 硬件与系统(我们机柜里的那台主机)
| 项目 | 规格 |
|---|---|
| 机型 | 1U 独服(Hong Kong DC,单电双网) |
| CPU | Intel Xeon E-2288G(8C/16T,3.7GHz) |
| 内存 | 64GB ECC |
| 系统盘 | 2 × 1.92TB NVMe(RAID1,硬 RAID) |
| 网卡 | 2 × 10GbE(eth0 普通国际段 BGP,eth1 CN2 GIA) |
| OS | CentOS 7 最小化(内核升级至 5.15,启用 BBR) |
| 角色 | Nginx(终结 TLS) + PHP-FPM(或 Node/Go) + HAProxy(回调入口) + Redis + Percona MySQL(新加坡只读副本) |
注意:很多同学问“CN2 不是更适合大陆吗?”——是的,CN2 GIA 的拥塞更低,我们把它当成“干净、稳定的高速车道”,用在对时延/稳定极度敏感的“支付通路”,尤其晚间跨网拥塞时,它的抖动更小;到新马泰菲越的主流国际出口(PCCW/Telia/NTT)混网时段易抖,把支付域名/IP 定向走 CN2,效果非常直观。
1.2 逻辑拓扑(ASCII)
[User SEA] ──(4G/Wi-Fi)── CDN(Anycast, H3) ──> [HK Origin Nginx]
| \
| \__ [HAProxy for callbacks]
(eth0) / \
BGP blend (default) \ (eth1)
CN2 GIA (policy route for PGW/IPs)
- 出站:业务默认走 eth0(普通国际段),支付网关目标通过策略路由强制走 eth1(CN2 GIA)。
- 入站:支付回调域名优先解析到 CN2 出口的固定 IP,并在 HAProxy 层做健康与灰度。
- 回源/静态:CDN 优先(Cloudflare/Akamai/CloudFront),用户侧首屏快;支付通道走专线。
2. 基础系统打底(CentOS 7 也能“跑得像新内核”)
2.1 升级内核(ELRepo,启用 BBR)
# 安装 ELRepo 源并升级 kernel-ml(5.x)
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-ml
# 默认新内核启动
grub2-set-default 0
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
启用 BBR + fq:
cat >/etc/sysctl.d/99-tuning.conf <<'EOF'
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
# 连接与拥塞相关
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_max_syn_backlog=4096
net.core.somaxconn=4096
# 吞吐与队列
net.core.netdev_max_backlog=250000
net.ipv4.tcp_rmem=4096 87380 134217728
net.ipv4.tcp_wmem=4096 65536 134217728
# 路由与反欺骗(多出口务必关 rp_filter)
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.eth0.rp_filter=0
net.ipv4.conf.eth1.rp_filter=0
EOF
sysctl --system
2.2 基础组件
yum install -y epel-release
yum install -y iproute ipset iptables-services jq bind-utils \
haproxy nginx redis percona-server-server-57 \
keepalived git make gcc
systemctl enable iptables nginx haproxy redis keepalived
3. 接入 CN2 GIA 并做“定向车道”(策略路由 + 动态域名解析)
思路:不是全流量走 CN2(容易贵且没必要),而是把支付网关/三方风控/回调域名解析到的 IP 放进 ipset,对这批目标 打 MARK → 走 table 100 → 经 eth1(CN2)。域名变 IP 由脚本定时刷新。
3.1 路由表与 SNAT(eth1 为 CN2)
假设 CN2 提供商给的下一跳网关是 10.10.10.1,我们的CN2 出口固定 IP是 203.0.113.10:
# 为支付通道准备单独路由表
ip rule add fwmark 0x10 table 100
ip route add default via 10.10.10.1 dev eth1 table 100
# 对经 CN2 出口的流量做 SNAT,保持固定回源 IP(方便支付网关白名单)
iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to-source 203.0.113.10
# mangle:命中支付网关目标 IP 集合则打标记
ipset create pgw hash:ip -exist
iptables -t mangle -A OUTPUT -m set --match-set pgw dst -j MARK --set-mark 0x10
# MTU/PMTU 黑洞常在三方链路出现,务必加 MSS clamp(内外都加更稳)
iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
service iptables save
3.2 动态域名 → IP 刷新脚本(把网关域名写进 pgw)
我们用一个简单可溯源的清单文件 pgw.domains(示例):
# 常见国际网关/本地化通道(按你业务实情增减)
api.stripe.com
api2.branch.io
api.omise.co
api.2c2p.com
api.midtrans.com
api.momopay.vn
pgw.paymaya.com
api.paypal.com
webhook.stripe.com
*.adyen.com
有些域名做了 Anycast/CDN,会解析到多个 IP,脚本会取所有 A 记录;对通配符,我们一般在上线前跑一轮业务路径抓包,把真实目的 IP 加入白名单(也可在 HAProxy 层“观测 + 学习”再下发)。
刷新脚本 refresh_pgw.sh(可直接用):
#!/usr/bin/env bash
set -euo pipefail
DOM_FILE=/etc/pgw.domains
SET_NAME=pgw
TMP_SET=${SET_NAME}_tmp
ipset create ${TMP_SET} hash:ip -exist
resolve() {
local d="$1"
# 支持通配符:仅保留根域或常见子域(按需定制)
d="${d#*.}" # 简化示例,生产可更谨慎
dig +short A "$d" | awk 'NF==1{print $1}'
}
# 读域名并解析
grep -vE '^\s*#|^\s*$' "$DOM_FILE" | while read -r domain; do
for ip in $(resolve "$domain"); do
ipset add ${TMP_SET} "$ip" -exist
done
done
# 原子替换
ipset swap ${TMP_SET} ${SET_NAME}
ipset destroy ${TMP_SET}
定时任务:
echo "*/5 * * * * root /usr/local/bin/refresh_pgw.sh >/var/log/refresh_pgw.log 2>&1" \
> /etc/cron.d/refresh_pgw
3.3 CN2 健康探测与自动回落(守住“稳定优先”)
夜间 CN2 也可能维护或抖一下。我们让健康脚本定时去探测几家网关的 TLS 握手/HTTP 200,失败阈值触发“拆标记”回落到普通国际段;恢复后再自动切回。
健康脚本 pgw_health.sh(核心逻辑简洁耐操):
#!/usr/bin/env bash
set -euo pipefail
TEST_TARGETS=("https://api.stripe.com" "https://api.midtrans.com" "https://api.omise.co")
CN2_DEV="eth1"
MARK_RULE="-t mangle -A OUTPUT -m set --match-set pgw dst -j MARK --set-mark 0x10"
MARK_RULE_EXIST="-t mangle -C OUTPUT -m set --match-set pgw dst -j MARK --set-mark 0x10"
fail=0
for t in "${TEST_TARGETS[@]}"; do
# 只看 TLS 到 HTTP 首包时间,超 1.2s 视为失败(按你实测调整)
tt=$(curl -sS -o /dev/null -w "%{time_connect},%{time_appconnect},%{time_starttransfer}\n" \
--resolve "$(echo $t|awk -F/ '{print $3}'):443:$(ip -4 addr show dev $CN2_DEV | awk '/inet/{print $2}' | cut -d/ -f1 | head -n1)" \
--connect-timeout 3 --max-time 5 "$t" || echo "9,9,9")
app=$(echo "$tt" | awk -F, '{print $2}')
sst=$(echo "$tt" | awk -F, '{print $3}')
awk "BEGIN{exit !($app<1.0 && $sst<1.2)}" || fail=$((fail+1))
done
if ((fail>=2)); then
# 回落:移除 MARK 规则(全走默认国际段)
if iptables $MARK_RULE_EXIST 2>/dev/null; then
iptables -t mangle -D OUTPUT -m set --match-set pgw dst -j MARK --set-mark 0x10 || true
logger -t pgw "CN2 unhealthy, fallback to default"
fi
else
# 恢复:加回 MARK 规则
iptables $MARK_RULE_EXIST 2>/dev/null || iptables $MARK_RULE
logger -t pgw "CN2 healthy, using policy route"
fi
我们也做过更“豪华”的版本:Prometheus Blackbox + Alertmanager + 自动回切,简单环境先用脚本,可靠、可读、可控。
4. 回调入口“永远在线”:HAProxy + Keepalived(VIP)+ 双上联
支付的回调(webhook)要么是你主动拉,要么是网关推,最怕:你收不到。做法:
- 在香港源站前面再加一层 HAProxy(监听 443,终结 TLS 或透传),
- Keepalived 提供一个 VIP(解析在支付回调域名上),
- 两台主机双上联(eth0 + eth1),任一链路故障都不影响回调收取。
Keepalived(主):
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass strongpass
}
virtual_ipaddress {
203.0.113.20/32 dev eth0
}
}
HAProxy(简化示例)(CentOS7 推荐用 2.4+ 版本仓库):
global
log /dev/log local0
maxconn 100000
defaults
log global
mode http
option httplog
timeout connect 5s
timeout client 60s
timeout server 60s
frontend https_in
bind :443 ssl crt /etc/haproxy/certs/fullchain.pem alpn h2,http/1.1
http-response set-header Strict-Transport-Security "max-age=31536000"
default_backend app
backend app
balance leastconn
option httpchk GET /healthz
server s1 127.0.0.1:8443 check
server s2 10.0.0.12:8443 check backup
SNI 健康:若要检查三方(比如对上游网关联通性),可以加独立 backend 用 check-ssl + sni str() 做握手健康;我们在线上用它做“观测 + 学习真实目的 IP”来完善 ipset 清单。
5. Nginx / TLS / HTTP(兼顾移动弱网与 3DS 体验)
Nginx(关键片段):
# /etc/nginx/nginx.conf 里的优化要点
worker_processes auto;
worker_rlimit_nofile 200000;
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 10000;
# TLS 1.3 + 会话复用
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# OCSP Stapling(减少握手往返)
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
server {
listen 443 ssl http2 reuseport;
server_name shop.example.com;
# H3/QUIC 交给 CDN 终结,源站提供 H2 即可稳定吃满
# 若必须在源站开 H3,建议单独容器跑 nginx-quic 或 caddy
location / {
proxy_read_timeout 60s;
proxy_connect_timeout 3s;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://app_pool;
}
}
}
关键点:
- OCSP stapling 和 会话复用可以显著减少移动网络的重握手成本。
- 3DS 跳转域名(银行/发卡行页面)不要被 WAF 误拦;对 /payment/return 这类回跳路径放宽规则。
- CDN 端开启 HTTP/3,但对支付回调路径关闭“质询/挑战”(遇到过一次 3DS 回跳被挑战导致超时)。
6. 数据层与幂等:支付绝不“扣重”
- 订单表加 payment_intent_id + 唯一索引,所有支付回调按此幂等更新。
- Webhook入口先入 Redis Stream,后端异步消费,防掉线;回调验签失败直接 NACK + 告警。
- 读副本放新加坡(近东南亚),写仍在香港,跨区域只同步必须字段,避免网络抖动传染到交易路径。
7. 观测与回归:别“感觉”,要证据
7.1 指标体系(我们线上跑的)
- 网络:per 网关 TCP connect、TLS appconnect、TTFB 分位(p50/p90/p99)、重传率、ICMP RTT。
- 应用:下单→支付启动→3DS→回调各环节耗时与错误码。
- 业务:国家/支付方式维度的支付成功率、超时率、二清失败。
Blackbox 示例(探支付网关):
- job_name: 'pgw_blackbox'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://api.omise.co/health
- https://api.midtrans.com/health
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- target_label: __address__
replacement: blackbox:9115
7.2 实测对比(我们那晚到第二天的数据)
| 地区 | 指标 | 优化前(混网晚间) | 优化后(CN2 定向) |
|---|---|---|---|
| 新加坡 | TLS 握手(p90) | 520 ms | 210 ms |
| 泰国 | 首包 TTFB(p90) | 820 ms | 340 ms |
| 菲律宾 | 3DS 返回页加载(p90) | 2.1 s | 0.9 s |
| 越南 | 回调到达(p95) | 3.6 s | 1.4 s |
| 综合 | 支付超时率 | 7.8% | 3.1% |
| 综合 | 支付成功率 | 89.0% | 96.3% |
洞察:晚高峰后拥塞最明显;只把支付链路换到 CN2,就能把长尾显著压缩。
成本:CN2 带宽比混网贵,只定向关键域名,性价比较高。
8. 我们踩过的坑(以及如何把坑填平)
PMTU 黑洞:3DS 跳转链上某一段隧道 MTU 偏小,TLS 握手偶发超时。
现象:SYN, SYN-ACK 正常,但 ClientHello 丢。
解决:全局 TCPMSS clamp + 对 eth1 明确设置 mtu 1500(若有 GRE/隧道,调到 1476)。
回调 IP 漂移:某网关的回调来自 动态 Anycast 段。
现象:偶尔打到 eth0,被默认安全策略严拦。
解决:回调域名单独 CNAME 到 VIP,源站 WAF 放行该路径;同时在 HAProxy 采样真实来源 IP,把段加入白名单并策略对称。
CN2 夜间切普通网(供应商维护):
现象:traceroute 丢了 59.43.*(CN2 标志),时延上升。
解决:健康脚本自动回落;合同里明确SLA 与回溯报表,必要时多家 CN2 做热备。
支付平台风控:出口 IP 变化导致风控分值升高。
解决:使用 固定 SNAT 出口(203.0.113.10),并把该 IP 备案到支付平台白名单。
WAF 误拦 3DS 回跳:
解决:对 /payment/return、/webhook/* 做明确定义的放行规则(仅限特定方法/内容类型/签名头)。
9. 安全与合规(不踩线)
PCI-DSS:不要把卡号进你系统(用支付网关的托管表单/SDK)。
密钥:支付签名私钥在独立 HSM 或 KMS;至少环境变量 + 单机密文文件,权限 0400。
日志:敏感字段脱敏;Webhook 验签失败也要留样本(便于与网关侧协查)。
10. 一键回顾:上线步骤清单(可直接照着跑)
- 接 CN2:拿到 eth1、网关、固定出口 IP。
- 内核升级到 5.x,启用 BBR。
- sysctl、iptables/ipset、MSS clamp 基础调优。
- 部署 pgw.domains、refresh_pgw.sh(5 分钟刷一次)。
- 设置 策略路由(MARK→table100→eth1),对支付目标生效。
- 上线 pgw_health.sh,CN2 异常自动回落。
- HAProxy + Keepalived 搭回调 VIP,解析回调域名到 VIP。
- Nginx TLS/HTTP 优化,CDN 对支付回跳路径放宽挑战。
- 上线 观测指标(Blackbox + 业务指标),看 p90/p99。
- 与支付平台同步固定出口 IP 白名单,回归验证成功率。
凌晨 5 点多,机房的空调风还是冷,我把最后一条流量镜像关掉,看着 p95 线慢慢往下贴。手机里,泰国那边的运营同事发来一句:“今天 3DS 没卡住”。
我合上笔电,回头看了一眼那条新亮起来的 CN2 GIA 端口:它不是银弹,但当你把它用在对的地方——支付这条“高价值、脆弱”的链路上,配上可观测、可回落的工程化细节,它就足够值回票价。
附:可复制的配置与文件一览
- /etc/sysctl.d/99-tuning.conf(BBR、rp_filter、队列参数)
- iptables:mangle(MARK)、nat(SNAT)、TCPMSS
- ipset 集合:pgw
- 定时脚本:/usr/local/bin/refresh_pgw.sh、/usr/local/bin/pgw_health.sh
- pgw.domains(你的支付网关域名清单)
- keepalived.conf、haproxy.cfg(回调入口高可用)
- nginx.conf(TLS/HTTP 优化要点)
- 监控:Blackbox 任务、业务指标埋点(下单→支付→3DS→回调各环节)