LINUX.ORG.RU

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

 , ,


0

3

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

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

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

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

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

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

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

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

★★★★★

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

Вполне нормально держать их открытыми. Только не забудь настроить системный лимит на количество открытых дескрипторов на процесс.

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

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

panter_dsd ★★★★
()

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

Если тред на соединение, то до десятков тысяч тоже проблем не будет. Если перспектив десятков тысяч нет, то и не заморачивайся. Если есть, то на асинхронное I/O переделывай, ну или планируй переделывать.

Это если на сервере есть возможность поставить хотя бы гигов 16 памяти. Если это какая-нибудь малина с 16 MB, там уже придётся думать.

В целом самый оптимальный с точки зрения сети способ для твоего юз-кейса это UDP. Данные шлёшь UDP-пакетами, сервер отвечает таким же UDP-пакетом мол принял и всё. Естественно нужно будет реализовывать всю логику, зашитую в TCP: ретрансляция потерянных пакетов, откидывание дубликатов, переупорядочивание пакетов, пришедших не в том порядке и тд. Но думаю, это уже оптимизация на спичках, времени потратишь много, а видимого эффекта не будет.

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

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

Вот тут у меня начинает хромать логика, т.к. с такими кучами потоков не работал пока. Допустим, при коннекте я беру открытый сокет и сохраняю его в список, который висит в памяти. Дальше вопрос - как получить в нужный момент пришедшие в него данные? Я это вижу так: на каждый сокет стартует тред, в котором запускает readline и ждет, пока эта самая line туда придет. Или тут я тоже неправ?

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

Это если на сервере есть возможность поставить хотя бы гигов 16 памяти. Если это какая-нибудь малина с 16 MB, там уже придётся думать.

Вангую, что хотят вообще VDS с парой гигов…

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

В целом самый оптимальный с точки зрения сети способ для твоего юз-кейса это UDP. Данные шлёшь UDP-пакетами, сервер отвечает таким же UDP-пакетом мол принял и всё. Естественно нужно будет реализовывать всю логику, зашитую в TCP: ретрансляция потерянных пакетов, откидывание дубликатов, переупорядочивание пакетов, пришедших не в том порядке и тд. Но думаю, это уже оптимизация на спичках, времени потратишь много, а видимого эффекта не будет.

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

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

Есть несколько механизмов по ожиданию данных от сокетов.

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

Какова позиция архитектора проекта по этому вопросу?

Вон он выше вопрос задает, в первом посте.

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

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

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

Тебя повысили до архитектора?

В данном случае я сам себе и архитектор, и программист, и менеджер.

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

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

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

Мишутка, как там военная приёмка? Все на винде сидит?

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

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

Надо попробовать. Сохранятор в БД у меня отдельно работает все равно, этим потокам надо только получить данные и передать их в очередь обработчик.

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

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

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

Ты можешь поднимать инстансы на разных портах и поставить сверху балансировщик (nginx), который будет приземлять сокеты на нужный порт.

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

Как я писал выше, тебе не нужно по треду на соединение - это явный оверхед. Тебе достаточно будет N тредов в пуле, которые будут обрабатывать данные от девайсов

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

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

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

Треды нужны на обработку данных, а не на чтение, ибо чтение достаточно легкая операция.

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

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

Тогда мне осталось только найти инфу, как без тредов обрабатывать пришедшие в N сокетов данные. Гугл, правда, везде выдает как создать по треду на сокет…

Обрабатываются они у меня и так в пуле.

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

Я выше давал тебе ссылку на gevent.

Да, я видел, спасибо.

Есть есть Qt, то там это вообще из коробки есть.

У меня-то не Qt =)

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

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

Давным давно, когда я искал новую работу и хотел перейти с J2ME на backend, проходил собеседование в конторе, где искали обоих. На J2ME я прошёл, но просился на backend, а мне в этой просьбе отказывали. Ну и чтобы показать мою тогдашнюю неготовность задали простой вопрос: «как написать сервер, который может одновременно держать 10000 соединений?» Я начал говорить, что основной проблемой будут накладные расходы (context switch) на такое количество потоков исполнения. Со мной согласились и спросили: «так как же надо сделать?» Тогда я ешё не знал про NIO и multiplexing.

hummer
()

пару тыщ сокетов

Норм.

по треду на каждый

Зачем?

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

Я это вижу так: на каждый сокет стартует тред

Архитектура ПО, основанная на асинхронном вводе/выводе, позволяет избежать создания множества тредов. При желании все соединения можно успешно обслуживать в одном потоке. На современных многоядерных процессорах оптимальнее делать число тредов пропорционально число ядер.

ИМХО в нагруженных приложениях лучше, когда число потоков зависит от количества ядер, а не от количества соединений.

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

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

Не обязательно. Это вкусовщина. Я не пользуюсь сторонними библиотеками, у меня свой код обслуживает всю асинхронщину. В качестве основы использую epoll().

pathfinder ★★★★
()
Последнее исправление: pathfinder (всего исправлений: 1)
Ответ на: комментарий от Zhbert

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

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

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

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

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

Ты упускаешь возможность держать 100500 коннектов на кратно меньшем числе потоков.

ya-betmen ★★★★★
()
Ответ на: комментарий от panter_dsd

Про джаву не знаю, но что на плюсах, что в пайтоне, что в Го - везде это достаточно легко делается.

Тут вроде тоже, судя по поверхностному гуглежу. Я просто никогда с таким не работал, поэтому затупил. Ну и не знал, куда гуглить, если честно.

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

Ты упускаешь возможность держать 100500 коннектов на кратно меньшем числе потоков.

Угу, уже ткнули носом выше куда надо =)

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

Ох, блин, не обратил внимания, что у тебя жаба

Кхм, жаба? Не силен в сетевых приложениях на яве. Вроде у неё есть штатный API - NIO. Он призван решать эту задачу.

pathfinder ★★★★
()

ServerSocketChannel в одном потоке и обработчик принятых SocketChannel в другом?

Только с обратной связью мне кажется могут быть проблемы из за " поключенными через мобильный инет или вообще черт знает как". Что будет при попытке послать команду девайсу если подключения еще нет, или оно было, но упало? Насчет брокера типа RMQ не думали?

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

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

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

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

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

Не особенно накладно и не дичь, а нормальная практика. Если конечно ты не отдельный тред на сокет делаешь.

no-such-file ★★★★★
()
Ответ на: комментарий от Zhbert

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

А именно:

Please keep in mind that it is the handler’s responsibility to release any reference-counted object passed to the handler.

Result-Code
()
Ответ на: комментарий от Result-Code

Тогда лучше сразу в руки бери netty, чтобы поменьше ошибок совершать.

Спасибо, посмотрю.

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

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

Выбрал не тот инструмент. Я бы тупо сделала это на эликсире за 2 дня. 1 коннект - 1 процесс и 64к+ коннектов тогда это просто фигня. Конкурентный доступ на запись в базу обеспечивается самой базой.

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

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

anonymous
()

Не вижу никакого смысла держать. Память впустую расходуем, а что выигрываем? Разве что round trip на установку соединения, но если это важно то нужно смотреть в сторону UDP.

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