上一篇 下一篇 分享链接 返回 返回顶部

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

发布人:Minchunlin 发布时间:2025-09-14 09:22 阅读量:622


半夜 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→回调各环节)
目录结构
全文