• Асинхронная многопоточность в PHP: для чего?

    Fesor
    @Fesor
    Full-stack developer (Symfony, Angular)
    Все очень просто. Вот вам приблизительное значение таймингов доступа к данным:
    io-cost.png

    То есть запросив данные в сети мы тупо ждем. Долго ждем и ничего не делаем.

    В случае с curl (он же HTTP) мы можем соорудить очередь запросов и послать их одним махом и ждать пока завершится загрузка всех документов в очереди для обработки результатов. Если мы хотим забрать 10 документов, то без multi curl у нас ушло бы времени "среднее время получения документа" * 10. И это примерно. В случае же с мультикурлом мы получаем время обработки 10 запросов как время выполнения самого долгого запроса. Если представить что время запросов всегда одинаковое, получаем выйгрыш примерно в 10 раз.

    С сокетами веселее. Они бывают блокируемые (по умолчанию) и неблокируемые (выставляется опцией O_NONBLOCK). Для начала давайте определимся что такое чтение данных из сокетов и как нам это дело предоставляет операционная система. Упрощенно, когда мы создаем сокет, мы просто просим операционную систему предоставить оный. У каждого сокета есть буфер чтения и буфер записи. Если буфер записи полный - ОС начинает отправку данных пока буфер не опустеет (буфер записи нужен для организации проверки дошли ли пакеты и переотправки в случае чего, так же этот буфер замешан в выборе операционкой размеров пакетов и т.д. Это не особо важно в контексте вопроса). Когда данные приходят в сокет, сначала они помещаются в буфер чтения. Там они лежат пока их не попросят вернуть из кода. Так мы можем быть уверены в том, что данные не пропадут.

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

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

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

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

    Еще есть такая чудная штука, которую предоставляет операционная система как select или epol (в контексте php socket_select и stream_select). Данные функции позволяют нам скармливать массивы сокетов, за которыми вы следите (не сокетов, а их дескрипторов но не суть, и не один массив а три, массив дескрипторов что бы следить появились ли данные на чтение, записал ли сокет все и освободился ли буфер записи и третий отслеживает сокеты в которых произошли какие-то ошибки, например отвалилось соединение). Так же этой функции можно задавать таймаут, что очень удобно если мы сначала собираем данные с нескольких клиентов и если от них небыло вестей пару секунд, значит мы забрали все и можно начинать обработку.

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

    Но все что выше имеет смысл только с TCP/TLS, если бы у нас были UDP сокеты, то было бы еще веселее. Там нету никаких буферов. Не принял данные - потерял данные. Нету соединений. Нету блокировок. Есть только пакеты. Поэтому этот протокол используют (или используют как основу) для реализации реалтайм систем. Задержек нету, а если какой пает не дошел, велика вероятность что он уже не актуален. Правда если сеть не надежная и потери пакетов велики, то начинается боль и слезы и обычно все же для таких случаев дублируют все на TCP.
    Ответ написан
  • Правильная структура бд. Нужна ли дополнительная таблица?

    cjey
    @cjey
    Попробуйте взглянуть на проблему с позиции удобства дальнейшего использования и доработки.
    В вашей формулировке можно не плодить дополнительные таблицы а просто добавить в DocumentType
    * учебный план
    * дошкольная программа
    * школьная программа
    * программа высшего учебного заведения
    * реферат
    * сочинение
    Но организовывать фильтрацию(например только программ) будет неудобно.

    Если в дальнейшем может появиться разделение рефератов на дошкольные/школьные и вы захотите фильтровать документы для школьников, то лучше делать так как вы предлагаете.
    Ответ написан
    Комментировать
  • Cтоит ли создавать отдельную таблицу для картинок в БД ?

    svd71
    @svd71
    На самом деле картинки - это такой ресурс, который лучше не колбасить движком без надобности. Запросто скажется на производительности. Намного лучше в базе в отдельной таблице хранить описание картинки(путь к файлу, заголовок и т.д. и т.п.), но картинку физически хранить на диске. С файловой системой операции показа картинок будут проходить намного быстрее.
    Ответ написан
    1 комментарий
  • Стоит ли писать свой php-фреймворк с целью улучшения знаний в области ООП и изучения шаблона MVC?

    @romeo7
    Читая Ваш вопрос,вспомнил себя 5-ей летней давности:) На тот момент мой бэкграунд состоял из дюжины сайтов на различных CMS и одного стартапа,который ясное дело не взлетел. Я к тому моменту долго вынашивал план по реабилитации одного замороженного проекта (спортивный портал),который разрабатывал со своими товарищами ещё в студенческие годы. Изначально задачи написать свой движок не было,но... всё началось с разработки шаблонизатора с синтаксисом а-ля CMS MODx. Много читал о том,что данная реализация выделяется на фоне остальных конкурентов,по сути являясь визитной карточкой последней. На поверку оказалось, что это всего навсего синтаксическая абстракция над Smarty,со всеми вытекающими по производительности. К примеру,моя реализация имеет альтернативную поддержку нативного php-шаблонизатора
    <?=$this->getSnippet('List', $params)?>
    для тех, кто терпеть не может синтаксические абстракции (Smarty, Twing, Fenom и иже с ними.) из-за их низкой производительности и иным религиозным соображениям.
    Шло время, кодовая база росла. От паттерна Singleton до DI (Dependency Injection), к Service Locator-у. Много чего выпилено в угоду существующих решений. За последние 2 года,не без помощи Composer,стало в разы больше готовых решений,причём несколько на реализацию конкретно задачи. К примеру,из последнего - был заменён собственный файловый менеджер (манипуляция с файловой системой) на библиотеку Flysystem, ибо последняя помимо всего прочего, умеет "бегать" в облака. Круто же ;) Единственное,в моей реализации была возможность поиска по regexp-паттерну,пришлось писать абстракцию.

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

    Вот сходу антипример. Достаточно популярная библиотека для валидации данных Respect Validation. Требовалось реализовать интернационализацию сообщений, не было возможности вывести иерархию выброшенных во время валидации ошибок одним большим массивом,а также хотелось вернуть,к примеру,только первую ошибку (first) или последнюю (last). Пришлось форкнуть,ибо абстрагироваться попросту невозможно.

    Совет: Иногда сталкиваешься с тем, что не все возможности какой-либо библиотеки задокументированы,даже если она достаточно популярна и имеет свой красивый "твиттеробутстраповый" сайт. Загляните в unit-тесты,возможно,откроете для себя что-то новое;)

    Если всё же надумаете писать свой фреймворк,то опирайтесь на существующие решения. Выберите для себя один-два популярных фреймворка и изучите,как реализован в них тот или иной функционал,хотя бы визуально,благо документации навалом. Начните с модели MVC. Посмотрите как реализованы actions,а именно доступ к ним,фильтрация на входе и выходе в action. Я,к примеру,не сторонник реализации псевдо-аннотаций,как в Symfony,ибо в PHP поддержки нативных аннотаций пока нет,а всё остальное - это жалкое подобие через медленные Reflections,даже если всё это кэшируется. Вот и автор АОП парадигмы для PHP всё это понимает, но всё равно продолжает разрабатывать свой проект Go!. Реализация конечно интересная,но я обойдусь событийной моделью (Observer или PubSub).
    Взгляните как устроен роутинг, ибо это основополагающая для реализации SOAP и REST. К примеру,моё решение испытало влияние Lavarel Routing по части использования групп. Нынче в силу распространённости мобильных платформ всё чаще используется REST не только на основе url-а,но и на основе кастомных заголовков (X-<имя прагмы>).
    Обратите внимания на то,как осуществляется обработка ошибок/exceptions. К примеру,Yii давно ругают на то,что нет возможности верной идентификации того или иного исключения. Лучше для каждой отдельной задачи (к примеру, FileManager) свой класс Exception,который наследуется от базового:
    class Exception extends BaseException
    {
        const FILE_EXISTS = 'File exists: {path}';
    
        public function __construct(
            $level = self::ERROR,
            $msg = null,
            array $dataReplace = null,
            \Exception $handler = null
        ) {
            return parent::__construct($level, $msg, $dataReplace, $handler);
        }
    }

    Тогда в базовом классе BaseException можно легко реализовать логирование (к примеру, воспользоваться Monolog) и красивую визуальную выдачу в режиме дебага (к примеру, Whoops).
    Желательно сразу избавить себя от привычки делать "жирные" контроллеры/actions. В этом может Вам помочь различные реализации валидации,фильтрации данных,а также задания дефолтовых значение на уровне модели. Обратите внимание на метод rules. Тогда в Вашем контроллере будет лишь метод отправки уже обработанных данных на вьюху.
    Что касается ORM и DBAL (синонимы: DAO и Query Builder), то в этом случае уж точно не стоит изобретать свой "велосипед". Написать по возможности единый интерфейс для различных реляционных и нереляционных решений (СУБД, Систем полнотекстового поиска/индексаторов (Sphinx, Elasticsearch)) - более чем нетривиальная задача. Я в своём фреймворке взял за основу AR (ORM) и Query Builder Yii2. Да,в Yii отсутствует модульность,а потому всё достаточно зависимо друг от друга,но если захотеть, то можно.
    Чувствуете этот момент. Вы препарируете почти готовое (прим. Yii2 еще в состоянии беты),одно из самых выдающихся на текущий момент решений и тем самым разбираетесь во всех тонкостях, попутно проявляете активность в исправлении ошибок.
    Научитесь писать unit тесты. Множество ошибок всплывут на поверхности,да и сон Ваш тогда будет более крепким.
    Вы наверно могли для себя заметить на том же stackoverflow или иных ресурсах, задаются достаточно тривиальные вопросы по фреймворкам. Вот и сейчас пока пишу Вам этот большущий ответ,в разделе "Похожие вопросы" красуется такой вопрос "Как реализовать правильно авторизацию с сессиями ... Отсутствует элементарная дисциплина к самостоятельности. И я даже догадываюсь почему так. Фреймворков стало больше,фреймворки стали лучше. Programmer Frendly,а не страшный зверь для избранных,как было когда-то. Правда,некоторые и по сей день недружелюбно скалятся;) Так или иначе,если есть желание задать вопрос из серии каким образом в JQuery сравнить две переменные,то стоит задуматься,а надо ли тебе всё это.
    Не в коем случае не нужно уверять себя в том,что Ваш инструмент взлетит. Он не уникален,и в конечном счёте скорее всего будет состоять из множества готовых библиотек (прим. в моём случае 60 против 40% вендорного кода. Если учитывать значимость,то в этом случае, уже счёт будет не в мою пользу). К сожалению, не могу найти ссылку на англоязычную статью, где автор сетует на полный отказ от фреймворков в пользу packagist. Даже если в уникальности вашего решения нет сомнения,это ничего Вам не гарантирует. Необходимо грамотное продвижение - множество статей на тематические ресурсах,а также участие в конференциях. К примеру, PHPDaemon хоть и стартовал первым, но пока вчистую проигрывает ReactPHP, а всего-навсего необходимо было уделить внимание написанию документации. Автор PHPDaemon, Василий Зорин,как-то на вопрос чем отчается его проект от React,указал на то,что последний использует его идеи. Конечно печально за нашего соотечественника,но его проект попахивает откровенным эгоизмом.
    Делайте Ваш инструмент прежде всего для себя,и возможно,когда-нибудь он станет интересен кому-то ещё. Так или иначе,Вы получите бесценный опыт. Главное,постараться довести это дело до конца. Отличительной чертой нашей ремесленной профессии является терпение. Вот Вам и проверка этого замечательного человеческого качества:) Кстати,чтобы интерес не угас,стоит свои наработки применять,если не в продакшене,то хотя бы небольшой проект для экспериментов.
    Заметил за собой,что пока занимался разработкой инструмента,гораздо больше получил опыта,чем на предыдущих двух работах. Но это субъективно. Можно с самого начала устроится в такое место,где замечательный отзывчивый коллектив и не менее интересные проекты/стартап, а не "натяни шаблон на Wordpress". Считаю,что пусть CMS-сника - это путь в никуда, и Кипелов здесь не причём:) Чем раньше, тем раньше;)
    Мне, как и sWinDos тоже забавно смотреть на свои исходники годичной давности:)) Вам знакомо такое понятие,как "Тезаурус"? В трактовке теории информации,это экспоненциальный рост знаний/опыта до какого-то предела, после которого, эффективность полученных знаний/опыта заметно падает. Получается этакая кривая Гаусса или что-то вроде жизненного цикла знаний/опыта в отдельно взятой предметной области.

    Совет: Запомните,Ваши проекты на github-е и контрибьюторская активность - это твёрдое, незыблемое портфолио. На собеседовании в большинстве компаний вы вправе выбрать свой сценарий поведения и темы для бесед.

    P.S. Я специально не стал затрагивать моральную и финансовую сторону вопроса. Opensource или заработок? Вечные поиски свободного времени между семьёй,работой и отдыхом. Смотрю здесь уже кто-то отметился:) Если Вы ещё студент и не обременены чем-либо,то дерзайте. Вполне возможно уже по окончанию университета или даже раньше, Вы выйдите уверенным таким коренастым мидлом:) К слову,в первой организации, с которой я начал свой профессиональный путь проповедовали процедурное программирование,ибо ООП никем не понималось должным образом.
    Ответ написан
    6 комментариев
  • Как быстрее выбрать изображения из БД (mysql php)?

    AMar4enko
    @AMar4enko
    Отличные советы вам дал человек в очках. Храните изображения в базе, доставайте их оттуда скриптом, из скрипта отдавайте! Зачем нам реверс-прокси, энджиниксы всякие.
    А по поводу вашего вопроса - самое правильное, это взять PostgreSQL, где есть типизированный массив в качестве типа столбца.
    А если без него, то я бы сериализовал массив имен файлов и пихал их в поле типа text, потому что:
    - вероятней всего вы не будете использовать одно изображение в нескольких лотах
    - вы не будете выполнять поиск по названиям файлов изображений
    В этом случае вы избавлены от накладного запроса в БД для получения изображений - получили запись, десериализовали массив имен и все, можете отдавать.
    Ответ написан
    3 комментария
  • Как правильно организовать структуру хранения файлов и картинок на фтп сайта?

    Rpsl
    @Rpsl
    Кратко о себе
    md5 от файла и размазываем по директориям.

    /images/news/a/s/d/f/asdf....jpg

    Превьюшки вообще не хранить, а делать их на лету через nginx и класть в его кэш. В крайнем случае хранить в папке рядом с оригиналом - удалять будет проще.
    Ответ написан
    1 комментарий
  • Как реализовать мониторинг HDD за аппаратным RAID контроллером STLab A-470 в Linux?

    Я конечно же не хочу вас обидеть но как можно это называть RAID контроллером (
    proc
    Ответ написан
    1 комментарий
  • Чем хорош Ruby?

    @Masterme
    Это как слепому объяснить, чем радуга лучше полутени.
    Как вы хотите чтобы вам это объяснили? Привели примеры из жизни, где питон берёт в рот, а руби справляется на отличненько? Такие примеры, конечно, есть, например, лямбды, но вы всё равно не въедете, если до сих пор не поняли, что такое семантика языка и почему она важна. Вы могли взять питон, взять руби, изучить каждый из них пару недель и сделать выводы. Вместо этого вы ждёте пока вам в мозг положат готовое решение. Этого не произойдет. Вам скорее хуй в рот положат, так же как если бы вы были слепы - вам не стали бы объяснять разницу между радугой и полутенью, а дали бы под дых и отобрали бы мелочь.
    Вы, тупицы, думаете, что познание языков программирования - это нечто внешнее, что можно повторить, сымитировать. Вам сказали - вы повторили - и вроде как знаете тот или иной ЯП. В действительности же, познание языков программирования - это большая внутренняя работа, которую человек проделывает с абстракциями в своей голове. И только если человек способен к абстрактному мышлению - только тогда он, помножив эту способность на знание языков, может понять разницу между ними. Для остальных порог вхождения высок, и, честно говоря, я этому рад.
    Ответ написан
    9 комментариев