Учебная модель
MapReduceдля обработки логов API
Небольшой учебный проект на C++, демонстрирующий идею MapReduce на примере обработки логов.
— это модель распределенной обработки больших данных, разбивающая задачу на два основных этапа:
- Параллельный Map (отображение/фильтрация)
- Агрегирующий Reduce (свертка)
Данная модель позволяет обрабатывать "петабайты" (с) данных, распределяя их по узлам кластера для одновременной обработки.
- Map (Разбиение): данные делятся на части, функция
mapобрабатывает каждую часть, превращая входные данные в промежуточные пары «ключ-значение». - Shuffle & Sort (Сортировка): промежуточные данные сортируются по ключу, чтобы все значения для одного ключа попали к одному редьюсеру.
- Reduce (Свертка): функция
reduceполучает ключ и список значений, агрегирует их (сумма, среднее и т.д.) и выдает итоговый результат.
timestamp,user_id,endpoint,status,duration_ms
Из всего этого набора нас интересуют только успешные запросы со статусом 200.
- сколько раз он встретился
- какое у него среднее время ответа
- какой был максимальный ответ
Endpoint Count Avg(ms) Max(ms)
-------------------------------------------------------
/login 3 126.67 150
/orders 3 243.33 260
/profile 2 85.00 90
(результат выполнения кода из src/main.cpp)
По сути здесь представлена ооочень маленькая реализация данной идеи, состоящая из таких этапов, как:
(0). Parse — разбор строки лога
Map— отсеивание битых записей и запросов с неподходящим статусомConsume— подсчет локальных статистикMerge— объединение промежуточных результатов
Потоки. Они здесь нужны затем, чтобы не обрабатывать весь массив логов строго последовательно.
В Run (запуске пайплайна) входной набор строк делится на несколько кусков, и каждый кусок уходит в свой std::thread.
Каждый поток независимо считает статистику по своему диапазону строк, а потом эти локальные таблицы объединяются.
Корутины. А они здесь решают более локальную задачу.
Map возвращает не готовый контейнер со всеми промежуточными значениями,
а генератор, который отдает их по одному через co_yield.
Это позволяет не создавать лишний временный массив объектов на каждый кусок входных данных.
MacBook :: ((somewhere)) >> cmake -S . -B build
MacBook :: ((somewhere)) >> cmake --build build
MacBook :: ((somewhere)) >> ./build/src/map_reduce