LINUX.ORG.RU

Си + libevent

 , ,


0

2

Уважаемые! Прошу подсказать мне вот в каком вопросе: У меня есть сервер один на сишечке с либевент и второй на пайтоне с twisted'ом. Сишечный сервер подключется в отдельном потоке к пайтоновскому серверу. На сишечный сервер сыпятся всякие данные, которые он обрабатывает и передает пайтоновскому серверу. Могу ли я эти данные передавать пайтону прям в коллбеке входящего сообщения либевента? Если нужно дождаться ответа от пайтоновского сервера (а пайтоновский сервер в свою очередь с этим запросом работает с базой данных), ну и конечно одновременных подключений к сишному серверу может быть МНОГО!

Могу ли я эти данные передавать пайтону прям в коллбеке входящего сообщения либевента?

Анонимус разрешает. Только через очередь.

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

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

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

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

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

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

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

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

Ты гарантированно не тормозишь прием от клиентов, Но исполнение может задерживаться. Зато ты получаешь возможность сказать очередному клиенту, например «busy», если счетчик невыполненных задания перевалит за какую-то N.

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

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

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

Спасибо. за разъеснение) А можешь подсказать как быть, если на пример от пайтона на очередное сообщение пришел какой-нить запрос, который нужно этому клиенту отправить. На сколько оправдан будет такой вариант: в очередь по мимо данных, которые пришли от клиента (и которые надо передать пайтону) заносим ссылку на объект struct bufferevent клиента и потом когда во втором потоке перебирая очередь пайтон на одно сообщение нам говорит: «у этого клиента надо спросить такие-то параметры» в этом потоке вызываем:

bufferevent_write(queue->buf_ev, MESSAGE, strlen(MESSAGE));
Или не желательно из двух разных потоков обращаться?

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

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

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

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

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

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

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

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

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

Это называется event-driven development. Вам в либэвентовском потоке нужен простейший конечный автомат. В данном случае достаточно добавить в запрос к питону и в ответ от него некий уникальный идентификатор запроса или идентификатор сессии. По идентификатору вы тем же единственным либэвентовским потоком найдёте контекст обработки и примете решение о дальнейших действиях.

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

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

А если сервис пайтона вдруг не доступен? Или ответ задерживается? Это же приостановит работу сервера? И другие клиенты будут простаивать.

Это называется event-driven development. Вам в либэвентовском потоке нужен простейший конечный автомат. В данном случае достаточно добавить в запрос к питону и в ответ от него некий уникальный идентификатор запроса или идентификатор сессии. По идентификатору вы тем же единственным либэвентовским потоком найдёте контекст обработки и примете решение о дальнейших действиях.

Все активные клиенты хранятся у меня в отдельной очереди, где есть ссылка на struct bufferevent клиента. Но как в этой очереди найти нужного клиента? Перебор очереди?

Сейчас сделал так: 1-ая очередь с активными клиентами 2-ая очередь с сообщениями для клиентов полученные от пайтона. И по таймеру либэвента перебираются эти очереди. 2 цикла: внешний перебирает активных клиентов, внутренний ищет в очереди сообщений. Если для клиента есть сообщение оно отправляется и цикл дальше выполняется... но это блин 2 цикла перебора.

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

А если сервис пайтона вдруг не доступен? Или ответ задерживается? Это же приостановит работу сервера? И другие клиенты будут простаивать.

Отправлять-то асинхронно надо, тогда всё будет в порядке. Через либэвентовский bufferevent, например, как вам выше посоветовали, либо напрямую, с использованием ожидания либэвентовского события записи на дескрипторе связи с питоном.

Сейчас сделал так:

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

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

Т.е создать в потоке либэвента отдельный дескриптор для подключения к пайтону?и к нему также обращаться как и к подключенным к серверу по tcp клиентам? И если он вдруг станет не доступным, то сообщения автоматически будут складываться в очередь? А если при подключении к пайтону от него нужно дождаться ответа, что он готов к приему данных и только после этого можно отправлять ему данные, да и после отправки данных нужно или ответа ОК дожидаться или новое сообщение доя клиента и ОК, а если ответа не было, то сообщение считается не доставленным... И получается, что все равно как-то нужно в списке подключенных клиентов искать, того кому нужно отправить соединение, каждому объекту присваивается идентификатор сессии, но кроме как перебора очереди я не понимаю как найти нужного клиента...

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

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

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

идите изучайте асинхронку, у вас в голове это не помещается, либо выбрасывайте libevent, и пишите на своих потоках

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

Да сейчас вроде все встает на места. Если я правильно понял, то система должна быть такая: Один поток, одна очередь куда складываются сообщения от клиентов (сообщения от клиентов разбираются в колбеке либэвента и складываются в эту очередь) И создаем еще один луп для работы с пайтоном, который при наличии данных в очереди отправляет их пайтону, верно?

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

И создаем еще один луп для работы с пайтоном

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

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

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

Код на питоне просто ищет в базе с указанным идентификатором (каждый клиент, который подключается к либэвенту имеет свой идентификатор) сообщение и если есть, то отправляет его обратно и слово ОК, что сообщение принято. Если в базе для данного клиента ничего нет, то просто отвечает ОК. Поэтому еще нужно дожидаться от пайтона фразы ОК, что мол принял запрос. Ну и соответственно если пайтон вернул для данного клиента сообщение, то его нужно отправить нужному клиенту. А вот как найти нужного клиента?

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

Да и если отправку данных питону сделать асинхронно, то получается же так: Отправили данные пайтону, дальше ответа не дожидаемся выполняем дальше основной код. Пайтон сообщение «переварил» и отправил обратно серверу либэвент, у либэвента возникло событие - вызвалась колбек ф-ция по приему сообщения и в ней уже анализируем ответ от пайтона. Или я что-то не так понимаю?

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

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

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

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

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

что такое подразумевается под ПОТОК thread?? в асинхронке нет потоков, все действия выполняются по событию, событие от таймера или событие от дескриптора, если событий нет значит просто крутится голый циклы в котором может выполнятся какая то отложенная процедура, как там контролируется взаимодействие с питоном у вас я хз

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

если ответа о получении сообщения не было, то повторно отправить данные

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

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

Грубо говоря в первом варианте была следующая схема связи в пайтоном: 1. Отправили данные (send) 2. Ждем от него ответа (recv). Если ответа не было переподключаемся к пайтону (данные из буфера не удаляем) 3. Получили ответ от пайтона. Разбираем его, если в ответе просто фраза ОК считаем сообщение доставленым (удаляем его из очереди), если кроме фразы ОК в ответе присутствует еще сообщение, то складываем ее во вторую очередь (там хранятся сообщения, которые в итоге нужно отправить определенному клиенту).

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

Если в синхронном режиме тут все прозрачно: 1. подключились. 2. дождались ответа, что сервис готов. 3. отправили сообщение 4. получили потверждение о приеме. То с асинхронной моделью как я понимаю подобное сделать будет сложнее?

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

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

Тут подумал вот на счет «поиска» «нужного» клиента. Т.е того клиента для, которого нужно отправить некое сообщение полученное от пайтона. На сколько правильный такой вариант будет: при передачи разобранной строки от клиента передаем еще сокет-дексриптор, пайтон разбирает строку смотрит в базе и блаблабла и возвращает потом сообщение для отправки + сокет-дескриптор с помощью которого потом обычной ф-цией send отправляем это сообщение клиенту.

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

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

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

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

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

Окей, с этим понятно. Если переводить общение с пайтоном на асинхронку в потоке с либэвентом, то как оптимальне организовать его работу? 1. подключаемся к пайтону 2. ждем от пайтона сообщение о готовности, что вроде «READY\r\n» 3. отправлем пайтону данные 4. ждем потверждения от пайтона о приеме данных и если есть о сообщениях для определенного клиента в базе «MESAGE\r\nOK\r\n» или просто «OK\r\n» если сообщений нет.

Если в синхронной модели все понятно - идем просто по порядку, то как быть в асинхронной модели?

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

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

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

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

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