Предистория
Заказал я недавно одну вещицу из Англии к себе в родной Санкт-Петербург. Посылка по территории России шла EMS-отправлением. И каким же было мое удивление, когда я обнаружил, что по выданному мне трек-номеру (идентификатор отправления) я не могу отслеживать состояние посылки в автоматическом режиме. Обладая весьма нетерпеливым характером, я буквально каждое утро начинал с проверки состояния своей посылки. И еще больше огорчался, когда понимал, что она все там же, где и была 5 дней назад. Немного поразмыслив я решил, что это ужасное упущение, и решил исправить ситуацию своими силами.
Строим велосипед
Да, уважаемый друг, такие сервисы уже существуют, и наверно, даже работают, но ведь всегда так приятно сделать что-то своими руками. Тем более, на всех этих сервисах требуется регистрация, а в наше время заводить себе еще один аккаунт на сайте, который нужен 1 раз в год непростительное удовольствие.
Вот примеры существующих сервисов:
gdeposylka.ru — после регистрации обещают e-mail уведомления, за отдельную плату даже пришлют sms
post-tracker.ru — тут тоже обещают автоматические уведомления после регистрации
Начнем
Ну первым делом я конечно выбрал доменное имя, нужно было что-то такое крутое, звучное, остановился на: trackmypost.ru
На мой взгляд полностью отражает идею проекта.
Далее надо было найти площадку, нужна была нормальная виртуалка с рутовым доступом и возможностью все собрать сконфижить под себя, и мой выбор пал на hetzner.de.
Итог приятной части создания сервиса: у меня есть клевый домен и «машина». Остается написать сам сервис.
Для начала развернем nginx, php-cli, php-fpm, mysql, ssmtp и postfix. CLI будет выполнять самую грязную работу, из-под него будут работать 2 наших «демона». На самом деле это просто 2 скрипта запускающихся по крону каждую минуту. Первый будет собственно трекать почтовые отправления, а второй — рассылать почтовые уведомления.
Кстати об архитектуре, почему я разделил эти 2 скрипта: я не знаю, сколько писем там будет в очереди, как будет работать postfix и т.п. Поэтому, чтобы защитится от зависания всего сервиса, я разделил 2 логические операции: трекинг и отправку уведомлений. Кстати, при таком варианте гораздо удобнее собирать статистику и вообще мониторить, что происходит на сервисе.
Данные
Пришло время подумать о там как и г��е хранить данные. Самое простое хранилище это mysql. Не буду расписывать все поля, просто обозначу ключевые моменты, само собой для таблицы заданий на трекинг (назовем ее tasks) нам потребуется поле last_state (последний статус посылки) и bounce (кол-во неудачных попыток найти трек на сайтах) и bounce_date (дата последней неудачной попытки найти трек).
Трекинг
Общее
Первый скрипт, назовем его tracker.php делает запросы на сервера emspost.ru и russianpost.ru с целью проверки состояния уведомления.
Если сервера ответили, но трек номер на них не найден, то увеличиваем поле bounce текущего задания на 1 и выставляем bounce_date равным значению текущей даты. bounce_date позволяет на сегодня забыть об этом задании.
Для показателя bounce я реализовал такую логику:
- bounce == 1 Отправляю письмо с вопросом, не ошибся ли человек в трек номере.
- bounce == 7 Отправляю письмо о том, что трек не найден уже 7 день.
- bounce == 14 Отправляю письмо о том, что трек не найден уже 14 день.
- bounce == 21 Отправляю письмо о том, что трек не найден 21 день подряд и удаляю задание. Терпение кончалось.
Если трек найден на одном из сайтов и текущий статус != last_state высылаем уведомление о изменении статуса.
Если новый статус равен «Вручение адресату» то завершаем отслеживание трека.
Очередь на трекинг
Мне требовалось реализовать простую и равномерную очередь на трекинг, и я поступил так: при каждом запуске скрипта tracker.php выбирается и обрабатывается только 1 трек номер. После обработки этот трек записывается в файл last_track.txt. При следующем запуске скрипт выбирает id > значения ID из last_track.txt. Когда доходит до последнего начинает с начала.
Таким образом:
- 1. Сервис никогда не простаивает (т.к. tracker.php отрабатывает каждую минуту, если в базе всего 2 трека, каждый из них будет проверятся 30 раз в час, если 4 трека, каждый 15 раз в час и т.п.)
- 2. Нагрузка и на сервис и на сайты почты всегда равномерно ничтожна, потому что более 1 трек номера в минуту никогда не проверяется
Mailing
С почтой все совсем просто. На самом деле сам tracker.php не вызывает функцию mail(), он лишь добавляет письмо в mysql табличку mails. Которую уже разгребает запускаемый по крону второй скрипт: mailer.php.
На каждый свой запуск он просто выбирает следующее, не отправленное письмо, и пытается его отправить. Если функция mail() вернула true, отмечает его как отправленное. Если false и postfix поломался то это же письмо будет снова пытаться уйти через минуту.
Послесловие
FPM мне оказался нужен только для обработки формы добавления трека и выдачи статистики.
При написании класса для socket запросов не забывайте задавать timeout на чтение и запись, в php это делается примерно так:
fputs($fp, $data);
$result = '';
while(!feof($fp) && (!$info['timed_out'])) {
$result .= fgets($fp, 128);
$info = stream_get_meta_data($fp);
}
fclose($fp);
Потому, что, например, сайт Почты России грешит не закрытием коннекта даже при Connection: close.
Как обычно не забудьтем в nginx запретить keep-alive, установив его в 0 секунд.
Свои вопросы и комментарии Вы можете оставлять прямо тут, или на сайте проекта: trackmypost.ru =)