Статьи

Как ускорить потребление API вашего приложения

Вступление

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

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

Делать несколько запросов одновременно

Когда выполняется типичный PHP-скрипт, команды, введенные в код, запускаются одна за другой. Это кажется совершенно логичным, поскольку вы, вероятно, хотите получить результат предыдущей операции (например, запрос к базе данных или манипулирование переменной), чтобы перейти к следующему шагу сценария. То же правило применяется, когда вы делаете вызов API. Вы должны отправить запрос, дождаться ответа от удаленного хоста, а затем вы можете сделать что-нибудь с данными, которые вы получили. Но если ваше приложение выполняет несколько вызовов API и вам нужны данные из каждого источника, вам не нужно выполнять каждый запрос отдельно. Помните, что сервер, отвечающий за обработку вызовов API, готов работать с несколькими запросами одновременно. Вам нужно просто создать скрипт, который выполняет вызовы API параллельно, а не один за другим. К счастью, PHP предлагает набор функций curl_multi которые предназначены для этого.

Использование функций curl_multi похоже на выполнение типичных запросов в PHP с библиотекой cURL. Единственное отличие состоит в том, что вам нужно подготовить набор запросов для выполнения (а не только один) с помощью функции curl_init и передать их в функцию curl_multi_add_handle . Затем вызов функции curl_multi_exec выполнит запросы одновременно, а curl_multi_getcontent позволит вам получить результаты каждого вызова API. Просто прочитайте здесь, чтобы увидеть пример кода, который реализует описанную логику.

Если вы хотите использовать функции curl_multi в своем PHP-приложении, необходимо помнить несколько важных моментов. Прежде всего, функция curl_multi_exec займет столько времени, сколько самый медленный вызов API в наборе запросов, переданных функции curl_multi_add_handle . curl_multi образом, использование curl_multi имеет смысл в тех случаях, когда каждый из вызовов API занимает одинаковое количество времени. Если есть один запрос, который значительно медленнее, чем другие в наборе curl_multi , ваш сценарий не сможет двигаться, пока этот самый медленный запрос не будет завершен.

Также важно то, что вам нужно определить количество параллельных запросов, которые могут быть выполнены за один раз. Помните, что если ваш сайт обрабатывает большой трафик и каждый пользователь инициирует одновременные вызовы API на один удаленный сервер, общее количество запросов, выполняемых за один раз, может быстро стать большим. Не забудьте проверить ограничения, указанные в документации API, и узнать, как служба будет реагировать на них. Удаленный сервер может отправить определенный код ответа HTTP или сообщение об ошибке, когда вы достигнете пределов. Такие случаи должны надлежащим образом обрабатываться вашим приложением или помещаться в журнал, чтобы вы могли диагностировать проблему и уменьшить количество запросов.

Отдельные вызовы API от основного потока приложения

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

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

Чтобы реализовать такое решение, вы можете просто написать отдельный скрипт PHP и выполнить его с помощью функции exec() , так же как вы выполняете любое приложение командной строки. Различные платформы PHP часто предлагают модули, которые упрощают написание сценариев командной строки и позволяют легко интегрировать их с существующими моделями приложений или компонентами. Просто проверьте компоненты консоли Symfony2 или CakePHP, чтобы увидеть некоторые примеры. Различные платформы PHP — не только платформы — могут также предлагать инструменты, облегчающие написание сценариев командной строки, например WP CLI — интерфейс командной строки для WordPress.

Если вы ищете более мощный способ обработки вызовов API в отдельном процессе, рассмотрите возможность настройки сервера заданий, такого как Gearman . Сервер заданий — это комплексное решение, которое выполняет все действия, необходимые для разделения определенных задач ( jobs ) на независимые процессы. Прочтите статью Введение в Gearman Алирезы Рахмани Халили, чтобы узнать, как она работает и как ее реализовать в PHP. Если вы работаете на платформе Zend Server, вы можете использовать компонент Zend Job Queue, который предлагает аналогичные функциональные возможности. Его особенности и примеры использования описаны в статье Scheduling with Zend Job Queue, написанной Алексом Стеценко.

Независимо от того, какое решение для разделения вызовов API вы выберете, вы должны придумать, как разные части вашего приложения могут общаться друг с другом. Прежде всего, вы должны поместить данные, полученные от вызова API, в место (например, таблицу базы данных или файл), доступное для всего приложения. Вы также должны поделиться статусом выполнения отдельного скрипта. Основное приложение должно знать, выполняется ли внешний вызов API, уже выполненный, завершился некоторое время назад или произошел сбой. Если вы думаете о применении решения сервера заданий, оно, вероятно, предложит функциональность для мониторинга состояния задания. Но если вы просто хотите написать простой скрипт командной строки PHP, вам придется реализовать такую ​​логику самостоятельно.

Несколько HTTP-запросов или несколько потоков?
Итак, какое решение лучше — использовать функции curl_multi для одновременного выполнения нескольких HTTP-запросов или отделять вызовы API от основного потока приложения? Ну, это зависит от контекста, в котором запрашивается удаленный сервер. Вы можете обнаружить, что весь сценарий обработки вызовов API занимает много времени не только из-за запросов. Может также существовать обширный код, отвечающий за работу с полученными данными, особенно если он включает преобразование файлов или выполнение тяжелых записей в базу данных. В таких случаях использование функций curl_multi вероятно, будет недостаточно для ускорения работы вашего приложения. Запуск отдельного потока, отвечающего за всю операцию, наряду с обработкой данных, полученных с удаленного хоста, может привести к достижению лучших результатов с точки зрения производительности вашего приложения. С другой стороны, если вам нужно выполнить много простых вызовов API, которые не требуют большой обработки данных, придерживаться функций curl_multi , вероятно, будет достаточно, чтобы сделать ваше приложение быстрее.

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

Создайте интеллектуальный кеш-движок

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

Чтобы написать механизм кэширования, который работает должным образом и возвращает действительные данные, вам необходимо определить случаи, когда ответ от удаленного сервера не изменяется, поэтому нет необходимости каждый раз получать его. Вероятно, он будет отличаться в зависимости от конкретной службы API, но общая идея состоит в том, чтобы найти набор параметров (которые передаются в запросе), которые дают одинаковый ответ в течение определенного периода времени. Например, если вы выбираете ежедневные курсы обмена валют из удаленного сервиса, вы можете быть уверены, что курс обмена для данной валюты (который является параметром) остается неизменным в течение всего дня. Таким образом, ключ кеша для хранения данных, полученных от этого конкретного API, должен содержать как валюту, так и дату. Если вашему приложению в следующий раз придется получать этот конкретный курс обмена, вы можете ссылаться на данные, сохраненные в кеше (например, в базе данных или файле), и избегать HTTP-запроса.

Описанный выше сценарий предполагает, что ваше приложение берет на себя всю ответственность за изучение случаев, когда данные, полученные от удаленной службы, могут быть кэшированы, поэтому вам необходимо самостоятельно реализовать правильную логику кэширования. Но есть также случаи, когда служба API отслеживает изменения в данных, которые она разделяет, и возвращает дополнительные поля, содержащие метаданные, связанные с определенным ресурсом. Метаданные могут состоять из таких значений, как дата последнего изменения, номер редакции или хэш, вычисленный на основе содержимого ресурса. Использование таких данных может быть отличным способом повысить производительность вашего PHP-приложения, особенно при работе с большими объемами данных. Вместо того, чтобы извлекать весь ресурс каждый раз, когда вы подключаетесь к API, вам просто нужно сравнить временную метку или хеш со значением, которое вы получили в последний раз. Если они равны, это просто означает, что вы можете использовать данные, выбранные ранее, так как удаленный контент не изменился. Такое решение предполагает, что вы используете механизм кэширования в своем приложении, но вам не нужно беспокоиться о том, что данные, хранящиеся в кэше, действительны. Поскольку вы полагаетесь на метаданные, возвращаемые службой API, вам нужно только сравнить значения метаданных, предоставленные удаленным сервером.

Использование метаданных удаленных ресурсов может быть особенно полезным при использовании API службы размещения файлов. Работа с удаленными папками и файлами обычно означает передачу большого количества данных, что может привести к проблемам с производительностью. Чтобы дать вам пример того, как этого избежать, позвольте мне описать решения, используемые в API Dropbox. Служба Dropbox API возвращает конкретные данные, которые следует использовать для проверки изменения удаленных файлов. Прежде всего, метод метаданных (который возвращает информацию о папках и файлах, такую ​​как их имена, размеры или пути), содержит поле hash представляющее значение хеш-функции возвращаемого ресурса. Если вы укажете значение хеша из предыдущего запроса в качестве параметра нового запроса, а удаленные данные не изменились между запросами, API просто возвратит ответ HTTP 304 ( Not modified ). Drobox API также предлагает дельта- метод, который создается исключительно для информирования об изменениях в определенных папках или файлах. Использование значений хеш-функции и delta метода рекомендуется в документации API, поскольку это может значительно повысить производительность вашего приложения.

Последнее, но не менее важное: освоить документацию по API

Это может показаться очевидным, но в некоторых случаях тщательное чтение документации API может дать вам конкретные решения о том, как сделать вызовы API более эффективными. Использование API Dropbox, описанное выше, является очень наглядным примером. Но могут быть и другие способы уменьшить объем данных, передаваемых в ответе (например, выбрать только несколько определенных полей, которые должны быть возвращены API, вместо получения всего набора данных). Вы также можете проверить, не могут ли действия, которые вы выполняете в отдельных запросах, выполняться одновременно. Например, метод перевода API Google Translate (который используется для выборки переводов текста на разных языках) может возвращать более одного перевода в одном запросе. Передав несколько текстовых строк для обработки за один вызов API, вы можете избежать нескольких запросов, что, вероятно, приведет к экономии времени выполнения приложения.

Резюме

Как видите, есть много способов улучшить производительность приложения PHP, которое в значительной степени зависит от использования удаленных API. Вы можете выполнить несколько запросов одновременно — либо используя функции curl_multi либо запустив отдельные потоки приложения. Другое решение заключается в реализации механизма кэширования, который не позволит вам делать ненужные вызовы API или уменьшит объем данных, передаваемых между серверами. Наконец, методы, предлагаемые службой API, могут предоставить вам некоторые готовые решения для повышения производительности, например, выполнение нескольких действий в одном запросе.

Я надеюсь, что статья дала вам некоторое представление о том, как эффективно обрабатывать запросы API. Если у вас есть какие-либо комментарии относительно пунктов, представленных в статье, или любые другие советы о том, как ускорить работу с API, не стесняйтесь размещать их ниже. Вы также можете связаться со мной напрямую через Google Plus .