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

如何把“香港服务器上的 RHEL9 CRM 平台”从国内访问卡顿,优化到丝滑:基于 CN2 GIA 前置加速 + 数据库中间件(ProxySQL)的一整套可复制部署与调优方案

发布人:Minchunlin 发布时间:2025-09-25 10:04 阅读量:749


“工单老是转不动,客户在电话里等了三分钟。”——那晚值班电话把我叫醒。

CRM 跑在香港机房(RHEL 9),白天一切正常,一到晚高峰,国内用户打开页面就像穿越迷雾。curl -w 显示 TTFB 动辄 1.8~2.5 秒,偶发超时。Traces 显示国内绕路、丢包,典型的跨境访问劣化。

我给自己定了两件事:

  • 用 CN2 GIA 前置节点打通“国内入口 → 香港”的高质量回源通道;
  • 用 数据库中间件(ProxySQL) 把连接池、读写分离、查询路由做起来,顶住并发峰值。

下面是那一晚到上线后一周,我的完整记录与复现手册。

目标与架构

目标

  • 国内用户访问 CRM 首页 P95 TTFB ≤ 450 ms,接口 P95 ≤ 600 ms;
  • 峰值 2.5K 并发时 数据库无排队,应用层无明显队头阻塞;
  • 宕一个前置节点或从库,服务 不间断。

最终架构(可最小化部署)

  • CN2 GIA 前置节点(内地):HAProxy 7/4 反向代理 + WireGuard 隧道回源香港(也可选 GRE/IPsec,本文用 WireGuard)
  • 香港主机(RHEL 9):Nginx + PHP-FPM(或你自研的服务)+ MySQL 8(Primary)+ ProxySQL(中间件)
  • 香港只读从库(可选):MySQL 8 Replica(读扩展、报表/导出请求下沉)

没有从库也能获益:ProxySQL 的连接池、规则与缓存同样能显著降延迟、稳 RT。

环境与参数(实配示例)

角色 位置/线路 规格 系统/内核 关键组件
前置节点 A / B 深圳/广州(CT CN2 GIA 入口) 4C/8G/10G NVMe;带宽≥100Mbps RHEL 9.3 / 5.14 HAProxy 2.9,WireGuard 1.0.202...
应用+主库(HK-APP-DB) 香港 8C/32G/2×1.92TB NVMe(RAID1) RHEL 9.3 / 5.14 Nginx 1.24、PHP-FPM 8.2、MySQL 8.0.36、ProxySQL 2.6
从库(HK-DB-RO,可选) 香港 4C/16G/960G NVMe RHEL 9.3 MySQL 8.0.36

说明:前置节点建议双活(A/B)+ Keepalived 漂移 VIP;单点同样可跑通,但不建议生产。

基线:用数据证明“慢在哪里”

1) 路由与丢包

mtr -rwzc 100 crm.example.com
# 观察国内->HK 的 AS 路径、是否出现高抖动/单跳丢包

2) 应用端 TTFB / 接口耗时

curl -s -o /dev/null -w 'time_namelookup:%{time_namelookup}\nconnect:%{time_connect}\nappconnect:%{time_appconnect}\nstarttransfer:%{time_starttransfer}\ntotal:%{time_total}\n' \
https://crm.example.com/api/ping

3) 压测(国内节点)

wrk -t8 -c512 -d60s https://crm.example.com/api/customer/list

初始结果(节选)

指标 P50 P95 P99
首页 TTFB 820ms 2100ms 3200ms
核心接口 /api/customer/list 980ms 2400ms 3800ms
丢包(mtr) 2%~5% 峰值跳变

方案设计要点

CN2 GIA 前置加速:

  • 在内地部署前置节点(走 CN2 GIA 入网),对外 80/443,回源到香港走 WireGuard 隧道;
  • HAProxy 终止 TLS(也可透传 SNI),静态资源可本地缓存/回源 CDN;
  • 隧道 MTU、MSS、RTO 等细节处理,避免黑洞/重传。

数据库中间件(ProxySQL):

  • 与应用同机,走 UNIX Socket/127.0.0.1,降低 hop;
  • 建立连接池,避免 PHP-FPM/应用频繁建连;
  • 读写分离(可选从库),报表/列表类读请求落入只读组;
  • 查询路由/正则规则,对慢 SQL、统计查询定向到 RO;
  • 内置 Query Cache + TTL,降低热点查询的回源开销。

实操一:RHEL 9 系统与内核网络调优

基础包与时间同步

sudo dnf install -y epel-release chrony tuned
sudo systemctl enable --now chronyd
sudo timedatectl set-timezone Asia/Hong_Kong
sudo tuned-adm profile network-latency

sysctl(BBR/TFO/队列等)

RHEL 9 默认 CUBIC,可启用 BBR v1;也可用 CUBIC,关键是队列与缓冲

cat <<'EOF' | sudo tee /etc/sysctl.d/99-crm-net.conf
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864

net.ipv4.tcp_mtu_probing = 1
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_fastopen = 3

net.ipv4.ip_local_port_range = 10000 65000
fs.file-max = 2097152
EOF

sudo sysctl --system

Nginx / PHP-FPM 关键点

  • Nginx:worker_processes auto; worker_connections 65535;、启用 HTTP/2、Gzip/Brotli(若浏览器支持)。
  • PHP-FPM:pm = dynamic,pm.max_children 根据内存与脚本耗时测算,pm.max_requests=500 避免内存泄漏累积。

实操二:CRM 与 MySQL 部署(香港)

安装

sudo dnf install -y nginx php php-fpm php-mysqlnd php-opcache php-gd php-intl php-mbstring php-xml php-zip
sudo dnf install -y mysql-server
sudo systemctl enable --now nginx php-fpm mysqld

MySQL 基础优化(/etc/my.cnf.d/server.cnf)

[mysqld]
innodb_buffer_pool_size=16G
innodb_log_file_size=2G
innodb_flush_log_at_trx_commit=1
innodb_flush_method=O_DIRECT
max_connections=2000
table_open_cache=8192
thread_cache_size=64
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=mysql-bin
server_id=1001    # 主库唯一

mysql_secure_installation
mysql -uroot -p -e "CREATE USER 'crm'@'127.0.0.1' IDENTIFIED BY 'StrongP@ss!'; GRANT ALL PRIVILEGES ON crmdb.* TO 'crm'@'127.0.0.1'; FLUSH PRIVILEGES;"

你的 CRM(SuiteCRM/Vtiger/自研)按官方手册导入数据库并完成安装向导。此处略。

实操三:ProxySQL(核心)

安装与开机

sudo dnf install -y proxysql
sudo systemctl enable --now proxysql

建议部署位点

与应用同机:App → 127.0.0.1:6033(ProxySQL)→ MySQL 主/从

这样本机回环极快,连同连接池收益最大

基本配置(通过 Admin 6032 端口)

-- 连接 ProxySQL 管理口
mysql -uadmin -padmin -h 127.0.0.1 -P6032

-- 1) 后端主/从(无从亦可,仅主库)
INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, max_connections) VALUES
(10,'127.0.0.1',3306,1,2000);             -- 写组(主库)
-- 可选:从库
INSERT INTO mysql_servers(hostgroup_id, hostname, port, weight, max_connections) VALUES
(20,'10.0.10.22',3306,1,1000);            -- 读组(从库)

-- 2) 监控账户(在 MySQL 创建 monitor@'127.0.0.1' 并授权)
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='Mon1torP@ss' WHERE variable_name='mysql-monitor_password';

-- 3) 用户映射(应用连接 ProxySQL 使用 crm/密码)
INSERT INTO mysql_users(username,password,default_hostgroup,transaction_persistent,active) VALUES
('crm','StrongP@ss!',10,1,1);

-- 4) 读写分离规则(仅当有从库)
-- 针对 SELECT 且非事务内的查询分配到 20 读组
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES
(100,1,'^SELECT .*',20,1);

-- 保证事务内一致性(BEGIN...COMMIT)走写组
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES
(90,1,'^BEGIN',10,1);
INSERT INTO mysql_query_rules (rule_id,active,match_digest,destination_hostgroup,apply) VALUES
(91,1,'^START TRANSACTION',10,1);

-- 5) 查询缓存(对列表页/字典表等热点查询)
UPDATE global_variables SET variable_value='600' WHERE variable_name='mysql-query_cache_size_MB';
UPDATE global_variables SET variable_value='300' WHERE variable_name='mysql-query_cache_ttl';  -- 300s
UPDATE global_variables SET variable_value='1' WHERE variable_name='mysql-query_cache_enabled';

-- 6) 连接池
UPDATE global_variables SET variable_value='2000' WHERE variable_name='mysql-max_connections';
UPDATE global_variables SET variable_value='2048' WHERE variable_name='mysql-threads';

-- 7) 生效并落盘
LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK;
LOAD MYSQL SERVERS TO RUNTIME;   SAVE MYSQL SERVERS TO DISK;
LOAD MYSQL USERS TO RUNTIME;     SAVE MYSQL USERS TO DISK;
LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK;

审核 SQL:对写多读少、强一致接口,建议在应用侧打标签(如 /*rw=write*/)配合规则强制入写组,避免被正则误分流。

从库(可选)快速复制

在主库:

CREATE USER 'repl'@'10.%' IDENTIFIED BY 'ReplP@ss!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.%';
FLUSH PRIVILEGES;

在从库(server_id 需唯一):

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='10.0.10.11', SOURCE_USER='repl', SOURCE_PASSWORD='ReplP@ss!',
  SOURCE_AUTO_POSITION=1, SOURCE_SSL=1;
START REPLICA;
SHOW REPLICA STATUS\G

实操四:CN2 GIA 前置加速(WireGuard 隧道 + HAProxy)

1) WireGuard(前置节点 ↔ 香港)

前置节点(内地) /etc/wireguard/wg0.conf

[Interface]
Address = 172.31.0.1/30
PrivateKey = <FRONTIER_PRIVATE_KEY>
ListenPort = 51820
MTU = 1420

[Peer]
PublicKey = <HK_PUBLIC_KEY>
AllowedIPs = 172.31.0.2/32
Endpoint = <HK_PUBLIC_IP>:51820
PersistentKeepalive = 15

香港(回源) /etc/wireguard/wg0.conf

[Interface]
Address = 172.31.0.2/30
PrivateKey = <HK_PRIVATE_KEY>
ListenPort = 51820
MTU = 1420

[Peer]
PublicKey = <FRONTIER_PUBLIC_KEY>
AllowedIPs = 172.31.0.1/32
Endpoint = <FRONTIER_CN2GIA_IP>:51820
PersistentKeepalive = 15

启用:

sudo systemctl enable --now wg-quick@wg0

MSS Clamp(强烈建议,避免黑洞)

# 前置节点
sudo nft add table inet wg_mss
sudo nft 'add chain inet wg_mss forward { type filter hook forward priority 0; }'
sudo nft 'add rule inet wg_mss forward tcp flags syn tcp option maxseg size set 1360'

实测 CN2 GIA + WireGuard 在 1360~1380 MSS 较稳,按你网络调整。

2) HAProxy(前置节点)

安装与证书(以 ACME 自动签发):

sudo dnf install -y haproxy certbot
# 证书生成略,或使用现成证书

/etc/haproxy/haproxy.cfg(TLS 终止 + 回源隧道 IP)

global
  log /dev/log local0
  maxconn 100000
  tune.ssl.default-dh-param 2048

defaults
  mode http
  timeout connect 5s
  timeout client  60s
  timeout server  60s
  option httplog

frontend https_in
  bind :443 ssl crt /etc/haproxy/certs/crm.pem alpn h2,http/1.1
  http-response set-header X-Accel-From cn2-gia
  default_backend crm_hk

backend crm_hk
  balance uri
  option httpchk GET /healthz
  http-check expect status 200
  server hk 172.31.0.2:443 ssl verify none check

想透传 TLS 可用 mode tcp + SNI ACL;我选择终止 TLS 便于观测与压测。

3) 路由策略(可选)

只让 CRM 的回源流量走隧道:

# 标记 443 回源
sudo nft add table inet rt
sudo nft 'add chain inet rt output { type route hook output priority mangle; }'
sudo nft 'add rule inet rt output tcp dport 443 meta mark set 66'
# policy route
echo '100 wg' | sudo tee -a /etc/iproute2/rt_tables
sudo ip rule add fwmark 66 table wg
sudo ip route add default dev wg0 table wg

4) 双机热备(可选)

前置节点 A/B + Keepalived:

vrrp_instance VI_1 {
  state BACKUP
  interface eth0
  virtual_router_id 51
  priority 100
  advert_int 1
  virtual_ipaddress {
    103.xxx.xxx.10/24
  }
}

验证与对比(上线一周观测)

网络侧(国内 → 前置 → 隧道 → 香港)

  • mtr 丢包从 2%~5% → <0.5%
  • RTT 抖动显著降低,跨境跳数更稳定

应用/接口延迟(真实样本 300K 次)

指标 优化前 P50 优化前 P95 优化后 P50 优化后 P95 备注
首页 TTFB 820ms 2100ms 190ms 420ms CN2 + TFO + 缓存
/api/customer/list 980ms 2400ms 240ms 560ms ProxySQL 缓存/RO
错误率 0.8% 0.12% 峰值期间超时消失

数据库(峰值 2.5K 并发)

指标 优化前 优化后
活动连接峰值 1,800 420(大部分走连接池)
InnoDB 行锁等待 偶发尖峰 几乎无
复制延迟(RO) 20–80ms(可接受)

现场“坑”与解决过程

MTU 黑洞:

现象:部分电信/联通用户加载大响应体卡住,小包正常。

排查:tcpdump 看到重传、ICMP 不通;MSS 未钳制。

解决:WireGuard MTU=1420 + nftables MSS=1360,并启用 tcp_mtu_probing=1。

SELinux 拦截(RHEL 9 默认 Enforcing):

现象:HAProxy 回源 443 报 Permission denied。

解决:setsebool -P haproxy_connect_any 1,或为特定端口标记 semanage port -a -t http_port_t -p tcp 443。

firewalld/nft 与路由策略冲突:

现象:Policy Route 生效后,偶发连接走默认路由。

解决:为 wg0 设定独立 zone,确保 fwmark 规则先于其他 mangle;核查 priority。

ProxySQL 规则误分流:

现象:事务内 SELECT 被打到从库。

解决:增加 BEGIN/START TRANSACTION 前置规则(权重更小的 rule_id 更早匹配),并在应用侧为强一致查询加注释 /*rw=write*/ 再写规则匹配此注释。

复制延迟导致读脏:

现象:用户提交后立即刷新列表看不到刚更新的数据。

解决:提交后 1–2 次查询临时加写路由(Sticky),或开启 transaction_persistent=1 保证会话内一致性。

BBR 与运营商队列:

现象:BBR 在少数上行超满链路表现不如 CUBIC。

解决:保留 CUBIC 回退选项:tcp_congestion_control=cubic,按需切回。

运维与应急

蓝绿回退:前置故障时,DNS 低 TTL(30s)与 VIP 漂移,支持秒级切换到备用前置或直连香港(临时)。

备份:xtrabackup 每日全量 + binlog 增量;从库每周演练还原。

SLA 观测:前置节点 Prometheus + Exporter(HAProxy、WireGuard、Nginx)、MySQL/ProxySQL Dashboard(连接池命中、规则命中率、Query Cache 命中)。

关键配置清单(可直接套用/对比)

Nginx(香港,片段)

worker_processes auto;
events { worker_connections 65535; }

http {
  sendfile on;
  tcp_nopush on;
  keepalive_timeout  65;
  gzip on;

  server {
    listen 443 ssl http2;
    server_name crm.example.com;
    root /var/www/crm/public;

    ssl_certificate     /etc/letsencrypt/live/crm/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/crm/privkey.pem;

    location ~ \.php$ {
      fastcgi_pass   unix:/run/php-fpm/www.sock;
      include        fastcgi_params;
      fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location /healthz { return 200 'ok'; }
  }
}

PHP-FPM(片段 /etc/php-fpm.d/www.conf)
pm = dynamic
pm.max_children = 128
pm.start_servers = 16
pm.min_spare_servers = 16
pm.max_spare_servers = 32
pm.max_requests = 500
listen = /run/php-fpm/www.sock

ProxySQL(持久化配置文件可选)
datadir="/var/lib/proxysql"
admin_variables=
{
  admin_credentials="admin:admin"
  mysql_ifaces="127.0.0.1:6032"
}
mysql_variables=
{
  threads=2048
  max_connections=2000
  interfaces="127.0.0.1:6033;/tmp/proxysql.sock"
  query_cache_size_MB=600
  query_cache_stores_empty_result=1
  query_cache=1
}

firewalld(允许必须的端口)
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-port=51820/udp
firewall-cmd --reload

上线复盘|“终于是正常的速度了”

新版本发布后的第一个周一,我特地在国内的 4G/家宽/公司专线各测了一遍:
首页 200ms 多一点,列表接口 300ms 多,客户工单“转着等”的抱怨没有再出现。夜里巡检的我,终于不是被电话吵醒的那一个。

FAQ:替代与扩展

可以用 GRE/IPsec 吗? 可以,但 WireGuard 轻量、易维护、性能稳。

没有从库,读写分离还有意义吗? 连接池、缓存、规则依然很有价值;从库是锦上添花。

能否叠加国内 CDN? 强烈建议对静态资源启用国内 CDN,回源走前置节点或直回香港。

BBR 一定更好吗? 不是。少数链路 CUBIC 更稳,保留切换策略。

结尾|把“快”做成习惯

这次优化对我最有价值的不是某个参数,而是从链路到中间件再到应用层的“一条龙闭环”。
跨境访问这件事没有银弹,但CN2 GIA 的前置 + 合理的数据库中间件,把“网络物理不可控”与“应用可控”两端都拉满,就能在大多数场景里把系统稳稳托住。

附:最小可运行步骤清单(Checklist)

  •  香港 RHEL9:Nginx/PHP-FPM/MySQL/ProxySQL 安装;sysctl/tuned;
  •  MySQL:主库参数、(可选)从库 GTID 复制;
  •  ProxySQL:用户、后端、规则、缓存、生效与持久;
  •  内地前置:WireGuard 隧道、MSS Clamp、HAProxy TLS 终止回源;
  •  监控:HAProxy/ProxySQL/MySQL/Nginx 指标 + 告警;
  •  压测与对比表:TTFB、P95、错误率、连接池占比;
  •  回退策略与演练:DNS TTL、VIP、直连兜底。
目录结构
全文