Ошибка работы с сокетами Битрикс не работает

Обновлено: 21.11.2024

Вы пробовали заказать в Макдональдсе поросенка жареного на аппарели с домашним вином и на десерт девушку рядом за столом, для приятной беседы во время еды? Даже не подумал об этом?? Вот именно — статья как раз об этом, о стереотипах программиста и лени, движущей прогрессом. А если серьезно — в статье мы за пару часов напишем очень полезный для многих высокопроизводительный сетевой сервер на PHP. Я совершенно серьезно :-)

В старые добрые времена.


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

Приходит лень.

Процессоры и потоки


Многие свято верили в аппаратную поддержку ООП на процессорном уровне, в 90-е этого не произошло. Но лень продолжает влиять и заставляет теперь твердо верить в эффективную реализацию потоков языка программирования — с учетом режима воспроизведения процессорных ядер. Те. Не хочу напрягаться и просто напишу "новая тема" и все будет работать качественно и быстро.

Тем временем.

Тем временем мир захватывают эффективные решения C в стиле nginx , создаются продуктивные решения NoSQL, которые близки к аппаратному обеспечению, и когда доходит до скорости, производительности - мозг помешанный на лени и рекламе начинает двигаться и чувствовать - что-то не то! Про нитки "врут" - работают не совсем эффективно, даже на многоядерных железах. Хотя теоретически - должны!

Забыли происхождение.

Спросите разработчика о разнице между закрытием и завершением работы или о разнице между процессом и потоком. и все чаще видишь, что значит выразительно грызть ногти :-) Но чтобы написать полезный сервер, нужно понимать, как работает операционная система, какие бывают сетевые протоколы, как устроена природа вещей а что такое настоящее пиво! :-)

Это не язык программирования

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

Обработка соединения

Люди, когда они были ближе к природе, умели эффективно обрабатывать сетевые сокеты. Само собой, это должно сделать ядро ​​операционной системы и уведомить вас о событии:
1) Пришел новый сокет в прослушивающее соединение (listen) — и его можно взять в обработку (accept)
2) Его можно прочитать из сокета, не блокируя процесс ( read ).
3) Вы можете писать в сокет, не блокируя процесс (write).

В мире физических законов другие способы обработки соединений, типа создания пучка нитей или, простите, иногда за счет лени и перфекционизма, процессов - работают медленнее и "потребляют" гораздо больше памяти. И хотя зачастую «отмазки» типа: «дешевле купить еще одну железяку, чем учить программиста асинхронно обрабатывать демультиплексированные сокеты» срабатывают, иногда попадаешь в ситуацию, когда нужно оперативно решить задачу на текущем оборудовании — снижение себестоимости на 1-2 порядка.
Именно по такому принципу работает всем известный nginx, обрабатывающий десятки тысяч соединений несколькими процессами операционной системы.

Для меня до сих пор остается загадкой, почему, несмотря на появление в той же java лет 10 назад библиотеки для решения серверных задач "в стиле nginx" - она ​​до сих пор не получила должного распространения и приложения продолжают "фиг на потоках, несмотря на всю тупиковость и расточительность этого подхода! :-)

Почему так не делают все?

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

PHP и сокеты


В PHP есть встроенная поддержка дорогих сокетов BSD. Но это расширение, к сожалению, не поддерживает ssl/tls.
Поэтому нужно лезть в интерфейс стримов - стримов, немного отчужденных от природы и ведущих здоровый образ жизни, наполненных абстракциями, "гоблинами и некроморфами". Если взять лопату и откинуть кучу шелухи, то можно увидеть за этим интерфейсом сетевые сокеты и достаточно эффективно работать с ними.

Кусочки кода

Ядро сервера

Простыми словами, мы заполняем ядро ​​сервера заданиями для работы с сокетами (например, заходим на сайты и собираем данные и т.п.) и ядро ​​запускает сразу сотни заданий в одном процессе.

Задача

Задача — это объект в терминологии ООП типа FSM. Внутри объекта есть стратегия — допустим: «зайти на этот адрес, создать запрос, загрузить ответ, парсить и т.д. вернуться в начало и конец, записать результат в NoSQL. Т.е. можно создать задачу от простой загрузки контента, до сложной цепочки нагрузочного тестирования с многочисленными ответвлениями - и все это, напомню, живет в объекте задачи.
Задачи в данной реализации задаются через отдельный контрольный сокет на порту 8000 - объекты json записываются в tcp сокет и далее начинают свое движение в ядре сервера.

Принцип обработки заданий и сокетов

Главное - не допустить блокировки серверного процесса в ожидании ответа в функции при чтении или записи информации в сетевой сокет, в ожидании нового подключения к управляющему сокету или где-то в сложных вычислениях/циклах . Поэтому все сокеты заданий проверяются в системном вызове select и ядро ​​ОС уведомляет нас только тогда, когда происходит событие (или по тайм-ауту).

Далее, когда произошло событие и нас уведомила ОС, мы начинаем обработку сокетов в неблокирующем режиме. Да, можно дополнительно оптимизировать обход массива задач, проиндексировать задачи по номеру сокета и выиграть 10мс — но пока. вы догадались, слишком ленивы :-)

Сам сокет тоже инициируется в неблокирующем режиме, важно установить флаги, оба! STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_CONNECT:

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

Теперь пишем запрос, определяя, сколько осталось.

После получения запроса читаем ответ. Важно понять, когда ответ полностью прочитан. Возможно, вам потребуется установить тайм-аут на чтение — мне это не понадобилось.

В последнем фрагменте мы можем иерархически направлять автомат в соответствии с нашей стратегией, реализуя различные варианты задания.
На момент написания класса задачи не покидало ощущение, что ты пишешь плагин для nginx ;-)

Результат


Вот видите, как просто и лаконично удалось решить проблему одновременной работы с сотнями и тысячами джобов и сокетов всего в одном PHP-процессе. А представьте, если мы поднимем для этого сервера сколько процессов PHP, сколько ядер на сервере — да, это тысячи обслуживаемых клиентов. И никакого сада с потоками и неэффективным переключением контекста процессора и повышенными требованиями к памяти. Серверный процесс PHP потребляет всего около 20 МБ памяти и работает как лошадь :-)

Обзор


Понимая, как ядро ​​операционной системы может обеспечить нам эффективную обработку сетевых сокетов, мы адаптировались к нему и внедрили высокопроизводительный PHP-сервер, обслуживающий сотни открытых сетевых сокетов за один процесс. При необходимости вы можете перекомпилировать PHP и обрабатывать тысячи сокетов в одном процессе.
Расширяйте круг своих знаний, не ленитесь под гнетом стереотипов — с помощью даже малотипизированных скриптовых языков можно создавать производительные серверы — главное уметь и не бояться экспериментировать: -) Всем удачи!

Читайте также: