Skip to content

shvyrev/contest_fun

Repository files navigation

Приложение, состоит из двух инстансов.

  • нативное приложение для обработки пользовательских запросов
  • Java приложение для парсинга контента из источников Ни одно приложение не хранит состояние. Масштабируются горизонтально.

Обоснование такого решения привел в руководстве по эксплуатации

Выполненные пункты задания

Getting Started

Оба приложения разворачиваются в Docker.

Предлагаю воспользоваться Docker-compose для разворачивания. Для этого подготовил файлы с конфигурацией окружения :

core.env            // для приложения Core
parser.env          // для приложения Parser

в них прописываются все доступы к окружению (Infinispan, MongoDb, Minio, RedisBloom). Для запуска достаточно запустить скрипт :

$ up.sh

Архитектурное решение

Разделил приложение на две части.

  • сore - обработка пользовательских запросов
  • parser - получение контента от источников

Для дистрибуции Core приложения выбрал нативную сборку. Для Parser Jvm приложение.
Решение принял на основании тестов benchmark. И ограничений накладываемых на приложения запускаемые в Docker и нативные приложения.

Для кэширования данных выбрал Infinispan. Предлагает бинарный протокол и лучшее время работы в кластере.

Для персистентного хранилища выбрал MongoDB.

Для хранения медиа фалйов выбрал Minio.

Для проверки дубликатов выбрал Redis c фльтром Bloom.

Обоснование такого решения привел в руководстве по эксплуатации

Производительность

Benchmark

Приложение обрабатывающее запросы пользователей может запускаться в двух вариантах, как нативное и как JVM приложение. Тестировались приложения в двух этих режимах. В режиме JVM приложение обеспечивает лучшую пропускную способность и время отклика, чем приложение в нативном режиме. Однако использует на 277% больше RSS памяти. Теоретически возможно улучшить показатели пропускной способности нативного приложения в три раза, запустив несколько экземляров. Тестирование приложений продолжалось 3 часа, без перезапуска, нативное приложение обработало 33 млн запросов. Тесты проводились без прогревания.

Для тестирования использовался WRK:

$ core/sh/benchmark/wrk.sh

Для статистических данных AB:

$ core/sh/benchmark/ab.sh

Для определения памяти PS:

$ ps -o rss -p <PID>

####Startup Time Самый важный показатель для приложений развернутых в Docker. Оценивал время от запуска до первого запроса. Предлагаю оценивать этот показатель по следующей методике.

В отдельном терминале запускаем скрипт :

$ core/sh/performance/startuptime.sh

В другом терминале запускаем приложение :

$  date +"%T.%3N" &&  target/core-1.0-SNAPSHOT-runner 

В логах будет строчка :

В консоле будет вывод значения времени запуска приложения в милисекундах:

first timestamp : 17:13:26.536 final timestamp : 17:13:26.624
startup time : 188 milliseconds

####Memory usage Для измерения объема памяти измерения проводились не для Heap JVM, а для RSS памяти, включающей в себя Heap, метаданные, стек тредсов, компиляцию, утилизацию. В случае работы с Docker более показательно. Поскольку кубер будет убивать процесс на основе именно RSS памяти.
Среднее значение памяти в результате тестов нативного приложения было : 214 Мб

Для тестирования использовался скрипт:

$ core/sh/performance/rss.sh

Параллельная неблокирующая работа

Проиложение Parser написано с использованием фреймворка Vertx. Обеспечивающего паралельную работу потоков не в ForkJoinPool, а в собственной реализации потоков. Обеспечивающих работу воркеров в параллельных неблокирующих потоках. Даже при обращении в жесткому диску.

Для управления производительностью можно задать максимально количество потоков через конфиг приложения. Если этот параметр не определен, приложение будет использовать N+1 потоков, где N - число доступных процессоров.

Фильтрация дуликатов

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

  • фильтрация по тексту, на основе фильтра Блума
  • фильтрация по хешу от содежимого файла, на основе murmur_32 Фильтр Блума реализован на базе реализации RedisBloom filter, хеширование файлов на основе Guava Hashing.

Настройка источников мемов

Каждый источник мемов добавляется через конфигурацию приложения. Для примера приведу текущую конфигурацию

application.url = https://de.orschlurch.net                                     // url сайта
application.seedSelector = a[class=page-link]                                   // селектор для сидов
application.contentPageSelector = h4[class=card-title] > a                      // селектор адресов страниц
application.contentPageUrlPattern = https://de.orschlurch.net%s                 // паттерн адресов страниц
application.imageSelector = .row-pix img                                        // селектор списка изображений
application.videoSelector = div [class=container] > video[controls] > source    // селектор видео файлов
application.descriptionSelector = div [class=container] > p                     // селектор описаний
application.titleSelector = div [class=site-title] > h1                         // селектор названий
application.upsSelector = span.lnd                                              // селектор лайков
application.textLanguages = de                                                  // список языков 

Rest

В соответсвии с заданием:

endpoint GET /feed[?page=*]         // возвращает постраничный список контента
endpoint GET /feed/*                // возвращает отдельный контент

Задачи со звездочкой

Дополнительные языки

Список языков контент на которых может быть распарсен устанавливается в конфигурации приложения или через env переменные:

application.textLanguages = de,en                                                  // список языков 

Сортировку ленты от более интересного контента к менее интересному

Сортировка контента определяется в файле конфигурации. Для того, что бы изменить сортировку по времени на сортировку по лайкам достаточно заменить значение параметра "rest.feed.sort.field=timestamp" :

rest.feed.sort.field=timestamp              // заменить на ups

Противодействие rate-limiting

Использовал список простых HTTP проксей для парсинга. Он задается в конфигурации или через env переменные:

application.proxies = 43.246.140.4:43091, 124.122.156.168:8118

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

Критерии положительно влияющие на оценку жюри

Healthcheck и Readiness check

endpoint /health/live - приложение запущено и работает
endpoint /health/ready - приложение готово обслуживать запросы
endpoint /health - общий чек для двух состояний 

Endpoint с Prometheus-метриками

Для полчения метрик :

endpont /metrics/                   
endpont /metrics/application

Для запуска Prometheus использовал :

$ core/sh/prometheus.sh

Для конфигурации Prometheus использовал темплейт :

global:
  scrape_interval:     15s
  evaluation_interval: 15s

rule_files:
# - "first.rules"
# - "second.rules"

scrape_configs:
  - job_name: prometheus
    static_configs:
      - targets: ['localhost:9090']
  - job_name: core
    static_configs:
      - targets: ['localhost:8080']

OpenAPI-документация;

Реализованана через нотификации в коде приложения.

endpoint /swagger-ui - Swagger UI
endpoint /swagger - экспорт для клиентских приложений

Фишки

Fault tolerance

Добавил CircuitBreaker при обращении к кэшу. Ожидает прекращения доступа к InfiniSpan. И Retry, повторные попытки получения, если первые запросы завершились неудачно. Например при расхождении синхронизации Mongo и InfiniSpan.

Exception Notification

Добавил логирование ошибок в Sentry. Каждый exception который будет выкидываться в приложении, будет логирвоаться сортироваться по типу в Sentry.

About

Приложение для конкурса

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published