Протокол WebSocket, как и любые другие протоколы имеет свои преимущества и свои недостатоки. Именно из за последних появляются новые версии протоколов, новые протоколы и новые подходы к реализации всего что вокруг них, а конкретно - клиентских и серверных приложений.
Хороший сервер - это такой сервер который учитывает особенности протокола. Хороший клиент - тоже самое. Здесь под "сервер" и "клиент" подразумевается именно имплементация протокола в виде конкретного софта или библиотеки.
Изучив WebSocket с разных сторон образовался следующий список проблем которые в WebSocket есть, а в других популярных и используемых местах их не имеется либо не несут важных или критичных проблем. Но давайте сперва изучим другую сторону WebSocket, ту которая лежит в его основе: TCP-keepalive.
TCP KeepAlive
Что такое KeepAlive? Это способ оставлять TCP-соединение открытым долгое время.
Как это делается? Раз в определенный период происходит обмен специальными пакетами которые озаглавлены в документации "keepalive probes". Выполняется это с помощью PSH и RST пакетов.
Как долго может жить KeepAlive соединение? Имеется максимальный временной интервам между пакетами с данными (не путать с keepalive probes) который соединение может продолжать жить. Если обмен данными происходит в этот период - то следующий период начинается сначала. Т.е. KeepAlive соединение периодически (пусть и редко) обменивающееся внутри себя данными может жить двольно долго.
В ядре Linux вокруг этого есть три настройки:
root@net-stage:~# sysctl -a | grep tcp_keepalive_ net.ipv4.tcp_keepalive_intvl = 75 net.ipv4.tcp_keepalive_probes = 9 net.ipv4.tcp_keepalive_time = 7200
Здесь показаны их значения по умолчанию. net.ipv4.tcp_keepalive_time - это как раз максимальное время между пакетами с данными.
net.ipv4.tcp_keepalive_intvl - интервал обмена пакетами "keepalive probes" по умолчанию 75 секунд. "net.ipv4.tcp_keepalive_probes" - это количество возможных неотвеченных "keepalive probes" пакетов - по сути попыток возобновить соединение.
Как переводится соединение в режим KeepAlive? на языке C очень просто:
optval = 1; optlen = sizeof(optval); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);
Так же можно использовать для своих сокетов свои величины intvl и probes:
int keepcnt = 5; int keepintvl = 120; setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(int)); //эквивалентно tcp_keepalive_probes setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(int)); //эквивалентно tcp_keepalive_intvl
В комментариях к этим опциям в документации отметчено: "This option should not be used in code intended to be portable." Эти опции требуется применять очень аккуратно: это настройки которые ДОЛЖНЫ быть одинаковы и на клиенте, и на сервере.
Если величины например tcp_keepalive_intvl разойдутся - то клиент работающий по умолчанию, и сервер работающий в режиме tcp_keepalive_intvl=25 будут иметь разную информацию о соединении. В этом примерер 75/25 - клиент может долго думать что соединение еще открыто, когда сервер будет уже знать о том что оно закрылось. Это будет приводить к отправке пакетов в уже закрытое на том конце соединение и потере пакетов.
Но тем не менее - если вы контролируете и клиентский код и серверный - эти величины для вас доступны.
Например величина 75 секунд - может быть слишком велика в определенных ситуациях, например, когда данные требуется передавать быстро, и при их недоставке так же быстро требуется получить ответ об ошибке.
WebSocket
Websocket использует TCP-KeepAlive соединения. В конкретном применении это дает как плюсы так и минусы. Плюсы очевидны:
Проблемы WebSocket в соверменном использовании менее очевидны:
Молчаливый отвал соединения. При отправке пакета в WebSocket вы не узнаете о том, доставлен он или нет, пока не пройдет 75 секунд таймаута. Сам протокол - ничего не расскажет об этом, а величина по умолчанию 75 секунд велика для быстрого взаимодействия. Например тот же чат работающий поверх реальной сети где клиенты могут "выпадать из сети" усложняется архитектурно: сервер должен внутри себя иметь очередь, он должен иметь unack буфер и логику переотправки сообщений. Часто по WebSocket разработчики запускают "свои самодельные ping-и" с целью понять жив он или нет.
Смена сети клиентом. В сетях мобильной связи часто бывает так что ваш внешний IP, NAT и вообще сеть в котрой присудствует мобильное устроство меняются. Это даже не зависит от оператора - вы пришли домой, подключился WiFi и весь websocket рухнул очень забавным образом:
Хотите пример такой утечки? Легко:
Повышенная нагрузка на серверы. Клиент ничего не знает о своей смене внешнего IP и сует данные в отпавшое соединение - сервер получает пакеты старого соединения уже с нового IP и отбрасывает их. Если у вас популярное мобильное приложение и как результат нагруженный сервер - то маленикой чих в сети оператора мобильной связи устроит DDoS атаку на ваш сервер - вам прилетят старые пакеты, реконнекты, данные, вместо того чтобы просто передать данные 1 раз. Т.е. вам требуется иметь многократный запас пропускной способности на серверах.
Способы решения проблем WebSocket
На данный момент решения для отработки обрыва соединения костыльно-ориентированные:
Универсальных способов решения проблем утечки информации об отправляемых пакетах при смене мобильным клиентом сети до сих пор нет.
Как сделать так, чтобы Ваш IT-отдел не стал местом утечки информации за которую Вы отвечаете?
Позволяет решать интеллектуальные задачи, помимо задач самого мониторинга
И снова о маленьких сетевых фокусах ради надежности работы web-сервисов
Потому что отсутствует самое важное, что должно быть в любой надежной инженерной системе
Быстро+Дешево+Качественно - так не бывает, в чем подвох?
Имеется такое явление набравшее свою популярность, как letsencrypt/certbot. Он выдает ssl-сертификаты автоматически и всем подряд. В целом это его основная проблема.