Pull to refresh

Пишем веб-сервис за 1 вечер

Reading time 4 min
Views 4.1K

Предистория


Заказал я недавно одну вещицу из Англии к себе в родной Санкт-Петербург. Посылка по территории России шла 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 =)
Tags:
Hubs:
+161
Comments 80
Comments Comments 80

Articles