много серверов хороших и разных

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

много серверов хороших и разных

Непрочитанное сообщение Olej » 24 апр 2012, 14:14

Это тема чисто программистская.
Это тема - продолжение того, что вспомнилось в viewtopic.php?f=9&t=1661#p3702:
- Сервер TCP/IP... много серверов хороших и разных
...
P.S. удивило, что поиск по фразе "много серверов хороших и разных" показал, что статья эта за прошедшие годы растиражирована по интернет в десятках, если не сотнях экземпляров...
Вот адаптация и проверка под Linux того, что было сделано под ОС QNX 10 лет назад - и есть эта тема.
Адаптация эта не прошла так гладко ... один вечер я уже убил на то, чтобы только добиться компиляции:
- там использована специфика использования счётчика процессорных тактов RDTSC как сверхточная шкала времени, а эти вещи в разных ОС выражены по-разному;
- в QNX есть (в API) такая сильная выдумка, как "динамический пул потоков" ... в Linux можно сделать такую же, при желании, или от одного из вариантов сервера отказаться;
- но нужно бы добавить к вариантам: последовательный сервер с очередью обслуживания - уже после опубликования первоначальной статьи мне много писали и обсуждали именно по этой части ... и оказывается, что такой последовательный сервер в конкретных ситуациях может быть оптимальнее параллельных, ... а в литературе о соображениях оптимальности производительности обсуждаются только параллельные сервера.

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

... поехали! ;-)

P.S. Первоначальную (давнюю) статью, от которой начинается вся пляска, лучше всего (по качеству редактирования) скачать на сайте журнала СТА - http://www.cta.ru/online/online_progr-nets.htm :
Изображение

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 24 апр 2012, 19:26

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

Код: Выделить всё

if( ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) = -1 ) errx( "create stream socket failed" );
Вот с тех пор я уже всегда подобные вещи записываю только вот так:

Код: Выделить всё

if( -1 == ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) ) errx( "create stream socket failed" );
Чего и вам желаю ;) (записывать вот так равенства ;-) ).
Как могла такая ошибка затесаться в публикацию, учитывая, что всё это делалось из работающих примеров кода!

Сейчас это это всё реконструировано и работоспособно в Linux, первое приближение (всё как было раньше, но меня это на сегодня не сильно устраивает) выложено в архиве.
Вложения
xservers.0.tgz
(31.66 КБ) 774 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 24 апр 2012, 21:42

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

Код: Выделить всё

pthread_create( NULL, NULL, &echo, &rs ) 
заканчивается

Код: Выделить всё

Ошибка сегментирования
что, вообще то, противоречит POSIX ... но Linux-у POSIX не указ, как известно, и это обязательно переписать так:

Код: Выделить всё

pthread_t tid;
...
pthread_create( &tid, NULL, &echo, &rs ) 
даже если вам идентификатор потока и даром не нужен ;-)

В любом случае, имеем в итоге 7 принципиально разных схем для построения серверов массового обслуживания.
И результаты тестирования их латентности...
Вложения
xservers.1.tgz
(137.33 КБ) 783 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 24 апр 2012, 22:02

Olej писал(а):В любом случае, имеем в итоге 7 принципиально разных схем для построения серверов массового обслуживания.
И результаты тестирования их латентности...
Сервера ретранслируют полученный запрос от клиента, после чего тут же закрывают соединение (так как поступает HTTP сервер).
Клиент фиксирует время задержки ответа.

Вот как это делает простой последовательный сервер:

Код: Выделить всё

[olej@notebook xservers]$ ./ech0
waiting on port 51000 ...
********************^C
[olej@notebook xservers]$ ./cli -p51000 -n20
TCP port = 51000, number of echoes = 20
time of reply - Cycles [usec.] :
318460[28661686]	213290[19196291]	201110[18100081]	233470[21012510]	244430[21998919]
201360[18122581]	243220[21890018]	326750[29407794]	215910[19432094]	287910[25912159]
211400[19026190]	359160[32324723]	233650[21028710]	198970[17907479]	206120[18550985]
218420[19657996]	293030[26372963]	221150[19903699]	198940[17904779]	206100[18549185]
А вот параллельный сервер блокированный на fork() (это классика - так работал Apache до версии 1.3 и работают все примеры для студентов):

Код: Выделить всё

[olej@notebook xservers]$ ./ech1
waiting on port 51001 ...
********************^C
[olej@notebook xservers]$ ./cli -p51001 -n20
TCP port = 51001, number of echoes = 20
time of reply - Cycles [usec.] :
595450[53591035]	629270[56634866]	715770[64419944]	679180[61126811]	653250[58793087]
643840[57946179]	632300[56907569]	280910[25282152]	576450[51881018]	653550[58820088]
620080[55807758]	696840[62716227]	696120[62651426]	656340[59071190]	662720[59645396]
660530[59448294]	1414580[127313473]	627310[56458464]	709540[63859238]	727710[65494554]
- задержка ответа вдвое больше чем у последовательного сервера, что естественно и понятно...

То же самое, но в работу fork() добавлен большой блок не инициализированных данных - показывает, что Copy On Write в Linux работает успешно:

Код: Выделить всё

[olej@notebook xservers]$ ./ech10
waiting on port 51002 ...
********************^C
[olej@notebook xservers]$ ./cli -p51002 -n20
TCP port = 51002, number of echoes = 20
time of reply - Cycles [usec.] :
265470[23892538]	644100[57969579]	626790[56411664]	1402360[126213662]	664800[59832598]
684810[61633516]	691680[62251822]	654220[58880388]	643030[57873278]	1349080[121418414]
594810[53533435]	625440[56290162]	609850[54887048]	536000[48240482]	306850[27616776]
699890[62990729]	607250[54653046]	611850[55067050]	591130[53202232]	638790[57491674]
Вот более редкая схема с pre-fork (fork() делается раньше, а блокирование потом производится на accept() - это нарушение правил, но это работает!)

Код: Выделить всё

[olej@notebook xservers]$ ./ech11
waiting on port 51003 ...
21021021021021021021^C
[olej@notebook xservers]$ ./cli -p51003 -n20
TCP port = 51003, number of echoes = 20
time of reply - Cycles [usec.] :
264890[23840338]	242110[21790117]	224740[20226802]	197670[17790477]	205860[18527585]
253670[22830528]	249320[22439024]	233310[20998109]	232180[20896408]	334040[30063900]
244620[22016020]	333940[30054900]	244620[22016020]	348460[31361713]	208530[18767887]
246230[22160921]	208680[18781387]	301630[27146971]	210000[18900189]	245390[22085320]
Реакция улучшена вдвое.

Вот ещё fork(), но косвенный: через суперсервер xinetd:

Код: Выделить всё

[olej@notebook xservers]$ ./cli -p51004 -n20
TCP port = 51004, number of echoes = 20
time of reply - Cycles [usec.] :
201372410[18123698136]	4631510[416840068]	6579840[592191521]	6685010[601656916]	6661510[599541895]
6611890[595076050]	6516470[586488164]	6669190[600233102]	6579970[592203222]	6344620[571021510]
6593150[593389433]	3761070[338499684]	6129520[551662316]	6586000[592745927]	6108540[549774097]
6098120[548836288]	6450080[580513005]	6042590[543838538]	6017040[541539015]	6184580[556617766]
Время хуже практически на порядок... Но не всем же нужна максимальная скорострельность? Зато какая простота кода!

Это параллельный сервер на потоках (нынешний Apache):

Код: Выделить всё

[olej@notebook xservers]$ ./ech2
waiting on port 51005 ...
********************^C
[olej@notebook xservers]$ ./cli -p51005 -n20
TCP port = 51005, number of echoes = 20
time of reply - Cycles [usec.] :
438550[39469894]	309340[27840878]	363090[32678426]	382170[34395643]	350030[31503015]
468220[42140221]	155430[13988839]	152060[13685536]	149410[13447034]	395890[35630456]
331570[29841598]	361000[32490324]	351430[31629016]	334230[30081000]	365590[32903429]
336360[30272702]	326130[29351993]	337690[30392403]	146510[13186031]	151240[13611736]
Время реакции вдвое лучше fork().

И предварительное создание потоков на манер pre-fork:

Код: Выделить всё

[olej@notebook xservers]$ ./ech22
waiting on port 51007 ...
30745137763066121072305772836830493356643040942960303255025630241575523015764848300737214429989794402990586736298219403229738013282965408624295701592029486232162940230512293183780829234451042915052400^C
[olej@notebook xservers]$ ./cli -p51007 -n20
TCP port = 51007, number of echoes = 20
time of reply - Cycles [usec.] :
248970[22407524]	283830[25544955]	299590[26963369]	401910[36172261]	219300[19737197]
229580[20662406]	326110[29350193]	147100[13239132]	249900[22491224]	143920[12952929]
233460[21011610]	147610[13285032]	147370[13263432]	325290[29276392]	331850[29866798]
154990[13949239]	154650[13918639]	322830[29054990]	321390[28925389]	369800[33282332]
Ещё лучше ... но чтобы зафиксировать это "лучше" сервера уже нужно запускать как приложения realtime-приоритета.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 25 апр 2012, 13:20

Последняя редакция:
Olej писал(а): Вот как это делает простой последовательный сервер:

Код: Выделить всё

[olej@notebook xservers]$ ./cli -p51000 -n20
host: localhost, TCP port = 51000, number of echoes = 20
time of reply - Cycles [usec.] :
162220[97]      185480[111]     113200[68]      114710[68]      112090[67]
112860[67]      112030[67]      112460[67]      112410[67]      111060[66]
111010[66]      112910[67]      110950[66]      113150[68]      112980[67]
111400[67]      112180[67]      133580[80]      112240[67]      114730[69]
- вот теперь то, что стоит в скобках [...] - это действительно микросекунды задержки ответа (программа клиента динамически при старте тестирует частоту тактов процессора), то что было раньше: число тактов - было совершенно точно, пересчёт в [...] - было ерундой, но пока было не актуально...

В этом варианте добавился ещё последовательный сервер с очередью обслуживания (теперь понятно и почему я это стал делать в С++ а не C - чтоб потом не бодаться с очередями, а использовать STL ... но это не так важно - всё можно как угодно переписать). Причём даже два варианта такого сервера:
- ech4 - неэффективный но простой и понятный (он тупо в цикле непрерывно перелопачивает очередь: нет ли там чего обработать и отправить?)
- ech41 - эффективный, там поток выгребания из очереди постоянно блокирована на условной переменной (pthread_cond_t), но для понимания это уже достаточно противный код :-?

Код: Выделить всё

[olej@notebook xservers]$ ./ech4
waiting on port 51008 ...
^C
[olej@notebook xservers]$ ./cli -p51008 -n20
host: localhost, TCP port = 51008, number of echoes = 20
time of reply - Cycles [usec.] :
1569360[943]    104440[62]      97460[58]       94970[57]       94870[57]
95580[57]       4757700[2861]   154200[92]      153970[92]      134830[81]
95470[57]       94520[56]       118130[71]      96130[57]       95430[57]
2833890[1704]   2060660[1239]   3542120[2130]   173680[104]     5137940[3090]
- видно как здесь задержка ещё достаточно "гуляет"...

Код: Выделить всё

[olej@notebook xservers]$ ./ech41
waiting on port 51008 ...
^C
[olej@notebook xservers]$ ./cli -p51008 -n20
host: localhost, TCP port = 51008, number of echoes = 20
time of reply - Cycles [usec.] :
174500[104]     136060[81]      112480[67]      112190[67]      111700[67]
112380[67]      111880[67]      111550[67]      109840[66]      110350[66]
112420[67]      114030[68]      150190[90]      113680[68]      110330[66]
112930[67]      110720[66]      110890[66]      110090[66]      110330[66]
- но здесь уже приближается к асимптотически минимальному значению даваемому простым последовательным сервером.
Вложения
xservers.4.tgz
(138.76 КБ) 772 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 01 май 2012, 11:00

Olej писал(а): Вот адаптация и проверка под Linux того, что было сделано под ОС QNX 10 лет назад - и есть эта тема.
Переписал полностью текст под Linux + переделал все примеры серверов + просмотрел их сравнительные характеристики в LAN - интересные получаются цифры!

Здесь (в прикреплённых файлах) всё: и текст и архив кодов.
Вложения
EchSrv-new.08.odt
(37.12 КБ) 787 скачиваний
xservers.5.tgz
(140.84 КБ) 785 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 05 май 2012, 18:31

Olej писал(а): Переписал полностью текст под Linux + переделал все примеры серверов + просмотрел их сравнительные характеристики в LAN - интересные получаются цифры!
Но вот это - то что описано в статье - это только начало, начальный шаг, по оцениванию применимости техник исполнения серверов к реальным потребностям.

Я специально (провокация! ;-) ) для оценки взял только один, и не самый характерный критерий оценки - время реакции (ответа) при низкой интенсивности запросов. И по такому критерию наилучшим будет простой последовательный сервер! ... который по другим критериям будет наихудшим.

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

Простейший пример-вопрос:
- добавьте в "работу" сервера, помимо тупой ретрансляции, некоторую работу, которая требует фиксированного объёма работы-обслуживания (скажем 1 миллисекунда)...
- но этот интервал обслуживания может быть: а). пассивным, в тесте что-то типа sleep(1) б). активным в тесте - цикл до истечения интервала обслуживания...
- результаты оценивания серверов в том и другом случае будут диаметрально противоположными! (при 100% активном обслуживании всякий параллельный сервер будет проигрышным!)
- а реальная работа серверов массового обслуживания - это цифра между 0% и 100% активности в интервале обслуживания единичного запроса.
- второй фактор, который (в паре с предыдущим) ключевым образом будет влиять на оптимальность выбранной модели сервера - это число процессоров (ядер), задействованных в работе сервера.

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 10 май 2012, 12:33

Этот вариант текста (+примеры) В.Костромин выложил на сайте для более простого чтения:
http://rus-linux.net/MyLDP/algol/analiz ... ra-01.html

Он же указал ссылку на обсуждение этого текста на LOR:
http://www.linux.org.ru/news/doc/7733472
... но мне там, по существу вопроса ;-) , пришлась по душе только одна реплика:
Ожидаемо
На ЛОРе одни дебилы, которые путают строительные инструменты с чертежами.

Hug37r011 (10.05.2012 10:30:02)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: много серверов хороших и разных

Непрочитанное сообщение Olej » 11 апр 2015, 17:05

Olej писал(а):
Olej писал(а):В любом случае, имеем в итоге 7 принципиально разных схем для построения серверов массового обслуживания.
И результаты тестирования их латентности...
Сервера ретранслируют полученный запрос от клиента, после чего тут же закрывают соединение (так как поступает HTTP сервер).
Клиент фиксирует время задержки ответа.
Эти тесты дополнительно интересны применительно к сравнении эффективности "потоки vs процессы", которое возникло вот здесь в обсуждении: параллельность + синхронизации (примеры).

По цифрам достаточно хорошо видно, что:
- Реакция реализации на fork() запаздывает сравнительно с реализацией потоками. Но это связано только с затратной самой операцией создания клона процесса fork() - вся последующая обработка равнозначна по эффективности...
- Это хорошо подтверждают результаты с pre-fork - здесь разница потоки vs процессы практически нивелировалась.

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


Тема поднималась пользователем Olej 11 апр 2015, 17:05.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 11 гостей