Этот пост не реклама моего проекта, а пример того, что дает человеку pet-project.


Я попробую рассказать, как, будучи тридцатипятилетним java-разработчиком, имеющим обязательства перед женой, маленьким ребенком и работодателем, сумел выделить время на самообразование и интересное хобби. Попутно расскажу, что я приобрел: какие сервисы, языки, фреймворки для себя открыл.


Сам проект представляет собой агрегатор цен online-магазина игр PlayStation Store. На данный момент с его помощью можно посмотреть, как менялась цена на игры в течение последних трех месяцев, увидеть список игр, которые обновили свои ценовые рекорды. В процессе разработки фича подписки на обновления цен.



Если интересно, добро пожаловать под кат.


Код в обеденных перерывах


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


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


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


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


К разработке приступал дважды. Сначала пытался создать приложение, разбитое на (микро)сервисы. Сделать это хорошо мне не удалось (возможно, из-за отсутствия промышленного опыта работы с микросервисной архитектурой). Я отказался от идеи сервисов и реализовал свое приложение монолитным. В качестве задела на будущее разбивал логические куски на java-пакеты. С этим подходом я и дошел в итоге до релиза.


Думаю, в первый раз мне помешало то, что я очень хотел сделать все идеально. Когда я переписывал проект заново, то поставил более достижимую цель — сделать MVP (minimum valuable product), — а дальше развивал проект итеративно, делая маленькие, но завершенные части.


Анатомия агрегатора


Если нарисовать FeedHandler широкими мазками, то получится такая схема:

Здесь элементы выделены тремя цветами: зеленый – внешние системы, от меня не зависящие (пользователи, сервис рассылки писем, Sony Store API), голубой – реализованные модули, которые можно посмотреть на сайте feedhandler.ru, и желтый – те части, реализацию которых я начал, но пока так и не закончил (как правило, они хотя бы запускаются на локальной машине).


Пойдем сверху вниз по схеме. В отладчике браузера я заметил, что PlayStation Store при отображении страницы обращается к своему API (valkyrie-api). В ответ на GET-запрос API возвращает простыню исчерпывающих данных про игру. При этом robots.txt выдает:


# we re-allow /valkyrie-api/ so that it will re-render properly.
# Disallow: /valkyrie-api/

Отлично, значит, и нам это можно использовать для сбора цен.


FeedHandler PriceCollector. Коллектор раз в сутки забирает данные из API PlayStation при помощи java-приложения и помещает их в базу данных. Java-приложение представляет из себя интеграционный флоу, написанный на Apache Camel, с которым я имел дело несколько лет, пока работал в страховой компании. Открыто говорю, этот фреймворк – моя любовь. Всегда, когда надо наладить интеграцию, пользуюсь им. Так как вначале я плохо представлял, какая схема будет у конечного продукта, я использовал schema-less базу данных — MongoDB. Это был мой первый опыт работы с ней, и мне очень понравилось.


MongoDB – первый продукт, с которым я познакомился в процессе работы над Feedhandler. Понимаю, что мне далеко до уровня эксперта в области докуменоориентированных баз данных, но был доволен, как получилось разбить задачу на несколько коллекций для разных целей: игры, статистика (агрегированные данные для отображения на сайте) и другие. Довольно интересно было обогащать существующие элементы или агрегировать данные для другой коллекции. В качестве визуального редактора понравился https://www.robomongo.org/.

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


RabbitMQ – второй продукт, с которым я познакомился. К сожалению, какой-либо экспертизы по этому продукту я не успел наработать и даже не знаю, как RabbitMQ ведет себя под нагрузкой. Осталось больше вопросов, чем ответов. Надеюсь, когда-нибудь еще удастся познакомиться с этим продуктом поближе.

FeedHandler Price API. Всё в том же монолите отдельным java-пакетом стоит REST-сервис, отвечающий за предоставление данных для сайта. Реализован он на spring-boot-starter-web. В планах было добавить туда кэширование, но на данный момент каждый запрос идет напрямую в базу.


Feedhandler UI. Сайт реализован на Nuxt.js (сначала написал на Vue.js, затем переделал на Nuxt.js). До этого пробовал Django, jsp, jsf и другие шаблонизированные web ui. Здесь хотелось посмотреть на что-то новое: либо react.js, либо vue.js. Выбор пал на второй. В целом, мне было довольно комфортно писать, хоть я и нисколько не UI-разработчик. Nuxt.js — еще более высокоуровневый фреймворк на базе Vue.js. Он освободил меня от ряда обязательств, например, от ручного конфигурирования путей.


Все модули со схемы собираются в образы и доставляются контейнерами. Давно был соблазн поработать с docker, и я воспользовался шансом.


Docker и docker-compose оказались идеальными помощниками. Такого удовольствия, пожалуй, давно не испытывал. Изоляция на уровне контейнеров чарует. На данный момент разворачиваю с помощью docker все, начиная от своих простеньких приложений, заканчивая домашним plex-сервером и Jenkins.

Отдельно на схеме выделены желтым те части, работа над которыми не закончена. Это все, что связано с личным кабинетом, где по плану пользователь мог бы подписываться на обновления цен и получать письма.


Model Synchronizer, Feedhandler Personal Account. API личного кабинета реализован на Django Rest — фреймворке. За этим API стоит база данных Postgres. API личного кабинета дергает все тот же Feedhandler UI, написанный на Nuxt.js. Обновление Django-модели происходит не напрямую из java-кода, а через celery.


Celeryproject — это очаровательный проект, который позволяет по RabbitMQ кидать таски в Python (и не только). В моем случае я кидаю события «новая игра», «удалить игру» и т.д. Это влияет на доступность подписки на изменения цен в личном кабинете пользователя. Базовая настройка в Django довольно простая.

Инфраструктура: CI, рассылка писем и другие тулзы. Дешево (бесплатно) и сердито


Свой код я исторически храню на GitHub. Проект FeedHandler разместил в приватных репозиториях.


Continuous Integration, CI. Для сборок мне идеально подошел GitLab CI. Из недостатков можно отметить то, что раз пять за год я встретился с недоступностью сервиса. Для моего проекта это не было критичным, зато у этого инструмента есть огромное преимущество в виде облачного Docker registry с весьма щедрыми лимитами. В заголовке проекта указано значение 1GB.


В GitLab CI есть ряд публичных worker’ов, которым он может выдать задачу собрать артефакт. Чтобы не беспокоиться, что мой код или пароли утекут, я пользуюсь персональным worker’ом на своем арендованном VDS. Образ таких сборщиков распространяется GitLab в виде Docker Image, который можно установить на свой сервер. Тогда схема сборки выглядит следующим образом:



Триггером является создание тега на master.


Почтовая рассылка. На схеме FeedHandler видно, что я пользуюсь сервисом почтовой рассылки. В качестве него я выбрал Mailgun. Я довольно много времени потратил на выбор, и Mailgun на тот момент обладал кучей преимуществ: во-первых, он был бесплатным. Во-вторых, в тарифный план входил удобный API, по 10000 писем и 100 валидаций в месяц. В интернете я нашел обзор 2015 года с сайта TexTerra, он мне показался наиболее полным и с грамотной оценкой сервисов. В целом, я прошелся почти по всему этому обзору (кстати, Maigun там не было), но ни один из предложенных автором почтовых сервисов мне не приглянулся: либо мало писем в стартовом пакете, либо нет API. В частности, MailChimp имел щедрый бесплатный тарифный план, но API предоставлял лишь за деньги.


На момент написания статьи заметил, что данного тарифного плана больше нет. На замену ему пришел тарифный план Flex, $0.8 за 1000 писем, что в целом тоже не дорого.

Issue and Time Tracker Из средств планирования мне больше всего понравился YouTrack от JetBrains. Имеется бесплатный облачный план на команды до десяти человек. Честно скажу, понравился этот сервис мне тем, что он больше всего напомнил Jira к которой я привык. Конечно, есть интеграция с IntelliJ IDEA.


Пример моей доски:



В процессе работы нашел отличный инструмент трекинга времени – Toggl. Самое главное не забывать выключать таймер. По результатам работы можно посмотреть интересную статистику, например, на что больше времени ушло (инфраструктура, backend, UI).


Мониторинг. Я использовал связку Prometheus (средство сбора метрик) и Grafana (знаменитое средство визуализации). Для того, чтобы своевременно получать информацию об ошибках, я добавил интеграцию с Slack. Я снимал несколько показателей: время request/response, количество ошибок в логе, количество обновлений цены.


Понятно, что эта система безопасности в некотором роде игрушечная — ведь расположена она на том же хосте, что и система, за которой она следила. В момент установки Prometheus мне так и не удалось указать псевдоним контейнера для снятия метрик – пришлось указать IP-адрес. Когда IP-адрес изменился, так вышло, что я не стал перенастраивать Prometheus, так как наблюдать за логами этой системы оказалось не очень интересно – мало что происходило.


Мой день в slack обычно выглядел так:



Nginx и безопасность. Активно пользовался nginx, раздавая веб-трафик наружу, некоторые порты закрывал для внешнего пользователя, позволяя достучаться до них только через VPN.


Коллега намекал, что мир шагнул вперед, и есть такая штука как Traefik. И он действительно выглядит очень перспективно, разруливать правила за счет меток на контейнерах — это классная идея. Но это тот продукт, на который я заглядываюсь, но пока побаиваюсь использовать в проде. Будет классно, если кто-то в комментариях расскажет про свой промышленный опыт работы с Traefik.

Также интересная штука – CDN. Я воспользовался CloudFlare. В бесплатном плане есть защита от DDoS атак, поддержка по email. Не могу рассказать о качестве защиты от DDoS — не сталкивался и не знаю, как она устроена. Для меня CloudFlare — это возможность спрятать реальный адрес виртуальной машины, где расположены все сервисы Feedhandler. Схема разруливания трафика имеет следующий вид:



Книги, из которых я черпал идеи


Это же самое приятное! Я слышал когда-то идею, что наша текущая точка зрения отражает книги, которые мы в данный момент читаем. Отчасти я согласен с этим. В этом разделе главное, что я хочу подчеркнуть, — это не «какой я молодец, вот сколько читаю» или «top-10 книг, которые надо прочесть». Я хочу сказать, что собственный проект – это возможность на лету реализовывать то, что читаешь в книгах. Понравилась идея? Надел походные сапоги и пошел шагать по бездорожью!


Надо отметить, что книги я не в обед читал, а в метро (еще до сами-знаете-чего).


Вот книги, которые я прочел за этот год эксперимента. Все они так или иначе отразились на проекте.


Release It!: Design and Deploy Production-Ready Software (Pragmatic Programmers) by Michael T. Nygard. По-моему, это часть золотого фонда книг про жизненный цикл программного обеспечения. Автор очень живо рассказывает про свой опыт (не всегда удачный) разработки архитектуры и поддержки программного обеспечения. После прочтения сразу хочется реализовать все эти патерны в своем продукте. Ну уж circuit breaker точно.


Mastering Spring Boot 2.0: Build modern, cloud-native, and distributed systems using Spring Boot by Dinesh Rajput Не могу сказать, что являюсь большим поклонником этой книги, но она отлично справляется с ролью дайджеста всех возможностей Spring Boot. По крайней мере после нее у меня уже не возникали вопросы о том, какие библиотеки spring-boot за что отвечают.


Clean Architecture: A Craftsman's Guide to Software Structure and Design (Robert C. Martin Series) by Robert C. Martin Слышал много споров о современных высказываниях дядюшки Боба. Сам в эту полемику вступать не буду, но скажу, что именно эта книга мне рассказала про некоторые архитектурные паттерны, которых я не мог до этого впитать из других источников, например, dependency inversion, CQRS и другие.


The Design of Web APIs by Arnaud Lauret так и не дочитал до конца, но впитал идею серьезного подхода к проектированию API. В частности, описывал спецификацию OpenAPI, прежде чем воплощать ее в коде. Не могу сказать, что сделал все по книге или ощутил какую-то завершенность или уверенность, но это было весело и здорово.


Итоги: много кайфа, интересных идей, и возможно, когда-то приду на собеседование со своим кодом


Что-то получилось, что-то нет.


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


Не смог убедить Twitter, что мне нужен доступ к Twitter API, получил отказ. Хотел в аккаунте публиковать новости по ценовым минимумам. Возможно, выберу альтернативную платформу типа Telegram.


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


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


Хотя, конечно, было бы лукавством говорить, что я смог это все сделать только за обеденные перерывы. После укладывания дочери бывали вечера, когда я тратил час-другой на свой проект, особенно в то время, когда меня зажигала какая-то задача. Например, когда я для себя открыл, насколько легко и непринужденно можно писать Telegram-ботов. Грех был не потратить хотя бы час на это дело. А книги вообще в метро читал.


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


Уверен, что смогу применить свои знания и наработки, опробованные на FeedHandler. Проект не брошу, планирую постепенно дописать его. Уже сейчас готова часть с пагинацией (UI, API), частично готов личный кабинет. Должна легко вписаться новая платформа PS5. Хотя я и не думаю, что смогу монетизировать этот проект, единственная выгода, которую я сейчас вижу, – смогу на следующие собеседования идти со своим ноутом, кодом и проектом.