$data = file_get_contents($location);

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

Хорошо, предположим, что у вас есть 3 web frontend’а, каждый из которых может обслуживать 60 параллельных запросов (итого 180 конкурентных запросов). На минутку предположим, что вышеприведенный код — это единственный код, который выполняется при обработке запроса. Если location указывает на локальный ресурс (на локальной файловой системе), то вряд ли что-то может пойти не так. Latency чтения с локальной файловой системы достаточно низкий для того, чтобы не беспокоится о возможных проблемах (если файлы достаточно маленькие).

А что если location указывает на удаленный ресурс? Например, на какой-нибудь ресурс доступный по протоколу HTTP. Предположим, что ресурс недоступен. Как поведет себя ваше приложение?

В лучшем случае, вы моментально получите управление назад, сможете определить ошибочную ситуацию и каким-либо образом восстановить поток исполнения. Но так бывает далеко не всегда. В случае если недоступна физический машина (а не приложение на этой машине), или если на сервере некорректно настроен firewall, то вы не получите управление назад. Клиент отсылает TCP SYN запрос и… тишина. Некому вам сказать, что ресурс недоступен, и все что вам остается — это ждать.

Возможно у вас возник вопрос: “А как это меня касается? Я же не могу оживить удаленный ресурс”. Два момента.

Во-первых, пользователи не любят ждать. Особенно они не любят когда после ожидания им говорят: “извините, мы не смогли обработать ваш запрос”. Зачем было ждать ЭТО? Во-вторых, путем довольно нехитрых расчетов можно вычислить, что если скорость поступления запросов равна 30 запросам в секунду, а запрос обрабатывается дольше 6 секунд, то при 180 обработчиках у вас переполнится очередь входящий запросов и вы перестанете отвечать на эти самые запросы. А пользователи, как известно, не любят ждать. Числа, может быть и ничего не говорящие, но смысл в том, что они конечны, а следственно конечен и тот timeout превышение которого для вас может закончится отказом в обслуживании для всех новых запросов.

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

Поэтому реализуя системы которые имеют синхронные зависимости от 3rd party ресурсов необходимо соблюдать ряд правил:

  • если обращение к внешней системе никак не влияет на user experience пользователя и выполняет чисто служебные функции (например, логгирование статистики), то отличным вариантом будет замена синхронного вызова, на асинхронный (например, при помощи ActiveMQ и STOMP);
  • если асинхронная обработка невозможна, то необходимо корректным образом настроить connection и read timeout’ы. Это позволит ограничить то время, которое клиент будет ждать ответа от сервера и исключить возможность отказа в обслуживании;
  • если сервер на котором располагается ресурс находится под вашим управлением, необходимо проверить, что в iptables нет правил, которые осуществляют DROP пакетов. DROP правила, в отличии от REJECT, откидывают входящий пакет, без соответствующего ICMP-ответа клиенту о недоступности ресурса, что и провоцирует такие ситуации.

Помните, для того чтобы исправить ошибку, надо сначала обнаружить ошибку.