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

Сервис работает более девяти месяцев.

За это время:

  • Удалось охватить 21 крупнейший город России. Среди них такие, как Москва, Санкт-Петербург, Екатеринбург и Казань.
  • Получилось увеличить общее число станций метро с 65 до 346.
  • Увеличилось среднее число объявлений с 131.2 до 519.41 в день.
  • Была добавлена панель управления настройками.
  • Были добавлены боты для Telegram и Вконтакте. Они автоматически уведомляют подписчиков о новых объявлениях.

Далее по тексту я буду использовать слово сервис — как модуль SOA, а не web сервис целиком.

Я выбрал SOA архитектуру, потому что она давала возможность:

  • Использовать разные технологии для решения разных задач.
  • Разрабатывать сервис независимо от других.
  • Деплоить сервисы независимо от других.
  • Горизонтально масштабировать сервисы.

Можно было бы назвать это микросервисной архитектурой, но имелись небольшие отличия. Между сервисами используется обмен данными на основе «Общей базы данных» по протоколу MDBWP вместо привычного для микросервисов HTTP API и хранения данных у каждого сервиса в своей БД. Такой подход был обусловлен быстротой разработки с возможностью сохранить все преимущества описанного SOA подхода.

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

В качестве базы данных была выбрана MongoDB. Эта документоориетированная БД отлично подходила для хранения объявлений с со списком станций метро, контактными данными арендодателей, а также описанием объявления.

На данный момент общая схема взаимодействия сервисов выглядит следующим образом:



Сервисы:




rent-view — сервис отображения объявлений и поиска по ним


github.com/mrsuh/rent-view



Сервис написан на NodeJS, т.к. самым важным критерием его качества была скорость ответа сервера пользователю.

Сервис обращается за объявлениями в MongoDB, рендерит HTML страницы с помощью шаблонизатора doT.js и отдает их браузеру.

Сервис собирается с помощью Grunt.

Для работы в браузере написаны скрипты на чистом JS, а стили на LESS. В качестве прокси сервера используется Nginx, который кеширует часть ответов и обеспечивает HTTPS соединение.

rent-collector — сервис сбора объявлений


github.com/mrsuh/rent-collector



Сервис собирает объявления, классифицирует их и записывает в БД.

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

Используется фреймворк Symfony 3.

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

С помощью beanstalk были сделаны 4 канала для обмена сообщениями:

  • parser — выделяет из текста такие факты, как тип объявления, цена, описание и ссылки. Чтобы ускорить обработку данных, я запустил несколько потребителей для этого канала.
    Примечание: потребитель общается с сервисом rent-parser.
  • collector — записывает обработанные данные о объявлениях в БД.
  • notifier — оповещает пользователей о новых объявлениях. Примечание: потребитель общается с сервисом rent-notifier.
  • publisher — публикует объявления в нескольких группах Вконтакте.

rent-parser — сервис классификации объявлений


github.com/mrsuh/rent-parser
Сервис написан на Golang.

Для извлечения структурированных данных из текста сервис использует парсер Tomita от Yandex. Осуществляет предварительную обработку текста и последующую обработку результатов парсинга.

Чтобы вы могли протестировать работу сервиса я сделал открытое API.

Попробовать парсер онлайн
Запрос:
curl -X POST -d 'сдаю двушку за 30 тыс в месяц. телефон + 7 999 999 9999' 'http://api.socrent.ru/parse'

Ответ:

{"type":2,"phone":["9999999999"],"price":30000}

Типы объявлений:
+ 0 — комната
+ 1 — 1 комнатная квартира
+ 2 — 2 комнатная квартира
+ 3 — 3 комнатная квартира
+ 4 — 4+ комнатная квартира
+ 5 — студия
+ 6 — не объявление

Более подробно о классификации объявлений я писал здесь habrahabr.ru/post/328282

rent-control — сервис управления настройками


github.com/mrsuh/rent-control



Он написан на PHP по нескольким причинам: знание необходимых библиотек для написания сервиса, а также высокая скорость разработки.
Используется фреймворк Symfony 3.
Библиотека стилей Bootstrap 3.

К настройкам, которыми управляет сервис, относятся:

  • объявления;
  • черный список;
  • конфигурации публикаций;
  • конфигурации парсинга.

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

rent-notifier — бот-сервис для рассылки новых объявлений в Telegram и Вконтакте.


github.com/mrsuh/rent-notifier

Пример подписки на объявления:



Сервис написан на Golang по причине критичности к скорости ответа пользователю.
Суть работы сервиса в следующем: вы подписываетесь на рассылку новых объявлений, а по мере добавления бот присылает вам о них сообщения. В текст сообщения сервис вставляет ссылку на оригинальное объявление.

Вспомогательные репо




Код для общей базы данных на PHP


github.com/mrsuh/rent-schema

Схема общей базы данных:



С добавлением сервиса rent-control появилось дублирование кода схем БД. Поэтому было принято решение вынести код в отдельный пакет. Теперь для любого сервиса на PHP достаточно добавить в зависимости этот пакет через composer.

composer require mrsuh/rent-schema


ODM для mongoDB


github.com/mrsuh/mongo-odm

Первой ODM для PHP MongoDB о которой я подумал была Doctrine 2. Она идет вместе с Symfony 3 и имеет хорошую документацию.

Но на момент написания сервиса, чтобы эта ODM начала работать с последней версией драйверов для Mongo PHP, необходимо было ставить еще один пакет в качестве прослойки между новым и старым API. Doctrine 2 сам по себе довольно большой проект, а с дополнительным пакетом становился еще больше. Вместо этого хотелось чего-то легковесного. Поэтому я решил сам написать ODM с минимальным функциональным набором. И у меня получилось — ODM полностью справляется со своими обязанностями.

Немного статистики




Сервис добавляет на сайт в среднем 519,41 объявлений в день.

Самыми популярными станциями метро, среди крупнейших городов России, оказались следующие:

  • Санкт-Петербург — Девяткино
  • Москва — Комсомольская
  • Казань — Проспект Победы
  • Екатеринбург — Уралмаш
  • Нижний Новгород — Автозаводская
  • Новосибирск — Площадь Маркса
  • Самара — Московская

Больше статистики можно посмотреть на самом сайте.

Заключение




Если вы еще не определились нужна ли вам SOA архитектура, то сделайте монолитное приложение с разбивкой на модули. Так будет проще перевести ваше приложение на сервисы при необходимости. Но если вы все-таки решите использовать SOA архитектуру, то должны понимать, что так может увеличиться сложность разработки, сложность деплоя, объем кода, а также объем сообщений между сервисами.

P.S. Последние две квартиры я нашел с помощью своего сервиса. Надеюсь, вам он тоже поможет.

Комментарии (10)


  1. myrkoxx
    04.12.2017 10:47

    Советую вам попробовать github.com/KnpLabs/KnpGaufretteBundle для абстракции над файловой системой. Удобнее будет потом с переездом куда нибудь (если планируеться или придеться).


    1. mrsuh Автор
      04.12.2017 10:52

      Спасибо, попробую.


    1. Fesor
      04.12.2017 12:31

      лучше уж FlySystem. Оно как-то удобнее.


      1. myrkoxx
        04.12.2017 21:49

        Спасибо, гляну


  1. VasikAlexey
    06.12.2017 00:23

    По теме не выскажусь, но огромное вам спасибище!


  1. Fesor
    06.12.2017 00:57

    Можно было бы назвать это микросервисной архитектурой

    Где та грань, когда наличие воркеров и очередей позволяет сказать что проект использует SOA архитектуру а не обычный монолит? Ведь если это все что требуется — значит подавляющее большинство проектов построено с использованием SOA.


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


    Вот если бы в рамках вашего проекта был еще сервис позволяющий публиковать объявления, то какие компоненты подвергнутся изменениям?


    1. mrsuh Автор
      07.12.2017 12:37

      Спасибо за развернутый комментарий.

      Очереди используются только в 1 сервисе и не служат основным инструментом для общения между сервисами (на начальном этапе очереди не использовались).

      Из 5ти сервисов можно выделить 2 полноценных микросервиса — сервис для бота и сервис классификации объявлений. Они могут работают полностью независимо от других сервисов по HTTP протоколу и один из них имеет свою БД (другому она не нужна).

      Действительно, остальные 3 сервиса общаются через БД и их можно было бы назвать монолитом, будь они написаны на одном языке и имея бОльшую связанность. Но в этом и есть суть SOA или микросервисного подхода:

      • использовать необходимые технологии по необходимости
      • независимо разрабатывать сервис
      • при необходимости горизонтально масштабироваться
      • функционировать независимо от других сервисов


      Например в книге «Шаблоны интеграций корпоративных приложений» одним из рассматриваемых способов связи между различными сервисами служит «Общая БД». Не думаю, что такое приложение можно назвать монолитом.

      Я мог бы сделать для каждого из этих сервисов свою БД и HTTP API, что обеспечило бы им большую независимость, но это отняло бы у меня больше времени.

      Поэтому я не могу назвать эту архитектуру микросервисной или монолитом, но вот сервис-ориентированной — да.


      1. Fesor
        07.12.2017 16:51

        и не служат основным инструментом для общения

        Очереди лишь для примера. Как таковой шины данных у нас нет, иначе нам нужно было бы как-то пулить данные что бы действия получать. У вас же просто есть 2 процесса которые работают с одной базой, и живут себе независимо. Одна часть грубо говоря пишет, другая читает. Это в целом можно назвать cqrs, если хотите (хотя тоже будет расплывчато) но не SOA и уж тем более микросервисами. Иначе любой проект где есть Cron и общая база может называться SOA.


        Из 5ти сервисов можно выделить 2 полноценных микросервиса

        Выходит так. Сервис разбора объявлений (который как я понимаю stateless) в целом можно так же отдельным микросервисом назвать.


        Но в этом и есть суть SOA или микросервисного подхода:

        ну там основной момент — это независимый цикл разработки сервиса, в частности деплоймент. А все что вы перечислили в целом и под монолит подходит. Разве что "функционировать отдельно"… Но тут такой момент — вспомним пример с cron, там тоже части работают независимо. Но мы же не называем это SOA.


        но вот сервис-ориентированной — да.

        в этом и суть, сегодня SOA это настолько широкий термин что в целом он не имеет смысла.


  1. PavelTuzov
    07.12.2017 12:16

    Особенно порадовали проектируемые станции метро в Екатеринбурге.


    1. mrsuh Автор
      07.12.2017 12:39

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