Проксіювання WebSocket-з'єднань передбачає налаштування проміжного сервера для коректної обробки рукостискання HTTP Upgrade та підтримки постійного, повнодуплексного TCP-з'єднання, необхідного для зв'язку через WebSocket. Таке налаштування дозволяє використовувати такі функції, як балансування навантаження, термінація SSL, кешування та контроль доступу для додатків реального часу.
Розуміння проксіювання WebSocket
WebSockets встановлюють постійне з'єднання між клієнтом і сервером через одне TCP-з'єднання. Це відрізняється від традиційного HTTP, який зазвичай використовує короткочасні цикли запит/відповідь. Перехід від HTTP до WebSocket ініціюється за допомогою механізму "Upgrade" у протоколі HTTP/1.1.
Клієнт надсилає початковий HTTP GET запит зі специфічними заголовками:
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Key: [base64-encoded nonce]
* Sec-WebSocket-Version: 13
Якщо сервер підтримує WebSockets, він відповідає статусом HTTP 101 Switching Protocols, підтверджуючи оновлення:
* HTTP/1.1 101 Switching Protocols
* Upgrade: websocket
* Connection: Upgrade
* Sec-WebSocket-Accept: [response key derived from client's key]
Після цього рукостискання з'єднання більше не є HTTP, а є сирим TCP-сокетом, через який обмінюються кадрами WebSocket. Проксі-сервер повинен бути налаштований на коректну переадресацію цих заголовків Upgrade та Connection, а потім підтримувати довготривале TCP-з'єднання без буферизації або передчасного закриття.
Налаштування для поширених проксі-серверів
Nginx
Nginx може ефективно проксіювати WebSocket-з'єднання, обробляючи рукостискання HTTP Upgrade. Ключовим моментом є передача заголовків Upgrade та Connection від клієнта до бекенд-сервера та забезпечення використання HTTP/1.1.
http {
upstream websocket_backend {
server backend_server_1:8080;
server backend_server_2:8080;
}
server {
listen 80;
server_name yourdomain.com;
location /ws/ {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host; # Важливо для бекенду, якщо він покладається на заголовок Host
proxy_read_timeout 86400s; # Налаштуйте за потреби для довготривалих з'єднань
proxy_send_timeout 86400s;
proxy_buffering off; # Вимкнути буферизацію для даних реального часу
}
# Інші HTTP-локації
location / {
proxy_pass http://other_http_backend;
# ... інші налаштування HTTP-проксі
}
}
}
proxy_http_version 1.1;: Важливо для механізму заголовкаUpgrade.proxy_set_header Upgrade $http_upgrade;: Передає заголовокUpgradeклієнта.proxy_set_header Connection "upgrade";: Передає заголовокConnectionклієнта. Nginx автоматично обробляє перетворенняConnection: upgradeнаConnection: keep-aliveдля HTTP/1.1 з'єднань, якщо це не встановлено явно на "upgrade". Явне встановлення забезпечує коректну поведінку для WebSockets.proxy_read_timeout/proxy_send_timeout: Збільшіть ці значення порівняно зі стандартними, щоб запобігти закриттю Nginx довготривалих WebSocket-з'єднань через неактивність.proxy_buffering off;: Запобігає буферизації відповідей Nginx, що є критично важливим для зв'язку WebSocket у реальному часі для забезпечення мінімальної затримки.
Apache HTTP Server
Apache може проксіювати WebSocket-з'єднання за допомогою mod_proxy_wstunnel, який є частиною mod_proxy. Переконайтеся, що mod_proxy, mod_proxy_http та mod_proxy_wstunnel увімкнені.
<VirtualHost *:80>
ServerName yourdomain.com
# Увімкнути модулі проксі
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# Проксіювати WebSocket-з'єднання
<Location /ws/>
ProxyPass ws://backend_server:8080/ws/
ProxyPassReverse ws://backend_server:8080/ws/
</Location>
# Проксіювати безпечні WebSocket-з'єднання (WSS)
<Location /wss/>
ProxyPass wss://backend_server:8443/wss/
ProxyPassReverse wss://backend_server:8443/wss/
</Location>
# Інші HTTP-локації
<Location />
ProxyPass http://other_http_backend/
ProxyPassReverse http://other_http_backend/
</Location>
</VirtualHost>
ProxyPass ws://...: Схемаws://вказуєmod_proxy_wstunnelобробляти оновлення WebSocket. Для безпечних WebSockets використовуйтеwss://.ProxyPassReverse: Забезпечує коректне переписування URL-адрес у заголовках відповіді.mod_proxyApache автоматично обробляє заголовкиUpgradeтаConnection, коли вказаноws://абоwss://.
HAProxy
HAProxy підтримує проксіювання WebSocket з версії 1.5. Він може працювати в режимі http для перевірки заголовків або в режимі tcp для сирої переадресації. Для WebSockets зазвичай використовується режим http зі специфічними правилами для виявлення запиту на оновлення.
global
log /dev/log local0 info
maxconn 4000
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5000ms
timeout client 50000ms # Збільшено для клієнтських WebSocket-з'єднань
timeout server 50000ms # Збільшено для серверних WebSocket-з'єднань
frontend http_frontend
bind *:80
acl is_websocket hdr(Upgrade) -i websocket
use_backend ws_backend if is_websocket
default_backend http_backend
backend ws_backend
mode http
# Ця опція є критично важливою для WebSockets в режимі HTTP; вона вказує HAProxy
# переключитися на TCP-тунелювання після HTTP-оновлення.
option http-tunnel
server backend_ws_1 backend_server_1:8080 check
server backend_ws_2 backend_server_2:8080 check
backend http_backend
mode http
server backend_http_1 backend_server_3:80 check
acl is_websocket hdr(Upgrade) -i websocket: Визначає список контролю доступу (ACL), який відповідає запитам, що містять заголовокUpgrade: websocket(без урахування регістру).use_backend ws_backend if is_websocket: Направляє запити на оновлення WebSocket доws_backend.option http-tunnel: Це критично важливо. Коли HAProxy виявляє заголовокUpgradeі встановлена ця опція, він перемикається з режиму аналізу HTTP на сирий TCP-тунель після початкового HTTP-рукостискання. Це дозволяє протоколу WebSocket працювати безперешкодно.timeout client/timeout server: Ці тайм-аути слід значно збільшити для бекендів WebSocket, щоб запобігти закриттю HAProxy довготривалих з'єднань.
Усунення поширених проблем
Розрив з'єднання або тайм-аут
- Тайм-аути проксі: Найпоширеніша причина. Переконайтеся, що
proxy_read_timeout,proxy_send_timeout(Nginx),timeout client,timeout server(HAProxy) або еквівалентні налаштування в інших проксі є достатньо високими (наприклад, 24 години або більше) для WebSocket-з'єднань. - Неактивні з'єднання: Деякі проксі або мережеві пристрої можуть агресивно закривати неактивні TCP-з'єднання. Якщо протокол WebSocket має механізм підтримки активності (наприклад, кадри ping/pong), переконайтеся, що він налаштований як на клієнті, так і на сервері для надсилання періодичних пінгів.
- Неактивність балансувальника навантаження: Якщо балансувальник навантаження знаходиться перед проксі, він також може мати власні тайм-аути неактивності. Перевірте ці налаштування.
HTTP 400 Bad Request (Заголовок Upgrade відсутній або некоректний)
- Запит клієнта: Перевірте, чи клієнт коректно надсилає заголовки
Upgrade: websocketтаConnection: Upgrade. - Конфігурація проксі: Переконайтеся, що проксі налаштований на переадресацію цих заголовків до бекенду.
- Nginx: Перевірте
proxy_set_header Upgrade $http_upgrade;таproxy_set_header Connection "upgrade";. - Apache: Переконайтеся, що
mod_proxy_wstunnelувімкнено, аProxyPassвикористовуєws://абоwss://. - HAProxy: Переконайтеся, що
acl is_websocketкоректно ідентифікує заголовокUpgrade, аuse_backendнаправляє його до бекенду зoption http-tunnel.
- Nginx: Перевірте
HTTP 502 Bad Gateway
- Проблеми з бекенд-сервером: Проксі отримав недійсну відповідь від бекенду.
- Чи працює бекенд-сервер WebSocket і чи прослуховує він очікуваний порт?
- Чи коректно налаштований бекенд для обробки оновлень WebSocket?
- Перевірте логи бекенд-сервера на наявність помилок.
- Мережеве з'єднання: Перевірте, чи може проксі досягти бекенд-сервера за вказаним портом.
- Буферизація проксі: Хоча
proxy_buffering offзазвичай добре підходить для WebSockets, надмірні дані або неправильні конфігурації іноді можуть призводити до 502, якщо проксі має проблеми з обробкою потоку.
Міркування безпеки (Термінація SSL/TLS)
- WSS (Безпечні WebSockets): Для безпечних WebSocket-з'єднань (wss://) проксі повинен обробляти термінацію SSL/TLS.
- Проксі буде прослуховувати порт 443 (або інший безпечний порт), виконувати рукостискання TLS, а потім проксіювати розшифрований трафік WebSocket до бекенду (часто через звичайний
ws://абоhttp://внутрішньо). - Приклад Nginx для WSS:
```nginx
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.crt;
ssl_certificate_key /etc/nginx/ssl/yourdomain.key;location /ws/ { proxy_pass http://websocket_backend; # Бекенд може бути звичайним HTTP proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_read_timeout 86400s; proxy_send_timeout 86400s; proxy_buffering off; }}
`` * **Заголовки Origin:** Клієнти WebSocket надсилають заголовокOrigin`. Бекенд-сервери часто перевіряють цей заголовок з міркувань безпеки. Переконайтеся, що ваш проксі не видаляє та не змінює цей заголовок, якщо бекенд на нього покладається.
- Проксі буде прослуховувати порт 443 (або інший безпечний порт), виконувати рукостискання TLS, а потім проксіювати розшифрований трафік WebSocket до бекенду (часто через звичайний
Обмеження буфера проксі
- Nginx:
proxy_buffering off;є критично важливим. Якщо не встановлено, Nginx може буферизувати великі повідомлення WebSocket, що призведе до затримки або тайм-аутів. - HAProxy:
option http-tunnelобробляє це, перемикаючись на сирий TCP.
Порівняння проксі-серверів для WebSockets
| Функція | Nginx | Apache HTTP Server (mod_proxy_wstunnel) | HAProxy |
|---|---|---|---|
| Конфігурація | Ручна переадресація заголовків | Специфічні директиви ws:// / wss:// |
ACL та option http-tunnel |
| Складність | Помірна | Помірна | Помірна до високої (для розширених функцій) |
| Продуктивність | Висока (керована подіями) | Помірна (на основі процесів/потоків) | Висока (керована подіями) |
| Термінація SSL/TLS | Відмінна | Добра | Відмінна |
| Балансування навантаження | Round-robin, IP hash, least_conn | Round-robin | Широке (багато алгоритмів, перевірки стану) |
| Sticky Sessions | IP hash (для базових), сторонні модулі | Обмежені | На основі файлів cookie, IP джерела |
| Контроль буферизації | proxy_buffering off |
Обробляється mod_proxy_wstunnel |
option http-tunnel вимикає буферизацію HTTP |
Ключові міркування
- Sticky Sessions: Для деяких WebSocket-додатків клієнт може потребувати залишатися підключеним до того ж бекенд-сервера протягом усієї сесії (наприклад, якщо підтримується стан на стороні сервера).
- Nginx: Використовуйте
ip_hashу блоціupstream. - HAProxy: Використовуйте директиви
balance sourceабоcookie. - Переконайтеся, що ваш бекенд-додаток розроблений для горизонтального масштабування без необхідності sticky sessions, якщо це можливо, оскільки це спрощує балансування навантаження.
- Nginx: Використовуйте
- Перевірки стану: Налаштуйте надійні перевірки стану для ваших бекенд-серверів WebSocket у межах проксі. Це гарантує, що лише здорові сервери отримують запити на оновлення WebSocket.
- Логування: Налаштуйте комплексне логування на проксі та бекенді. Проблеми з WebSocket-з'єднанням можуть бути тимчасовими, і детальні логи є важливими для діагностики.
- HTTP/2 та WebSockets: Хоча WebSockets зазвичай використовують HTTP/1.1 для рукостискання оновлення, вони можуть співіснувати з HTTP/2. Однак сам протокол WebSocket не працює безпосередньо через кадри HTTP/2. Проксі, що обробляють HTTP/2 від клієнтів, зазвичай знижують версію до HTTP/1.1 для оновлення WebSocket до бекенду.