LINUX.ORG.RU

Разумно ли держать открытыми множество соединений TCP?

 , ,


0

3

Возник вопрос в процессе разработки, можно сказать идеологический.

Итак, есть физические девайсы, которые по TCP шлют данные в виде массива байт на сервер. Есть сервер, который принимает присланный массив, парсит его и сохраняет в БД.

Также у девайсов есть возможность получать извне команды, которые они обрабатывают и что-то выполняют.

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

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

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

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

Вопрос - может, что упускаю?

★★★★★

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

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

Для примера, где есть такая возможность: серверы SMPP (например, для передачи обычных смс-ок). Кстати, тут вы могли бы взять сервер SMPP.

anonymous
()

Вот поэтому погроммировать нужно допускать только людей с профильным образованием :)

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

Вот поэтому погроммировать нужно допускать только людей с профильным образованием :)

Тебе показать мой диплом государственного образца по профессии Java девелопера? =)

Zhbert ★★★★★
() автор топика

Майните чтоли там?

anonymous
()

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

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

В тэгах же. Джава.

Не увидел. Тогда тоже присоединяюсь к netty.

turtle_bazon ★★★★★
()

по треду на каждый, по идее, ведь и с той стороны данные все еще будут идти раз в минуту

нинужно, используй poll

anonymous
()
Ответ на: комментарий от Harald

Ну, они накладывались на ранние знания все же, образование-то у меня изначально профильное.

А вообще ИМХО важна не корочка, а опыт и приобретённые во время работы знания. Утверждаю это как человек, обучавший с нуля «специалистов» с профильным образованием, от которого толку было только поставить чашку на книжечку диплома.

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

Поскольку в данному случая у меня нет наставника выше на работе, пришлось спрашивать здесь. Ответ я получил, дальше разберусь. Главное, чтобы в итоге было сделано по феньшую, а не сразу. А где получить пинок в нужную сторону - это уже мелочи.

Zhbert ★★★★★
() автор топика
  1. На длинные соединения делать потоки неправильно. Надо делать асинхронную работу с сетью (Java NIO или взять готовую библиотеку вроде https://netty.io/)
  2. Поддержку комментарий анонимуса «Если кратко, то желательно заложить оба подхода». Реализуйте в программе «очередь команд на отправку» - чтобы, если сейчас нет соединения с устройством, для которого предназначена команда - она добавлялась в очередь и отправлялась, когда устройство подключится.

Пара тысяч сокетов имхо не очень много, раз по ним не идет большой траффик. Но четыре тысячи потоков в программе (по паре на сокет) - перебор.

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

она добавлялась в очередь и отправлялась, когда устройство подключится.

А если устройсво перезагрузилось и прежний контекст не имеет смысла?

anonymous
()
Ответ на: комментарий от Zhbert

Хочешь буду твоей наставницей? Только если будешь меня слушаться.

anonymous
()
Ответ на: комментарий от Harald

поидее его на этапе собеса должны были срезать. даже в ждуны (допустим он не знал как это сделать именно на жабке, но саму концепцию и основы должен был знать, хотя бы на пальцах)

ну или действительно сейчас даже водителей автобусов берут неглядя, как в рекламе курсов вайтивайти

n_play
()
Ответ на: комментарий от Legioner

Первое правило UDP: если изобретаешь ретрансляцию или упорядочивание или дедупликацию, то тебе нужен TCP.

anonymous
()
Ответ на: комментарий от Harald

Щас правильно писать Software Engineer, Lifelong Learner, Philanthropist, Mysanthrope.

anonymous
()

И оффтоп, но передайте @bad_master, что его задача решается int len = snprintf("", 0, «%lld», lli) - (lli < 0); без писанины в память. Заколебали анона блочить, а потом всем тредом ковырять суп вилкой.

anonymous
()
Ответ на: комментарий от Legioner

Естественно нужно будет реализовывать всю логику, зашитую в TCP:

Это дерьмо почти никогда ненужно.

ретрансляция потерянных пакетов, откидывание дубликатов, переупорядочивание пакетов, пришедших не в том порядке и тд.

Это всё элементарно решается, если оно нужно, конечно. Основной пердолинг в tcp не в этом. Там всякое легаси, подбор окна на сетях дерьма.

Если по пунктам. Порядок нахрен ненужен. Его нигде нет, если только это убогая передача файликов. В 99% случаев хватит каких-то атомарных сообщений.

На дубликаты так же насрать, потому как их должна фильтровать логика выше, Если они критичны.

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

Ога, подумаешь какие-то порядки по производительности и линейная маштабируемость в отличии от tcp-параши. Кому они нужны. Эффекта то нет.

rafyibofye
()
Ответ на: комментарий от Zhbert

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

rafyibofye
()
Ответ на: комментарий от Zhbert

Это не куча потоков - это нисколько. По поводу «как читать» - так ненужно делать. Сокет - это абстракция дерьма созданная идиотами для домохозяек. Она целиком и полностью несостоятельна.

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

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

Условно fds = wait({fd0, fd1, fd2, ..., fdn-1}, READ_READY);, далее ты получает список всех fd для которых можно выполнить read и получить данные.

Потоки дерьмо не потому, что у тебя там их много - это протухшая чушь. А в том, что практически никогда эти потоки не являются независимыми. И потоки используются чисто для отслеживания состояния «есть данные» на fd.

Далее ты всё это будешь всё равно писать в одно место. В результате ты либо родишь свой мультиплексинг, либо будешь пердолится с локами до потери пульса и это дерьмо будет лагать, течь и пребывать в вечном дедлоке.

Поэтому происходит хейт тредов, поэтому там производительность дерьмо. А не потому, что там как-то плохо от тысячи тредов.

rafyibofye
()
Ответ на: комментарий от anonymous

Это неправда. В TCP много избыточности. Если нужна максимальная эффективность, от TCP нужно уходить. В TCP один хендшейк сколько времени занимает, а зачем он топикстартеру нужен? Он отослал UDP-пакет с информацией, получил в таком же UDP-пакете ответ OK и на этом всё. В TCP мы сначала полчаса раздупляемся, пока здороваемся, потом с фанфарами передаём тот самый пакет на 20 байтов с информацией и потом ещё закрываемся со всеми церемониями.

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

то нужен и порядок и ретрансляция и дедупликация.

Ненужно, к тому же я об этом сообщил:

В 99% случаев хватит каких-то атомарных сообщений.

Порядок в рамках сообщения - это не порядок в рамках стрима. Это совершенно разные задачи. Как и ретрансляция. И пакет может быть попилен.

В общем методичка есть, понимания темы и умения читать нет.

rafyibofye
()
Ответ на: комментарий от Legioner

Это малая часть проблемы. На трупут к тому же это не сильно влияет в рядовой сети.

Проблема в tcp-дерьме именно в состоянии, т.е. в самом подключении. При этом это состояние нахрен никому не упало, потому как на хайлевеле итак всё дублируется, но о чём ты сообщаешь.

В результате мы имеет тормоза, адский пердолинг на уровне сетевой инфраструктуры, адский пердолинг с балансированием всего этого дерьма, с переброской трафика.

Поэтому в tcp не просто много избыточности - он из неё целиком состоит.

rafyibofye
()

Итак, есть физические девайсы, которые по TCP шлют данные в виде массива байт на сервер.

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

anonymous
()
Ответ на: комментарий от Zhbert

Оно и сейчас так работает. Принял пакет, ответил «ОК», отключился и ждет дальше. Вопрос в том, как реализовать отправку команды при условии отсутствия прямого контакта с устройством (наты и прочее без возможности настроить пробросы портов).

А как происходит tcp соединение? Так же и по udp подключайся. Нехай устройства за натом шлют с рандомного upd порта свой статус на сервер через энные промежутки времени, и между посылками продолжают прослушивать порт, а сервер из Интернета отвечает на эти адреса и порты, когда ему надо. Но моё имхо, не нужно городить tcp там, где можно обойтись udp, даже если соединений немного.

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

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

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

В таком варианте нужно будет периодически слать keep-alive-ы, т.к. роутер очень быстро очищает NAT-правило для UDP. По-моему 30 секунд типичный таймаут, то бишь нужно слать чаще.

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

Не вижу большой проблемы для сети и сервера передать и принять раз в 15 сек. 2000 * 160 байт < 320 КБ. И уж тем более не вижу проблемы для клиентов, каждому из которых надо будет передать только 160 байт раз в 15 сек. Зато не будет проблем с зависанием tcp-соединений, которые порой случаются. Плюс не надо будет упорядочивать пакеты, которые, как я понимаю, в этом на самом деле не нуждаются. Хотя, конечно, лучше потестировать расход трафика, нагрузку на сервер и стабильность работы в обоих режимах.

P.S. Кроме того, слать контрольные пакеты можно и реже, если команды клиентам не такие уж срочные. Например, если +/-5 мин. роли не играют, то клиент может отмечаться раз в 5 мин., а сервер, если получил уведомление от клиента более 30 сек. назад, может просто подождать, когда клиент снова пришлёт свой адрес и порт.

aureliano15 ★★
()
Последнее исправление: aureliano15 (всего исправлений: 2)
Ответ на: комментарий от Legioner

Атомарных сообщений не хватит.

Ага.

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

Это ничего не изменит. К тому же это уже пошли шуе-мазы, вместо вменяемых доводов.

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

Кучу. Ты перепутал методичку. Кучи в дерьмовой скриптухе, как и баги.

Мало того, что переписывать ничего не придётся, потому как ничего не изменится. Фентезийная подпись часть сообщения. Какие-то кучи есть в мусоре вроде tcp,java и прочей скриптухи.

Заходы уровня «производительность» так же смешны, потому как эти проблемы не имеют никаких предпосылок - это просто фантазии. А даже если бы имели, то оно откатится до уровня рядового дерьма. И, поздравляю, ты сам себе похоронил. Потому как если подобные проблемы - проблемы, то ведь тот мусор, что ты знаешь - это проблема. Вопрос, почему же ты не идёшь и никому об этом не сообщаешь?

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

Условно fds = wait({fd0, fd1, fd2, …, fdn-1}, READ_READY);, далее ты получает список всех fd для которых можно выполнить read и получить данные.

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

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

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

Нет, всё это ненужно. Там никто на read не читает какие-то данные из сокета. Даже если read блокирующий. Здесь блокировка используется именно для того, чтобы read вернул какие-то данные. Ни для чего более.

Сам read просто читает данные из буфера и ему похрен на скорость сетевого соединения. Заполнением буфера занимается ядро, мы к нему отношения не имеем.

В этом и смысл мультиплексера. Мы вместо создания n потоков и блокирования в каждом из них до момента появления данных в буфере, имеем один поток и блокируемся сразу на всех. А исполнение возобновляется уже тогда, когда в каком-то сокете(его буфере) есть данные. И мы можем их прочитать. Вот нам возвращает список этих сокетов.

rafyibofye
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.