Pull to refresh

Comments 78

Написано здорово!
Однако, асинхронная работа с сетью не всегда простая и очевидная. Для кого-то (например меня) больше применимы микропотоки. Я, например, написал веб-сервер на python с использованием greenlet + gevent. Выглядит как поток, работает как поток, а поток не создается! Код остается легкочитаем, но, в то же время, реальные потоки не создаются.
Для особо ленивых есть monkey patching — пишите обычное поточное приложение, затем применяете monkey patch, и, хоп, потоки больше не создаются! Магия просто.
Вообще интересно, когда же появятся эффективные потоки, которые раскидываются по железным процессорам? :-)
Они уже и так есть — проблема потоков в дорогом переключении контекста CPU (надо сохранить регистры и т.д.)
Легковесные потоки (то есть симуляция потоков на уровне runtime) сталкивается с не всегда оптимальным размером кванта времени для потока — на что runtime повлиять без патчей ядра ОС никак не может.

Есть ОС, в которых green threads реализованы на уровне ядра — но это все экспериментальные ОС, где используется один единственный runtime, который гарантирует, что green threads не поломают друг другу контексты.
Хочется чтобы потоки начали поддерживать не на уровне ОС, а на уровне железки, связывающей процессоры проводами и микросхемами:
— сохранение контекста и регистров
— переключение
— шедулинг
:-)
сталкивается с не всегда оптимальным размером кванта времени для потока

А я говорил о кооперативной многозадачности, когда вы их сами переключаете. В greenlet они именно такие.
Во, классно. Но кто нить может захватить же поток и начать мешать другим?
Да. Микропотоки не подходят для вычислений, а вот для сетевых приложений типа прокси, чтобы байты туда-сюда гонять — супер. Ну и monkey patching работает здорово: вы можете, по большому счету, использовать блокирующие функции, но они патчатся таким образом, что во время блокировки в одном микропотоке, могут работать другие микропотоки.
Как-то страшно :-) Обещали же эффективную работу сборщика мусора и jit — и где она, java иногда просто безобразно начинает залипать по неизвестным науке причинам. В C# видимо такая же беда — периодические конвульсии :-)

Там ниже synergy дал ссылочку где эрланг запускается вообще без операционки и быстро обслуживает сокеты, интересная темка, надо покопать.
Например, ядро Windows NT. Файберы уже тогда были. Совсем непопулярное ядро :)

Еще одна тема, которой сто лет в обед, — IO Completion Ports. Кстати, тоже давно в ядре Windows NT.
Эффективное раскидывание легких потоков по процессорам есть в Go и специализированных средствах типа Erlang'а. Кооперативная многозадачность (на самом деле не совсем) позволяет сделать это достаточно эффективно.
Согласен, асинхронная работа требует предварительного массажа мозга. Зато выигрыш в производительности просто огромный. Интересный пример привели, про автотрансляцию кода потоков в код без потоков, спасибо.
Я так понимаю, там используются сишные модули pecl для расширения PHP?
Ага, но оно того стоит :}
Тоже асинхронно мультиплексируете там или потоки эмулируете?
Мопед не мой, боюсь наврать — давно уже не приходилось.
Лучше позвать в топик TravisBickle.
Там ниже про эрланг пишут, которому ОСь не нужна и он еще круче чем select/pool работает с сокетами — интересно, может правда будущее за ним и нужно nginx срочно переписывать? :-)
UFO just landed and posted this here
Ну как же, libevent. Да и runkit желателен
UFO just landed and posted this here
если точнее libevent,
в данном посте автор показал, как самому реализовать libevent
точнее наверно работающее решение на php, использующее системный вызов select
Ох, зря вы упомянули MongoDB в контексте этой статьи :) Там всё очень плохо. На каждого клиента к mongos — 1 тред в mongos, 1 коннект mongos-mongod и ещё один тред в mongod, работа с сетью блокирующая. В итоге по 2-3 тысячи тредов на каждом mongod. Таски на мультиплексирование сети стоят аж с 2009 года.
Я много хорошего понаслылал про нее и поверил вот на слово :-) Уберу упоминание.
реклама — двигатель торговли (в данном случае продается поддержка и обучение)
если честно — там очень много подводных камней
Интересно, apache httpd решаться перейти на асинхронную модель когда нить? :-) Или будут жить в мире префорк-процессов и потоков.
Убрал про MongoDB от греха подальше ;-)
«тысячи сокетов»… эрланг стоит в сторонке и тихо хихикает
На С я за 100 строк сделаю обработку сокетов лучше, чем эрланг, поверьте :-) Вот если бы эрланг заменил собой ядро операционной системы и предложил что-то более эффективное чем select/pool!
ух ты, и правда круто, своя операционка какая-то у них прямо
реализована на коротинах
Говорят сыроватая и tcp-стек не очень и его хотят на эрланге переписать? Интересна конечно идея отказаться от ОС общего назначения, но линукс сколько лет писали и десятки миллионов строк кода.
А вот нет: stackoverflow.com/questions/3536459/erlang-c-and-erlang
Эрланг как и java использует виртуальную машину и соответствующие рунтайм-издержки неизбежны. Плюс — он слаботипизированный, отсюда затраты времени на обработку типов в рунтайме.

Т.е. скорее всего один процесс работающий с select/pool на С будет обрабатывать столько же сокетов, сколько эрланг на одном аппаратном сервере с N-процессорами. Просто на C это писать долго, а на эрланге — быстро и можно железом решить задачу, что есть путь, далекий от природы и здорового образа жизни :-)
Ну, это всё не мешает писать на Erlang видеостриминогвый сервер с 10Гб/с на одной машине www.highload.ru/2013/abstracts/1262.html

Опять же, вопрос в том, как именно нужно «обрабатывать»? Если считать с одного сокета и записать в другой то может и разницы нет.
Ваша статья — сплошной пиар мясоедения и потребления алкоголя, где обработка сокетов выглядит лишь заманухой, а не наоборот :)
Ха ха :-) На самом деле хотелось разбавить код чем-то полезным и приятным :-)
Спасибо. Я всё время хотел узнать, как разрабатывают Битрикс. И тут кое-что приоткрылось…
Да ну, в статье лишь цветочки. Битрикс разрабатывают суровые балтийские викинги, какой там макдональдс — исключительно здоровый образ жизни, свежий воздух, чистая вода, домашнее пиво, экспертные знания современных технологий! :-)
у большинство РНРников отношение к Битриксу времён 2005г больше отрицательное…
я конечно понимаю, что прогресс ушел вперед, но осадок говнокода остался
А я вот раньше с вдохновением смотрел на ZendFramework и другие навороченные ООПшные проекты на PHP — а сейчас пришел к тому, что на PHP будет работать быстрее, если писать в стиле старого доброго C и лишь при необходимости применять объекты, когда выхода нет и лаконично не получается решить. Нагромождение объектов как в ZF играет злую шутку в интерпретируемых языках с виртуальными машинами.
Работать-то будет быстрее (если не эмулировать ООП функциями), но будет ли быстрее разработка и внесение изменений?
Остальной код должен быть очевиден. Готов неясные моменты прояснить.
Цельный код легче воспринимается, а эти кусочки нужно каждый раз в голове сшивать. Конечно, пихать все исходники в статью не стоит. Я надеялся на какой-нибудь github/bitbucket.

Несколько вопросов:

1. Этот код давно был написан? Я не вижу ни одного public/protected/private.
2. Используется ли в коде $ar_ex?
3. Что посоветуете почитать по теме?
1. Очень недавно, «strict OOP», но не стал все копировать. Рекомендую там где нужно и полезно — ООП, но без фанатизма конечно.
2. Нет.
3. Книжки по юниксу и сети, типа www.unpbook.com
в первом же фрагменте

foreach ($this->jobs as $job) {


в отрыве от контекста уже не понятно
Итерация по массиву заданий/запросов/клиентов — чего угодно. Задания ставятся в массив отдельно, например я их ставлю через отдельный управляющий сокет по tcp — пишу туда строку.
UFO just landed and posted this here
Ну сейчас PHP уже стал языком общего назначения, как и python. Почему бы не использовать его возможности, тем более, что результаты получаются отличные. На С такую штуку точно лез 5 писать :-)
я пишу сервера на Си, такую штуку напишу за день-два…
libev в помощь — то вообще пара часов на написания любого сервиса (без реализации сложной логики)
Если не усложнять, возможно, согласен. У Стивенса в учебнике рабочий пример занимает пару страниц. Нужно правда с буферами, аналогично как в nginx, настроить удобную работу только. В PHP проще — строки :-)
У меня ощущение, что этот код можно было бы и на C++ написать с той же ясностью. Зачем здесь PHP?
просто для примера направления мышления :-)
хочется чтобы не тулили везде потоки, а посмотрел на задачу шире
В мире UNIX потоки совершенно не общеприняты (в отличие от Windows).

Чаще дробят по процессам, — надежнее, да и скиллы другие.

Хотя, по-хорошему, нужно проводить исследование, но думаю, я прав.
Не могли бы вы уточнить диаметр орудийного шомпола?
О, это исключительно дело вкуса. Если выбрать большой диаметр. поросенок будет выглядеть… ну не очень аппетитно и видимо состоять из 2 частей :-)
Был полностью согласен с автором до момента:
… даже на заточенном немного на другие задачи PHP написать аналогичный сервер — совсем просто
Но после этого возник: «А зачем? А зачем это делать на PHP» :) Не спешите минусовать, я свой, тоже кое что могу на PHP :) Но зачем делать такое на PHP? Этот язык не был заточен с самого начала на работу отличную от «обработать запрос, отдать ответ». Зачем делать на нем демонов и async networking? Что бы отгрести проблем?

Если статья чисто академического плана, то нет вопросов. А если это руководство к действию, то… :)
Ну так если у вас все остальное на PHP, то зачем сразу усложнять задачу. Сейчас PHP хорошо развит, почему бы его не использовать если это возможно и получается я бы сказал очень все быстро и экономично.
Ну так если у вас все остальное на PHP, то зачем сразу усложнять задачу.

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

Код был написан вполне себе хорошо, ревью делали все программисты группы, но ничего подозрительного с точки зрения PHP не находили. Вывод был печальный, утечка была при непонятых стечениях обстоятельств в С коде функций сокетов или ХМЛ парсера. Проблема решилась только с помощью Pythonа.

Так что, если в проекте реально понадобилась что-то типа асинхронной обработки запросов, а все программисты в группе исключительно пэхапешники, то у вас проблемы.
Я ловил меморилик на PHP при использовании curl вместе с ssl. Отказался от curl и переписал на чистых сокетах. Скорее всего утечка была в xml-парсере, можно через gdb попробовать зайти и глянуть где.
Да на С можно такую штуку написать, просто лень, если на PHP утечек не замечаю и работает быстр.
А я кажется нашел компромисс между «громоздкостью» С/С++, «тормозами» Python и «дзен-буддизмом» Erlang для решения подобных задач. Это язык D, на второй день чтения книги Александреску у меня получилось на нем сделать асинхронный TCP эхосервер на libev, а на четвёртый выделить его в модуль и «свернуть колбеки» с помощью фидеров. В итоге получилось что-то типа:

    void echo_handler(Stream stream)
    {
        stream.timeout = 5;
        while (stream.active)
        {
            try
                stream.write(stream.read());
            catch (Stream.TimeoutException e) {
                stream.close();
                writeln("Connection closed by timeout.");
            } catch (Stream.ClosedException e) {
                writeln("Connection closed.");
            }
        }
    }    


    auto loop = new Loop();
    auto listener = new TCPListener(loop, &echo_handler);
Язык просто супер, и похоже уже стал достаточно стабилен для серьёзного использования.
Итак битва начинается!
В углу ринга в красных шортах выступает… Streams!
Во втором углу ринга в синих шортах ему противостоит… Sockets!
Бой комментирует восьмикратный чемпион мира в легком весе… Libevent!
Раунды для Вас обявляет обворожительная… Pthreads!
Помощь в организации и проведении чемпионата заботливо оказал… EIO!
Не не, все проще, нафиг streams — сокеты, чисты родные любимые BSD-сокеты, против некроморфов и потоков :-)
сам-то в сырцы сокетов и стримов заглядывал?
пхпшных? а что там интересного может быть :-) А в ядре linux недавно искал кстати ответ на вопрос, может тебе интересно будет — как реализовано на уровне tcp закрытие сокета вызовом shutdown(SHUT_RD). С ходу не нашел, хотя по rfc такая конструкция смысла похоже не имеет :-)
Даже в джава можно использовать правильный не блокирующий фреймворк, PlayFramework например.
And what about Node.js?
Тесты нужны, если можно написать, но работает в реальной жизни плохо, то писать действительно не надо.
можно долго спорить о том, надо оно или не очень, просто бывают ситуации, когда задача ставится определенным образом и надо сделать «вот так», а не иначе. А по теме, сам недавно писал подобное на php для работы с web-sockets. При запуске сервер кушал всего 632148 байта, на каждое соединение тратится еще примерно 120000 байт. работает очень шустро. планирую прикрутить к нему волшебную библиотеку pthreads и посмотреть на что еще способен этот язык…
Спасибо за конструктивный пример!
Скажите, а куда в этом коде вставить вызов обработчика запроса, когда уже весь запрос из сокета прочитался?

///// command processing
///

Это?

Второй вопрос: если под обработкой подразумевается другой скрипт PHP который с некоторой вероятностью сожрёт память, фатально рухнет или превысит таймаут ответа — как такое вылавливать именно в этой реализации?

Третий вопрос: nginx при необходимости обработать запрос с помощью интерпретатора php передаёт входной поток в spawn-fcgi или php-fpm через сокеты или tcp, а потом ждёт от них поток ответа, который и возвращает клиенту. На уровне spawn_fgci/php_fpm обработка таких запросов выполняется в отдельных процессах. Количество этих процессов ограничено, а количество запросов на сервер nginx — нет, потому nginx выполняет шедуллинг. Я исходники не читал, могу ошибаться, но общая концепция мне видится именно такой, хотя, шедуллинг может делаться в spawn_fgci/php_fpm. В общем, в вашей реализации как сделать так, что запросов много, а обработчиков мало?
1) Вставить после:
////////// А тут мы уже считали ответ и начинаем его парсить

2) Обрабатывать ответ нужно тоже в неблокирующем режиме :-) Иначе смысла нет.

3) В нгинксе нет входных потоков и вообще потоков. Есть процессы — воркеры, работающие с буфферами. Каждый воркер грубо имеет описанную в моем коде архитектуру. Текущий код на PHP может держать к примеру 1000 запросов к 10 другим серверам, в точности как делает nginx в режиме обратного проксирования.
Sign up to leave a comment.