LINUX.ORG.RU
ФорумAdmin

Параметры ядра для сокетов

 , , ,


0

3

Есть приложение на Java, которое собирает условные логи. Приложение слушает TCP порт. К нему подключаются клиенты (совсем немного, от 3 до 5) и после подключения больше не отключаются (логи же). Все клиенты находятся в одном сетевом сегменте (свич, vlan, подсеть - всё одинаковое). Сервер клиенту ничего не пишет в ответ (и не должен), он просто читает с него TCP поток, разбивает на отдельные сообщения и скидывает в очередь для обработки (0 логики и задержек при чтении сообщений).

Проблема:

Спустя какое-то непродолжительное время, примерно 10 мин. с одного (и только одного) из клиентов начинают идти потери. Предположительно, это происходит во время кратковременных всплесков нагрузки. В дампе это видно как множественные Dup ACK и «previous segment not captured». Ну то есть, TCP нам говорит забейте на проверку ошибок на стороне приложения, я ГАРАНТИРУЮ доставку и … не гарантирует.

UDP использовать нельзя, он не поддерживается клиентами. С той стороны промышленная железка для которой нагрузка, при которой на сервере происходит потеря, это 1% от её возможностей. Сервер тоже даже близко не перегружен, иначе это должно было бы сказываться на других клиентах.

Отличие проблемного клиента от не проблемных только в том, что с него payload пожирнее (там XML на ~1Kb внутри, а в других нет) и пакеты бегут почаще. Соответственно, подозрение падает на TCP стек линукса (Centos 7) и параметры сокета, в частности.

Выкручивал параметры net.core.rmem_max и net.core.wmem_max до 1MB, что достаточно много. Контролировал, что и ОС и приложение увидели эти настройки. Не помогло.

Подскажите, пожалуйста, где ещё можно покрутить и с чем ещё могут быть связаны такие потери TCP фреймов.



Последнее исправление: emcode (всего исправлений: 2)

Ну то есть, TCP нам говорит забейте на проверку ошибок на стороне приложения, я ГАРАНТИРУЮ доставку и … не гарантирует.

  1. Никто не говорит «забейте на проверку ошибок», при работе с TCP-сокетом могут произойти ошибки и их нужно обрабатывать.

  2. TCP гарантирует доставку в том смысле, что в случае потерь пакеты отправляются заново, и всё, что было записано в сокет на одной стороне, придёт на другую без пропусков и со строгим соблюдением порядка. Но может произойти обрыв или «подвисание» соединения, с потерей всех данных начиная с определённого момента. Такое можно и нужно обрабатывать.

annulen ★★★★★
()
Ответ на: комментарий от annulen

Обрыв соединения понятно, что нужно обрабатывать переподключение. Не уверен, что такое «подвисание». Но проблема именно в том, что теряется, например, 100 байт и одного сообщения, потом 2000 байт нормально, потом еще 100 байт теряется и т.д. То есть пропадает кусками. Такое TCP обязан гарантировать.

emcode
() автор топика
Ответ на: комментарий от emcode

Не уверен, что такое «подвисание»

Это когда формального обрыва не происходит, но пакеты приходить перестают. Такое может происходить с долгоживущими соединениями при длинном пути между клиентом и сервером.

То есть пропадает кусками. Такое TCP обязан гарантировать.

Да. Что наводит на мысль о косяке в реализации TCP на стороне железки.

annulen ★★★★★
()
Ответ на: комментарий от annulen

Oracle Linux внутри. У этой железки есть проприетарная система мониторинга и те же самые логи на нее без проблем передаются и ничего не теряется. Поэтому нет, проблема не в ней.

emcode
() автор топика
Ответ на: комментарий от emcode

В дампе есть пропуски sequence number TCP-пакетов?

теряется, например, 100 байт

Как-то маловато для потери целого пакета, даже с очень плохим MTU.

На стороне «железки» есть возможность записать дамп?

annulen ★★★★★
()
Ответ на: комментарий от annulen

В дампе есть пропуски sequence number TCP-пакетов?

Тоже про это думал, но это сложно проследить, потому что счетчик инкрементируется не на 1. Считать задолбаешься. Но встроенный анализатор на этот счет никаких ошибок не показывает, только потери.

На стороне «железки» есть возможность записать дамп?

Надо попробовать. Этот трафик идет с интерфейса управления, поэтому не факт. Спасибо за идею, упустил.

Как-то маловато для потери целого пакета, даже с очень плохим MTU.

Поэтому и подозрение на буфер сокета, как будто какой-то кусок не влез.

emcode
() автор топика

Спустя какое-то непродолжительное время, примерно 10 мин. с одного (и только одного) из клиентов начинают идти потери. Предположительно, это происходит во время кратковременных всплесков нагрузки. В дампе это видно как множественные Dup ACK и «previous segment not captured». Ну то есть, TCP нам говорит забейте на проверку ошибок на стороне приложения, я ГАРАНТИРУЮ доставку и … не гарантирует.

Весьма мутное описание. Во-первых, уточни:

1) Что значит «начинают идти потери»? Как ты это определил?

2) О каком дампе речь? Как ты его получил?

Во-вторых, у TCP нет способа передать данные без гарантированной доставки. Либо он передаёт всё что надо, либо не передаёт вовсе (соединение зависает или теряется).

firkax ★★★★★
()

это проблемы с сетью. входящий трафик клиента. клиент атправляет пакет с данными, сервер должен ответить ack после чего клиент отправляет следующий пакет. и этот ack от сервера где то теряется у тебя в сети. сделай дампы на сервере и клиенте в момент возникновения проблемы.

antech
()
Ответ на: комментарий от firkax

Что значит «начинают идти потери»? Как ты это определил? О каком дампе речь? Как ты его получил?

Приложение получает неполные сообщения и матерится в лог. Дамп - это tcpdump с сервера ан котором установлено приложение. В этом дампе видно что с сервера на железку летят Dup ACK (т.е. ретрансмиты), которые иногда сопровождаются сообщениями вида «previous segment not captured», т.е. какой-то ретрансмит все-таки не случился и это по времени совпадает с тем, что приложение получило неполное сообщение. Т.е. произошла потеря.

emcode
() автор топика
Ответ на: комментарий от emcode

Тоже про это думал, но это сложно проследить, потому что счетчик инкрементируется не на 1. Считать задолбаешься.

В wireshark можно отфильтровать пакеты, относящиеся к заданному потоку. pcap-дамп можно записать в tcpdump, а потом открыть в wireshark, если так удобнее.

Поэтому и подозрение на буфер сокета, как будто какой-то кусок не влез.

Если бы не влез, произошла бы ошибка записи (на стороне отправителя), или пакеты бы начали отклоняться (на стороне получателя) и передаватся заново. Потеря куска из середины может означать только три вещи: 1) баг в реализации tcp; 2) эти данные пропускает сам клиент — возможно, не обрабатывая ошибку записи в сокет на своей стороне; 3) какой-то злодей через raw-сокет шлёт специально сформированные пакеты, которые ломают нормальный поток.

annulen ★★★★★
()
Ответ на: комментарий от emcode

У приложения есть исходники (или ты их сам и писал)? Что значит «неполные сообщения»? У tcp нет способа задать длину сообщения, если что, он передаёт только поток байт, который может нарезаться на ответы recv() в совершенно любом порядке.

previous segment not captured - это относится к исходящим с сервера пакетам? Отключи tcp offload а сетевухе, из-за него иногда tcpdump всякую чушь выдаёт.

firkax ★★★★★
()
Ответ на: комментарий от annulen

Спасибо. Я понял, что надо сделать две вещи. Первое, исключить сеть, т.е. засунуть сервер/тестовую машину в тот же сегмент, где сидят клиенты. Второе, посмотреть как проблема выглядит со стороны клиента. Если не прямым дампом с него, то хотя бы зеркалом.

emcode
() автор топика

А нет ли на стороне писателя какого-то буфера перед записью в сеть? Звучит, будто бы он переполняется и не влезающие данные отбрасываются.

urxvt ★★★★★
()
Ответ на: комментарий от firkax

Что значит «неполные сообщения»? У tcp нет способа задать длину сообщения, если что, он передаёт только поток байт, который может нарезаться на ответы recv() в совершенно любом порядке.

Конечно есть. TCP - это транспорт в виде потока. Внутри него реализуется протокол обмена на уровне приложения. Клиент передает сообщение, вначале идет длина (фиксированное кол-во байт), потом полезная нагрузка. Сервер получает длину сообщения, узнает сколько байт в сообщении, потом читает полезную нагрузку. Потом точно также переходит к следующему сообщению. Если произошла потеря, т.е. ты не получил/потерял длину сообщения, то поток сломался и его надо чинить. Т.е. заново искать начало следующего сообщения. Это реализовано.

Но это все лирика и детали реализации. На стороне приложения всё нормально.

previous segment not captured - это относится к исходящим с сервера пакетам?

Это встроенный анализатор Wireshark так помечает потери TCP сегментов на основе анализа sequence number. Как я написал в самом начале, сервер ничего не пишет клиенту, он только получает с него TCP. Иначе говоря, подтверждение получения сообщений реализовано только на транспортном уровне средствами TCP.

emcode
() автор топика
Ответ на: комментарий от gag

Нет, это просто самый быстрый способ восстановления. Можно закрыть соединение и тогда клиент сам переподключится через какое-то время. Но он это делает с большим лагом, поэтому быстрее сохранить соединение и найти следующее сообщение по специальному маркеру.

emcode
() автор топика

Ну то есть, TCP нам говорит забейте на проверку ошибок на стороне приложения

ээээ, а если буфер забьется при потерях/тормозах сети? приложение даже не узнает об этом и данные потеряются

Rost ★★★★★
()
Ответ на: комментарий от emcode

Если произошла потеря

Такого не может быть. Ищи баги у себя в коде. Где-то не проверяется что записано в сокет столько сколько попросили или прочитано не столько сколько попросил

cobold ★★★★★
()
Ответ на: комментарий от emcode

Нет.

TCP - это транспорт в виде потока. Внутри него реализуется протокол обмена на уровне приложения. Клиент передает сообщение, вначале идет длина (фиксированное кол-во байт), потом полезная нагрузка. Сервер получает длину сообщения, узнает сколько байт в сообщении, потом читает полезную нагрузку. Потом точно также переходит к следующему сообщению.

Вот это правильно.

Если произошла потеря, т.е. ты не получил/потерял длину сообщения, то поток сломался и его надо чинить. Т.е. заново искать начало следующего сообщения. Это реализовано.

Вот это неправильно. Ты не можешь «не получить длину сообщения», поскольку это просто два (или сколько у тебя?) байта в потоке. Вот ты прочитал эти байты - это и есть длина. Но TCP тут совершенно ни при чём, это твой протокол поверх него.

Как я понял, у тебя в коде стоит какое-то условие, определяющее «неполучение длины». Как оно выглядит? (если что, таких проверок там быть не должно, за исключением разве что случая забагованного приложения - не tcp а именно приложения - на той стороне, которое шлёт изначально битый поток).

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)

Там голый TCP без HTTP? HTTP сервер часто настроен так, чтобы рвать соединение каждые 100 пакетов (не помню, входит это в стандарт или нет). Мы у себя часто встречали проблему что пакет, отправленный серверу по уже установленному соединению от нас отправлен, но ответ мы не получим, потому что сотый пакет и сервер без ответа просто обрывает сокет. Мы такое у себя детектим и перепосылаем пакет, потому что получив его сервер не выполняет задачу, отправленную в оборванном пакете. Если этого не делать, то получается, что есть какие-то необъяснимые потери.

PPP328 ★★★★★
()
Ответ на: комментарий от emcode

Конечно есть. TCP - это транспорт в виде потока. Внутри него реализуется протокол обмена на уровне приложения. Клиент передает сообщение, вначале идет длина (фиксированное кол-во байт), потом полезная нагрузка. Сервер получает длину сообщения, узнает сколько байт в сообщении, потом читает полезную нагрузку. Потом точно также переходит к следующему сообщению. Если произошла потеря, т.е. ты не получил/потерял длину сообщения, то поток сломался и его надо чинить. Т.е. заново искать начало следующего сообщения. Это реализовано.

ЯННП Вы сейчас про TCP или уже про свой сервер?

anc ★★★★★
()
Ответ на: комментарий от PPP328

HTTP сервер часто настроен так, чтобы рвать соединение каждые 100 пакетов (не помню, входит это в стандарт или нет).

В некоторых HTTP-серверах действительно есть такая функциональность: после обслуживания N запросов происходит перезапуск рабочего процесса. Но суть в том, что это должно происходить *после" ответа на запрос, а не вместо.

потому что сотый пакет и сервер без ответа просто обрывает сокет

Это больше похоже на жёсткую утечку памяти или ещё какой-то баг в серверном приложении, из-за которого оно тупо валится на сотом запросе, чем на штатное поведение.

annulen ★★★★★
()

А нет ли там фильтрации по состоянию соединения?

Какие значения стоят в net.netfilter.nf_conntrack_tcp_timeout_established ?

Нет ли на клиенте возможности включить keepalive для tcp соединения?

vel ★★★★★
()