戰(zhàn):Nginx 與 Gateway 協(xié)同部署指南)
1. 項(xiàng)目概述為什么 WSS 配置成了 GatewayWorker 項(xiàng)目上線前的“最后一道關(guān)卡”GatewayWorker 是國內(nèi) PHP 社區(qū)廣泛采用的長連接服務(wù)解決方案它用 Swoole 實(shí)現(xiàn)了 Gateway、BusinessWorker、Register 三進(jìn)程模型把 WebSocket 連接管理、業(yè)務(wù)邏輯處理、服務(wù)注冊發(fā)現(xiàn)做了清晰分層。很多團(tuán)隊(duì)在開發(fā)實(shí)時通知、在線客服、協(xié)同編輯、IoT 設(shè)備心跳上報這類功能時第一反應(yīng)就是上 GatewayWorker——因?yàn)樗臋n齊、生態(tài)熟、配合 ThinkPHP 或 Laravel 做業(yè)務(wù)層非常順手。但真正走到部署階段90% 的人會卡在同一個地方本地ws://跑得飛起一上生產(chǎn)環(huán)境切到wss://就連不上控制臺報錯net::ERR_CONNECTION_CLOSED或WebSocket connection to wss://... failedGateway 日志里卻安靜得像沒這回事。這個問題不是偶發(fā)而是有明確技術(shù)動因的WSS 不是簡單給 WebSocket 加個 SSL它是建立在 TLS 協(xié)議之上的完整加密通道要求從客戶端發(fā)起請求到反向代理如 Nginx再到 GatewayWorker 的 Worker 進(jìn)程每一環(huán)都必須正確識別、透傳、終止或協(xié)商 TLS。而 GatewayWorker 本身不內(nèi)置 HTTPS 服務(wù)能力——它的 Worker 進(jìn)程只監(jiān)聽純 TCP 端口比如 2121所有 TLS 解密工作必須由前置組件完成。這就引出了最典型的配置失配Nginx 配置了 SSL但沒正確轉(zhuǎn)發(fā) WebSocket 升級頭或者 Nginx 轉(zhuǎn)發(fā)了但 GatewayWorker 的gateway.php配置里ssl相關(guān)參數(shù)全為 false又或者證書路徑寫錯、權(quán)限不足、Swoole 版本不支持 TLS 1.3……每一個看似微小的疏漏都會讓整個 WSS 鏈路在握手階段就靜默失敗。我最近在一個在線教育平臺的直播互動模塊中復(fù)現(xiàn)了全部典型問題。這個項(xiàng)目需要支撐 5000 并發(fā)學(xué)生端實(shí)時接收答題倒計(jì)時、教師端廣播白板操作、后臺監(jiān)控連接健康度——對連接穩(wěn)定性、首包延遲、斷線重連成功率要求極高。我們前期在測試環(huán)境用ws://跑通了全部邏輯但正式灰度前切換 WSS 時花了整整三天才定位到根源Nginx 的proxy_set_header Upgrade $http_upgrade;這一行被注釋掉了而開發(fā)同學(xué)誤以為“SSL 配好了WebSocket 就自動好了”。這種認(rèn)知偏差在中小團(tuán)隊(duì)中極其普遍。所以這篇紀(jì)實(shí)不講原理推導(dǎo)也不堆砌 RFC 文檔只聚焦真實(shí)生產(chǎn)環(huán)境里你馬上會遇到的配置項(xiàng)、報錯日志、驗(yàn)證命令和繞過陷阱的實(shí)操路徑。無論你是剛接觸 GatewayWorker 的 PHP 開發(fā)者還是負(fù)責(zé)部署的運(yùn)維同學(xué)只要你的項(xiàng)目需要對外提供wss://接口這篇內(nèi)容就能幫你省下至少 8 小時排查時間。2. 整體架構(gòu)與配置思路三層解耦是 WSS 可靠性的底層前提要理解 GatewayWorker 的 WSS 配置為什么容易出錯必須先厘清它的標(biāo)準(zhǔn)生產(chǎn)部署結(jié)構(gòu)。這不是一個單體服務(wù)而是一個典型的“前端卸載 后端專注”的三層協(xié)作模型最外層反向代理Nginx / Apache / Caddy它是用戶直接訪問的入口承擔(dān) SSL 證書加載、HTTP/HTTPS 請求終止、WebSocket 協(xié)議升級協(xié)商、負(fù)載均衡、靜態(tài)資源托管等職責(zé)。它不處理任何業(yè)務(wù)邏輯只做“流量翻譯官”。中間層Gateway 進(jìn)程它運(yùn)行在獨(dú)立端口如 8282負(fù)責(zé)維持與所有客戶端的長連接做心跳檢測、連接數(shù)統(tǒng)計(jì)、IP 黑白名單、消息廣播等網(wǎng)關(guān)級功能。它通過register進(jìn)程與 BusinessWorker 通信但自身不解析業(yè)務(wù)數(shù)據(jù)。最內(nèi)層BusinessWorker 進(jìn)程它監(jiān)聽另一個端口如 2121只接收來自 Gateway 的已認(rèn)證、已解包的業(yè)務(wù)消息例如{type:submit_answer,data:{...}}執(zhí)行數(shù)據(jù)庫操作、調(diào)用第三方 API、觸發(fā)事件總線等。它完全不知道 TLS 是什么只認(rèn) TCP 流。WSS 的核心難點(diǎn)就在于這三層之間存在兩個關(guān)鍵“協(xié)議轉(zhuǎn)換點(diǎn)”客戶端 ? Nginx這是標(biāo)準(zhǔn)的 HTTPS WebSocket Upgrade 流程??蛻舳税l(fā)送GET /websocket HTTP/1.1Upgrade: websocketSec-WebSocket-Key頭Nginx 必須原樣透傳這些頭并返回101 Switching Protocols同時將后續(xù)二進(jìn)制幀流持續(xù)轉(zhuǎn)發(fā)給后端。Nginx ? GatewayWorker這里必須是明文 TCP 連接。Nginx 解密完 TLS 后把原始的 HTTP Upgrade 請求不含 TLS 層以明文形式發(fā)給 Gateway 進(jìn)程監(jiān)聽的端口。Gateway 進(jìn)程收到的是干凈的 WebSocket 握手請求它再按標(biāo)準(zhǔn)流程響應(yīng)101并升級為 WebSocket 連接。提示很多人誤以為 GatewayWorker 也要開 TLS 端口于是去改gateway.php里的ssl true并填證書路徑。這是根本性錯誤。GatewayWorker 的 SSL 配置僅在一種場景下有效當(dāng)你跳過 Nginx讓客戶端直連 Gateway 進(jìn)程即wss://your-domain.com:8282。但生產(chǎn)環(huán)境絕不能這么做——沒有反向代理你就失去了證書自動續(xù)期、DDoS 緩沖、HTTP/2 支持、日志審計(jì)等關(guān)鍵能力。所以ssl true在標(biāo)準(zhǔn)架構(gòu)中應(yīng)永遠(yuǎn)為false。那么正確的配置責(zé)任劃分就非常清晰了Nginx 全權(quán)負(fù)責(zé) TLS 終止、證書管理、Upgrade 頭透傳GatewayWorker 專注 WebSocket 協(xié)議處理只與 Nginx 建立明文連接BusinessWorker 完全無感照常收發(fā) JSON 消息這種解耦帶來的最大好處是可維護(hù)性換證書只需改 Nginx 配置調(diào)優(yōu) WebSocket 參數(shù)只需改 Gateway 配置升級業(yè)務(wù)邏輯只需重啟 BusinessWorker——三者互不影響。而所有“踩坑”幾乎都源于打破了這個邊界比如在 Nginx 里忘了加Upgrade頭導(dǎo)致 Gateway 收到的是普通 HTTP 請求而非 WebSocket 握手或者在 Gateway 配置里誤啟了 SSL結(jié)果它試圖用明文端口去讀取證書文件啟動直接報錯。3. 核心配置項(xiàng)逐項(xiàng)拆解Nginx 與 GatewayWorker 的 7 個關(guān)鍵開關(guān)WSS 配置的成敗取決于 7 個具體配置項(xiàng)是否全部正確。少一個連接就建立不了錯一個日志里就找不到線索。下面我按實(shí)際部署順序把每個開關(guān)的作用、常見錯誤、驗(yàn)證方法、修復(fù)方案說透。3.1 Nginx Server 塊SSL 證書與基礎(chǔ) HTTPS 啟用這是整個 WSS 的起點(diǎn)。Nginx 必須能正常響應(yīng)https://your-domain.com的普通 HTTPS 請求否則 WebSocket 根本沒機(jī)會發(fā)起。server { listen 443 ssl http2; server_name your-domain.com; # 證書路徑必須絕對準(zhǔn)確且 Nginx 用戶通常是 www-data 或 nginx有讀取權(quán)限 ssl_certificate /etc/nginx/ssl/your-domain.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/your-domain.com/privkey.pem; # 強(qiáng)烈建議啟用 OCSP Stapling提升 TLS 握手速度 ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/nginx/ssl/your-domain.com/chain.pem; # TLS 版本與加密套件兼顧安全與兼容性 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # 其他常規(guī)配置... root /var/www/html; index index.php; }常見錯誤與排查證書路徑錯誤ls -l /etc/nginx/ssl/your-domain.com/看文件是否存在sudo -u www-data cat /etc/nginx/ssl/your-domain.com/fullchain.pem | head -n 1測試 Nginx 用戶能否讀取。如果報Permission denied執(zhí)行sudo chmod 644 /etc/nginx/ssl/your-domain.com/*.pem。證書鏈不完整用openssl s_client -connect your-domain.com:443 -servername your-domain.com 2/dev/null | openssl x509 -noout -text | grep CA Issuers檢查是否包含中間證書。若缺失fullchain.pem應(yīng)為cert.pemchain.pem拼接。Nginx 未重載改完配置必須sudo nginx -t sudo systemctl reload nginx只reload不restart避免連接中斷。注意不要在ssl_certificate路徑里用相對路徑或~符號Nginx 啟動時工作目錄不固定必須用絕對路徑。3.2 Nginx Location 塊WebSocket 升級頭的精準(zhǔn)透傳這是 WSS 失敗率最高的環(huán)節(jié)。Nginx 默認(rèn)不會透傳Upgrade和Connection這兩個關(guān)鍵頭必須顯式聲明。location /websocket { # 關(guān)鍵必須開啟代理緩沖否則大消息會截?cái)?proxy_buffering off; # 關(guān)鍵透傳 WebSocket 升級所需的兩個頭 proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; # 關(guān)鍵告訴后端這是 WebSocket 請求不是普通 HTTP proxy_http_version 1.1; # 后端 Gateway 進(jìn)程地址必須是明文 HTTP非 HTTPS proxy_pass http://127.0.0.1:8282; # 超時設(shè)置WebSocket 是長連接不能沿用默認(rèn)的 60s proxy_read_timeout 86400; proxy_send_timeout 86400; # 其他可選優(yōu)化 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }為什么這三行缺一不可proxy_http_version 1.1WebSocket Upgrade 協(xié)議要求 HTTP/1.1HTTP/1.0 不支持。proxy_set_header Upgrade $http_upgrade$http_upgrade是 Nginx 內(nèi)置變量值為客戶端發(fā)來的Upgrade頭內(nèi)容通常是websocket。如果寫死成websocket當(dāng)客戶端不帶此頭時會出錯如果省略后端收不到升級指令。proxy_set_header Connection upgradeConnection: upgrade是 HTTP/1.1 協(xié)議規(guī)定的升級觸發(fā)頭必須原樣傳遞。常見錯誤與排查忘記proxy_buffering offGatewayWorker 發(fā)送大消息如初始同步數(shù)據(jù)時Nginx 緩沖區(qū)滿會導(dǎo)致連接被靜默關(guān)閉?,F(xiàn)象是客戶端能連上但收不到第一條消息。tail -f /var/log/nginx/error.log會看到upstream sent too big header類似錯誤。proxy_pass寫成https://proxy_pass https://127.0.0.1:8282是致命錯誤。Gateway 進(jìn)程監(jiān)聽的是明文端口Nginx 會嘗試用 HTTPS 去連它必然失敗。必須是http://。proxy_read_timeout過短設(shè)為 60 秒是常見錯誤。WebSocket 連接空閑時Nginx 會在 60 秒后主動斷開導(dǎo)致客戶端頻繁重連。設(shè)為8640024 小時是穩(wěn)妥選擇實(shí)際超時由 Gateway 的心跳機(jī)制控制。3.3 GatewayWorker 配置gateway.php中的 4 個核心參數(shù)GatewayWorker 的配置文件Applications/YourApp/Config/gateway.php是第二道防線。這里不需要開 SSL但必須確保與 Nginx 的通信方式完全匹配。return [ gateway [ class \GatewayWorker\Gateway, transport tcp, // 必須是 tcp不是 ssl ip 127.0.0.1, port 8282, count 4, lan_ip 127.0.0.1, start_port 2900, ping_interval 30, ping_not_response_limit 0, ping_data {type:ping}, // 關(guān)鍵必須為 false表示不啟用內(nèi)置 SSL ssl false, // 關(guān)鍵必須與 Nginx 的 proxy_pass 地址一致 registerAddress 127.0.0.1:1238, ], businessWorker [ class \GatewayWorker\BusinessWorker, count 4, // 關(guān)鍵監(jiān)聽端口必須與 gateway.php 中 registerAddress 的端口一致 registerAddress 127.0.0.1:1238, ], register [ class \GatewayWorker\Register, ip 127.0.0.1, port 1238, ] ];關(guān)鍵參數(shù)詳解transport tcp明確指定傳輸層為 TCP。雖然默認(rèn)就是 TCP但顯式寫出可避免歧義。ssl true時 transport 才是ssl。ssl false再次強(qiáng)調(diào)這是標(biāo)準(zhǔn)架構(gòu)下的鐵律。設(shè)為true會導(dǎo)致 Gateway 啟動時報failed to open stream: No such file or directory找不到證書或SSL operation failed with code 1證書格式錯誤。registerAddressGateway 和 BusinessWorker 都必須指向同一個 Register 進(jìn)程地址。這是三進(jìn)程通信的“電話總機(jī)”地址不一致會導(dǎo)致業(yè)務(wù)消息無法送達(dá)。ping_interval心跳間隔。設(shè)為 30 秒是經(jīng)驗(yàn)值太短增加無效流量太長如 120 秒可能導(dǎo)致 Nginxproxy_read_timeout先于心跳觸發(fā)斷連。驗(yàn)證方法啟動 GatewayWorker 后執(zhí)行netstat -tuln | grep :8282確認(rèn)有tcp6 0 0 *:8282 *:* LISTEN。再執(zhí)行curl -i -N -H Connection: upgrade -H Upgrade: websocket http://127.0.0.1:8282/websocket如果返回HTTP/1.1 101 Switching Protocols說明 Gateway 本身 WebSocket 握手正常問題一定出在 Nginx 層。3.4 Swoole 版本與 OpenSSL 支持被忽視的底層依賴GatewayWorker 依賴 Swoole 擴(kuò)展而 Swoole 的 WebSocket 服務(wù)器能力與 OpenSSL 版本強(qiáng)相關(guān)。很多“配置全對卻連不上”的案例根源在此。Swoole 4.5.0開始支持 TLS 1.3推薦使用 4.8.x 或 5.x 系列。OpenSSL 版本必須 ≥ 1.1.1。Ubuntu 18.04 自帶 OpenSSL 1.1.1但 CentOS 7 默認(rèn)是 1.0.2k不支持 TLS 1.3會導(dǎo)致 Nginx 與 Gateway 通信異常。驗(yàn)證命令# 查看 Swoole 版本與編譯參數(shù) php --ri swoole | grep -E (Version|OpenSSL) # 查看系統(tǒng) OpenSSL 版本 openssl version # 檢查 Swoole 是否鏈接了正確的 OpenSSL ldd $(php-config --extension-dir)/swoole.so | grep ssl輸出示例正確swoole support enabled Version 5.0.3 OpenSSL OpenSSL 1.1.1w 11 Sep 2023 ...錯誤場景與修復(fù)如果php --ri swoole顯示OpenSSL Disabled說明編譯 Swoole 時沒找到 OpenSSL 開發(fā)庫。Ubuntu 上執(zhí)行sudo apt install libssl-devCentOS 上執(zhí)行sudo yum install openssl-devel然后重新編譯安裝 Swoole。如果OpenSSL版本是1.0.2k必須升級系統(tǒng) OpenSSL 或更換發(fā)行版。強(qiáng)行用舊版 OpenSSL 會導(dǎo)致 TLS 握手失敗Nginx 錯誤日志出現(xiàn)upstream prematurely closed connection while reading response header from upstream。3.5 客戶端連接 URL協(xié)議、域名、端口的三位一體校驗(yàn)客戶端代碼里的一行new WebSocket(wss://...)是整個鏈條的發(fā)起點(diǎn)也是最容易寫錯的地方。// ? 正確協(xié)議 wss域名與 Nginx server_name 一致路徑與 location 匹配 const ws new WebSocket(wss://your-domain.com/websocket); // ? 錯誤1協(xié)議寫成 ws本地調(diào)試可用生產(chǎn)必須 wss const ws new WebSocket(ws://your-domain.com/websocket); // ? 錯誤2域名不匹配如用了 IP 或子域名但 Nginx 沒配 server_name const ws new WebSocket(wss://192.168.1.100/websocket); // ? 錯誤3路徑不匹配Nginx location 是 /ws客戶端寫了 /websocket const ws new WebSocket(wss://your-domain.com/ws);驗(yàn)證方法在瀏覽器開發(fā)者工具 Network 標(biāo)簽頁過濾WS點(diǎn)擊連接看 Initiator 列是否顯示wss://your-domain.com/websocket。如果顯示ws://檢查前端構(gòu)建配置如 Vue CLI 的devServer.proxy或 Webpack 的resolve.alias是否在生產(chǎn)環(huán)境注入了錯誤的協(xié)議。3.6 防火墻與安全組云服務(wù)器的隱形攔路虎本地curl能通但線上連不上90% 是防火墻擋的。云服務(wù)器阿里云、騰訊云、AWS的安全組默認(rèn)只放行 80/443而 GatewayWorker 的 8282 端口是內(nèi)部通信端口絕對不能開放給公網(wǎng)。但很多人誤操作把 8282 加到了安全組白名單結(jié)果 Nginx 無法訪問本地 8282因?yàn)榱髁勘话踩M規(guī)則攔截了。正確做法Nginx 與 GatewayWorker 在同一臺機(jī)器無需開放 8282 端口127.0.0.1訪問走本地回環(huán)不受安全組影響。Nginx 與 GatewayWorker 在不同機(jī)器如 Nginx 在 LBGateway 在應(yīng)用服務(wù)器必須在應(yīng)用服務(wù)器的安全組中僅允許 Nginx 服務(wù)器的內(nèi)網(wǎng) IP 訪問 8282 端口禁止0.0.0.0/0。驗(yàn)證命令在 Nginx 服務(wù)器上執(zhí)行# 測試能否 telnet 通 Gateway 服務(wù)器的 8282 端口 telnet gateway-server-ip 8282 # 如果不通檢查 Gateway 服務(wù)器防火墻 sudo ufw status verbose # Ubuntu sudo firewall-cmd --list-all # CentOS 8 # 確保有類似 allow 8282/tcp from nginx-ip 的規(guī)則3.7 日志分級與關(guān)聯(lián)快速定位故障環(huán)節(jié)的黃金法則WSS 故障排查本質(zhì)是日志串聯(lián)。必須養(yǎng)成習(xí)慣一次連接失敗同時查看三處日志。日志位置查看命令關(guān)鍵線索Nginx error.logsudo tail -f /var/log/nginx/error.logupstream timed out超時、upstream refused端口拒絕、no live upstreams后端宕機(jī)GatewayWorker logstail -f Applications/YourApp/Logs/gateway-worker.logGateway started啟動成功、onConnect連接建立、onClose連接關(guān)閉、SSL handshake failed如果誤啟了 SSL瀏覽器 ConsoleF12 → ConsoleWebSocket connection to wss://... failed客戶端層面、Failed to construct WebSocket: An insecure WebSocket connection may not be initiated from a page loaded over HTTPS協(xié)議混用實(shí)戰(zhàn)技巧在 GatewayWorker 的Events.php中加入詳細(xì)日志public static function onConnect($client_id) { \GatewayWorker\Lib\Gateway::log(Client {$client_id} connected from {$_SERVER[REMOTE_ADDR]}); // 記錄 User-Agent判斷是否是爬蟲或異??蛻舳?\GatewayWorker\Lib\Gateway::log(User-Agent: {$_SERVER[HTTP_USER_AGENT]}); }在 Nginx 配置中添加自定義日志格式記錄 WebSocket 升級狀態(tài)log_format websocket $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_upgrade $http_connection; access_log /var/log/nginx/websocket.log websocket;4. 實(shí)操過程全記錄從零部署一個可驗(yàn)證的 WSS 服務(wù)現(xiàn)在我們把前面所有知識點(diǎn)串起來走一遍完整的、可立即復(fù)現(xiàn)的部署流程。以下步驟基于 Ubuntu 22.04 PHP 8.1 Swoole 5.0.3 Nginx 1.18 環(huán)境所有命令均可復(fù)制粘貼執(zhí)行。4.1 環(huán)境準(zhǔn)備安裝基礎(chǔ)依賴與 Swoole# 更新系統(tǒng) sudo apt update sudo apt upgrade -y # 安裝 PHP 及常用擴(kuò)展 sudo apt install -y php8.1-cli php8.1-mbstring php8.1-curl php8.1-xml php8.1-zip # 安裝 OpenSSL 開發(fā)庫編譯 Swoole 必需 sudo apt install -y libssl-dev # 安裝 Composer curl -sS https://getcomposer.org/installer | sudo php -- --install-dir/usr/local/bin --filenamecomposer # 安裝 Swoole推薦 PECL自動處理依賴 sudo pecl install swoole # 安裝過程中如果提示 Enable OpenSSL support? [no] :輸入 yes 并回車 # 啟用 Swoole 擴(kuò)展 echo extensionswoole.so | sudo tee /etc/php/8.1/cli/conf.d/20-swoole.ini echo extensionswoole.so | sudo tee /etc/php/8.1/fpm/conf.d/20-swoole.ini # 重啟 PHP-FPM sudo systemctl restart php8.1-fpm驗(yàn)證 Swoolephp --ri swoole | grep -E (Version|OpenSSL) # 輸出應(yīng)包含 Version 5.0.3 和 OpenSSL OpenSSL 1.1.1w4.2 下載并初始化 GatewayWorker 項(xiàng)目# 創(chuàng)建項(xiàng)目目錄 mkdir -p /var/www/gateway-demo cd /var/www/gateway-demo # 使用 Composer 創(chuàng)建 GatewayWorker 項(xiàng)目官方推薦方式 composer create-project workerman/gatewayworker:4.1.0 . # 初始化應(yīng)用結(jié)構(gòu) php start.php start -d # 此時應(yīng)看到 Gateway start success但這是臨時啟動我們馬上要配置 # 停止臨時服務(wù) php start.php stop4.3 配置 Nginx生成可直接使用的 server 塊創(chuàng)建/etc/nginx/sites-available/gateway-demoupstream gateway_backend { server 127.0.0.1:8282; } server { listen 80; server_name gateway-demo.local; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name gateway-demo.local; # 為本地測試生成自簽名證書生產(chǎn)環(huán)境請用 Lets Encrypt ssl_certificate /etc/nginx/ssl/gateway-demo.local.crt; ssl_certificate_key /etc/nginx/ssl/gateway-demo.local.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; # WebSocket 專用 location location /websocket { proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_pass http://gateway_backend; proxy_read_timeout 86400; proxy_send_timeout 86400; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 其他靜態(tài)資源 root /var/www/gateway-demo/Web; index index.html; }生成自簽名證書僅用于本地測試sudo mkdir -p /etc/nginx/ssl sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout /etc/nginx/ssl/gateway-demo.local.key \ -out /etc/nginx/ssl/gateway-demo.local.crt \ -subj /CCN/STBeijing/LBeijing/ODemo/CNgateway-demo.local啟用站點(diǎn)sudo ln -sf /etc/nginx/sites-available/gateway-demo /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx4.4 配置 GatewayWorker修改gateway.php與start.php編輯/var/www/gateway-demo/Applications/GatewayDemo/Config/gateway.phpreturn [ gateway [ class \GatewayWorker\Gateway, transport tcp, ip 127.0.0.1, port 8282, count 2, lan_ip 127.0.0.1, start_port 2900, ping_interval 30, ping_not_response_limit 0, ping_data {type:ping}, // 關(guān)鍵必須為 false ssl false, registerAddress 127.0.0.1:1238, ], businessWorker [ class \GatewayWorker\BusinessWorker, count 2, registerAddress 127.0.0.1:1238, ], register [ class \GatewayWorker\Register, ip 127.0.0.1, port 1238, ] ];編輯/var/www/gateway-demo/start.php確保use GatewayWorker\Autoloader;在頂部并在start()函數(shù)前添加// 設(shè)置時區(qū)避免日志時間錯亂 date_default_timezone_set(Asia/Shanghai);4.5 啟動服務(wù)并驗(yàn)證連接# 啟動 GatewayWorker守護(hù)進(jìn)程模式 cd /var/www/gateway-demo php start.php start -d # 查看進(jìn)程是否運(yùn)行 ps aux | grep GatewayWorker # 查看 Gateway 日志 tail -f /var/www/gateway-demo/Applications/GatewayDemo/Logs/gateway-worker.log # 應(yīng)看到 Gateway started 和 Register started # 測試 Nginx 到 Gateway 的連通性在服務(wù)器本地執(zhí)行 curl -i -N -H Connection: upgrade -H Upgrade: websocket http://127.0.0.1:8282/websocket # 應(yīng)返回 HTTP/1.1 101 Switching Protocols # 測試 WSS 連接在另一臺機(jī)器或?yàn)g覽器中 # 訪問 https://gateway-demo.local打開開發(fā)者工具 Console執(zhí)行 # const ws new WebSocket(wss://gateway-demo.local/websocket); # ws.onopen () console.log(Connected!); # ws.onerror (e) console.error(Error:, e);如果瀏覽器報錯ERR_CONNECTION_REFUSED檢查sudo netstat -tuln | grep :8282確認(rèn) Gateway 進(jìn)程在監(jiān)聽。檢查sudo nginx -t確認(rèn) Nginx 配置語法正確。檢查/var/log/nginx/error.log看是否有connect() failed (111: Connection refused)說明 Nginx 連不上 8282。如果瀏覽器報錯ERR_SSL_PROTOCOL_ERROR檢查curl -I https://gateway-demo.local確認(rèn) HTTPS 基礎(chǔ)服務(wù)正常。檢查證書路徑是否正確ls -l /etc/nginx/ssl/看文件是否存在。4.6 前端驗(yàn)證頁面一個最小可運(yùn)行的 HTML在/var/www/gateway-demo/Web/index.html中寫入!DOCTYPE html html head titleGatewayWorker WSS Test/title /head body h1GatewayWorker WSS 連接測試/h1 div idstatus等待連接.../div button onclickconnect()連接/button button onclicksend() disabled idsendBtn發(fā)送消息/button divtextarea idmsg rows2 cols50Hello from browser!/textarea/div script let ws; function connect() { const url wss://gateway-demo.local/websocket; ws new WebSocket(url); ws.onopen function() { document.getElementById(status).innerText ? 已連接; document.getElementById(sendBtn).disabled false; }; ws.onmessage function(event) { console.log(收到:, event.data); document.getElementById(status).innerText 收到: event.data; }; ws.onclose function() { document.getElementById(status).innerText ? 連接已關(guān)閉; document.getElementById(sendBtn).disabled true; }; ws.onerror function(error) { console.error(WebSocket 錯誤:, error); document.getElementById(status).innerText ? 連接錯誤: error; }; } function send() { const msg document.getElementById(msg).value; if (ws ws.readyState WebSocket.OPEN) { ws.send(msg); document.getElementById(status).innerText 已發(fā)送: msg; } } /script /body /html訪問https://gateway-demo.local點(diǎn)擊“連接”即可看到實(shí)時連接狀態(tài)和消息收發(fā)。5. 常見問題與排查技巧實(shí)錄那些讓你抓狂的“幽靈錯誤”在多個項(xiàng)目的 WSS 部署中我整理出一份高頻問題速查表。這些問題往往沒有明確報錯或者報錯信息極具誤導(dǎo)性必須結(jié)合上下文才能定位。5.1 問題速查表癥狀、原因、驗(yàn)證命令、修復(fù)方案癥狀可能原因驗(yàn)證命令修復(fù)方案瀏覽器控制臺報net::ERR_CONNECTION_CLOSEDNginx error.log 為空Nginx 的proxy_pass指向了錯誤的端口如 2121 而非 8282或 Gateway 進(jìn)程未啟動sudo netstat -tuln | grep :8282curl -i http://127.0.0.1:8282/websocket確認(rèn)gateway.php中port與 Nginxproxy_pass端口一致執(zhí)行php start.php start -d啟動 Gateway連接能建立但 30 秒后自動斷開onClose日志頻繁出現(xiàn)Nginxproxy_read_timeout過短或 Gatewayping_interval與 Nginx timeout 不匹配grep proxy_read_timeout /etc/nginx/sites-available/*grep ping_interval /var/www/gateway-demo/Applications/*/Config/gateway.php將 Nginxproxy_read_timeout設(shè)為86400Gatewayping_interval設(shè)為30確保 Nginx timeout 3 × ping_intervalNginx error.log 報upstream prematurely closed connectionGatewayWorker 進(jìn)程崩潰退出或 Swoole 版本與 OpenSSL 不兼容sudo systemctl status php8.1-fpmphp --ri swoole | grep OpenSSL升級 Swoole 至 5.x確保 OpenSSL ≥ 1.1.1檢查 Gateway 日志是否有 Segmentation fault