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

“工单老是转不动,客户在电话里等了三分钟。”——那晚值班电话把我叫醒。
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、直连兜底。