[C++] А как сейчас модно сетевые сервера писать?

doublemother

Я имею в виду, чем принято пользоваться и всё такое. Развлекаться с сырой сишной работой со всеми этими bind accept poll и т.п. не хочется. Использовать что-то монструозное типа QTcpServer — тоже.
Попробовал было ØMQ, всем прекрасен, только под нагрузкой периодически внезапно подвисает где-то на минуту, потом раздупляется — пытался грешить на свой код, но не смог: кода всего пара страниц, система многопроцессорная, никаких блокировок, все классы, кроме ØMQ, стандартные STL-ные.
Хочется что-то простое, хорошо параллелящееся, чтобы, грубо говоря, в идеале я мог задать обработчики и не париться с приёмом запроса, отсылкой ответа, созданием нового воркера или передачей запроса уже имеющемуся.
Upd. Да, интересуют только линуксы.

slonishka

я на libev пишу все:
void watcher_callback(struct ev_loop *loop, ev_io *w, int tev)
{
/* do recv(2) */

ev_io_stop(loop, w);
}

...

ev_io_init(watcher, watcher_callback, sd, EV_READ);
ev_io_start(loop, watcher);

...

ev_run(loop, 0);

но это наверное слишком вручную для тебя будет.
хотя тут тоже по сути дела только задаешь коллбек и все.
но правда надо еще открыть сокет и сказать ему FIONBIO, но это вроде просто...
sd = socket(AF_..., SOCK_..., ...);

int nbio_value = 1;
ioctl(sd, FIONBIO, &nbio_value);

зато ничего "под нагрузкой периодически внезапно" не "подвисает где-то на минуту".

slonishka

короче, вряд ли тебе мой совет помог. :grin:

doublemother

короче, вряд ли тебе мой совет помог. :grin:
Увы :) По докам, конечно, всё круто и ваще, но как посмотрю на все эти "Кстати, память под ваши watcher'ы надо выделять самому, ручками, ах да, выделять её на стэке — это обычно плохая идея", сразу так тоскливо становится.
Мне очень нравится, как можно сделать в ømq:
    zmq::context_t context(0);
zmq::socket_t clients(context, ZMQ_XREP);
clients.bind("tcp://*:12345");
zmq::socket_t workers(context, ZMQ_XREQ);
workers.bind("inproc://workers");
std::thread* t[workerCount];
for (unsigned int i = 0; i < workerCount; ++i)
t[i] = new std::thread(worker_routine, &context);
zmq::device(ZMQ_QUEUE, clients, workers);
...
void worker_routine (zmq::context_t *context)
{
zmq::socket_t socket(*context, ZMQ_REP);
socket.connect("inproc://workers");
while(true)
{
zmq::message_t request;
socket.recv(&request);
/* do some work */
socket.send(reply);
}
}

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

okis

Зато у Бачана код короче )

NAIL

В треде ещё пианиста нехватает. Подождём ...

elenangel

Зато у Бачана код короче )
у пианиста, надо полагать, код толще будет

ava3443

Попробовал было ØMQ, всем прекрасен, только под нагрузкой периодически внезапно подвисает где-то на минуту, потом раздупляется — пытался грешить на свой код, но не смог: кода всего пара страниц, система многопроцессорная, никаких блокировок, все классы, кроме ØMQ, стандартные STL-ные.
какая версия ØMQ?
в список рассылки не писал? почему?

doublemother

2.1.7. Не писал, потому что до конца не уверен, что проблема с их стороны. Я лучше сначала локализую проблему, потом буду уже им писать.

Werdna

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

doublemother

Кстати, судя по всему, косяк действительно был у меня: я заюзал std::unordered_map из c++0x, и в какие-то моменты он начинал увеличивать свой размер и рехэшировать данные, что и приводило к таким тормозам, так что дурак действительно я.
После фикса уже полтора часа работает стабильно, если сутки ещё отпашет нормально, можно будет с уверенностью говорить, что ømq — это круто.

slonishka

Кстати, судя по всему, косяк действительно был у меня: я заюзал std::unordered_map из c++0x, и в какие-то моменты он начинал увеличивать свой размер и рехэшировать данные, что и приводило к таким тормозам, так что дурак действительно я.
ничего себе у тебя хеш-мапчик! данные минуту перекладывает!
сколько ж у тебя там данных-то? :shocked:
и как пофиксил?
и откуда STL? g++-ный?

slonishka

Кстати, память под ваши watcher'ы надо выделять самому, ручками, ах да, выделять её на стэке — это обычно плохая идея
а я помню что-то такое из доки, но как-то смутно... и, короче, не парюсь и выделяю на стеке. там в рассылке у леманна обсуждалось, что могут быть проблемы с отступами на каких-то неведомых архитектурах, но это архитектуры какие-то совсем неведомые (для меня так что пока все окейно было... =)

doublemother

ничего себе у тебя хеш-мапчик! данные минуту перекладывает!
сколько ж у тебя там данных-то? :shocked:
и как пофиксил?
и откуда STL? g++-ный?
С гиг где-то.
Взял да и заменил на обычный map. Скорость выборки возрастает некритично, зато перестраивать при увеличении не нужно.
g++-ный, да. На машине только 4.4 есть, так что очень теоретически это ещё может быть причиной — я люблю использовать всякое из c++0x, а в 4.4 поддержка ещё не слишком хороша.

slonishka

странно, мы вот именно в unordered_map как раз где-то в районе gcc-4.3-4.4 много хранили. гига по 2-4. может и 8 было где-то...
и он не перестраивался почему-то, правда он обычно на старте заполнялся чем-то сохраненным.
ну ладно, понятно. спасибо!

doublemother

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

slonishka

кстати, если кто пользуется sparse_hash, пианист недавно вот такую штуку про него рассказал:
http://google-sparsehash.googlecode.com/svn/trunk/doc/sparse...
[6] sparse_hash_map requires you call set_deleted_key before calling erase. (This is the largest difference between the sparse_hash_map API and other hash-map APIs. See implementation.html for why this is necessary.) The argument to set_deleted_key should be a key-value that is never used for legitimate hash-map entries. It is an error to call erase without first calling set_deleted_key and it is also an error to call insert with an item whose key is the "deleted key."
пипец, блин. очень неудобно, зачем вообще такие алгоритмы использовать, которые требуют deleted_key?
и потом, если уж пишешь drop-in replacement для std::map, то путь он будет до конца drop-in.
а не с вот такой вот заковыкой 6-ым пунктом в примечаниях мелким текстом.
причем, ладно бы он был каким-нибудь суперэффективным (хотя он неплох).
но он проигрывает judy и по скорости, и по размеру на всех задачах (+попсовых железках к ним что я видел.
а judy не нужно никакого deleted_key и прочей лабуды.
Оставить комментарий
Имя или ник:
Комментарий: