+ All Categories
Home > Documents > Управляемый Code injection (Михаил Якшин)

Управляемый Code injection (Михаил Якшин)

Date post: 05-Dec-2014
Category:
Upload: ontico
View: 966 times
Download: 0 times
Share this document with a friend
Description:
 
47
Управляемый code injection: как мы считаем все пользовательские отчёты за один проход в системе интернет-статистики Openstat Михаил Якшин http://www.openstat.ru/ [email protected]
Transcript
Page 1: Управляемый Code injection  (Михаил Якшин)

Управляемый code injection:как мы считаем все

пользовательские отчёты за один проходв системе интернет-статистики Openstat

Михаил Якшинhttp://www.openstat.ru/[email protected]

Page 2: Управляемый Code injection  (Михаил Якшин)

О чём этот доклад

● Какие бывают отчёты в веб-аналитике?● Как эти отчёты реализуются традиционно и не

очень традиционно?● Как посчитать много отчётов в одном потоке?● Какую пользу из этого можно извлечь?

Page 3: Управляемый Code injection  (Михаил Якшин)

Веб-аналитика:взгляд с высоты птичьего полета

Сайт

Счётчик

Отчёты

Логивеб-сервера

Логисчетчика

Page 4: Управляемый Code injection  (Михаил Якшин)

Типы отчётов

● Стандартные отчёты – предоставляются всем клиентам по умолчанию, не требуют никакой дополнительной настройки

● Хорошо параллелизуются и считаются на потоке● Нестандартные отчёты – требуют понимания

специфики задач клиента, дополнительной настройки или программирования

● Возникает проблема с параллелизацией и масштабированием

Page 5: Управляемый Code injection  (Михаил Якшин)

Типы отчётов

Стандартные:

● Популярные страницы

● Точки входа, точки выхода

● Источники трафика

● Браузеры

● Операционные системы

● Экранные разрешения

● Мобильные устройства

● География

● ...

Нестандартные:

● Разделы сайта

● Характеристики посетителей

● Популярные темы и комментарии в форуме

● Продажи интернет-магазина

● Каналы привлечения аудитории

● SEO

● ...

Page 6: Управляемый Code injection  (Михаил Якшин)

Тридцатисекундный экскурсв термины веб-аналитики

● Просмотры (Pageviews) – события загрузки страницы сайта посетителем

● Визиты/сессии (Visits/Sessions) – последовательность обращений посетителя к сайту, интервал между которыми не превышает 30 минут

● Посетители (Visitors) – пользователи, совершившие обращение к сайту и идентифицируемые некоторым образом (по IP, cookie и т.д.)

Page 7: Управляемый Code injection  (Михаил Якшин)

Про термины в картинках

P = 14 S = 4 V = 3

Посетитель 1

t

Не менее 30 минут

Посетитель 2

Посетитель 3

Page 8: Управляемый Code injection  (Михаил Якшин)

Какие отчёты заказывают потребители веб-аналитики? (1)

Раздел P S V

Новости 4905 490 301

Пресс-релизы 3691 527 301

Магазин 4548 413 391

Техподдержка 2548 509 364

Файлы 4092 511 378

Page 9: Управляемый Code injection  (Михаил Якшин)

Какие отчёты заказывают потребители веб-аналитики? (2)

Пол →Раздел ↓

Мужской Женский Всего

Новости 80 15 95

Статьи 60 50 110

Магазин 50 100 150

На пересечении – один из показателей, например, P – число pageviews

Page 10: Управляемый Code injection  (Михаил Якшин)

Какие отчёты заказывают потребители веб-аналитики? (3)

Группа ↓ Источник →Товар ↓

SEO Поисковики Контекст Прямые переходы

Кабель

UTP 30 90 60 100

STP 40 20 0 740

Коннекторы

RJ45 80 150 300 30

На пересечении – один из показателей, например, V – число уникальных посетителей

Page 11: Управляемый Code injection  (Михаил Якшин)

Level 1: сегментация

”Сегменты” – механизм для построения статистики по характеристикам, которые:

● имеют относительно немного интересующих значений, например:

● пол: мужской / женский● география: Москва / остальная Россия / зарубежье● совершил ли посетитель целевое действие: да / нет

● не изменяются на протяжении визита

Page 12: Управляемый Code injection  (Михаил Якшин)

Пользовательские сегменты

● Сегмент – бинарная характеристика визита; визит либо относится к сегменту (1), либо не относится к нему (0)

● Сегмент задается в виде булевой функции, оперирующих полями лога, например:

● se_name=”Google”● geo_country=”RU” AND browser=”Firefox”

● Если хотя бы в одном событии визита функция вернула true, то весь визит относится к сегменту

Page 13: Управляемый Code injection  (Михаил Якшин)

Иллюстрация работы сегмента

”Google” ”Bing”

Условие: se_name = ”Google”(”посетители, пришедшие на сайт с Google”)

Page 14: Управляемый Code injection  (Михаил Якшин)

Иллюстрация работы сегмента

Условие: se_name = ”Google”(”посетители, пришедшие на сайт с Google”)

”Google” ”Bing”

Page 15: Управляемый Code injection  (Михаил Якшин)

Иллюстрация работы сегмента

Условие: se_name = ”Google”(”посетители, пришедшие на сайт с Google”)

”Google” ”Bing”

Page 16: Управляемый Code injection  (Михаил Якшин)

Иллюстрация работы сегмента

Условие: se_name = ”Google”(”посетители, пришедшие на сайт с Google”)

”Google” ”Bing”

Page 17: Управляемый Code injection  (Михаил Якшин)

Как это выглядитв веб-интерфейсе

Page 18: Управляемый Code injection  (Михаил Якшин)

Как это выглядит внутри в отчётеID счётчика Сегмент Провайдер S

1234567 0 Всего 51352

1234567 0 Ростелеком 5336

1234567 0 Корбина 2270

1234567 0 Билайн 1637

1234567 0 ... ...

1234567 1 Всего 1584

1234567 1 Ростелеком 31

1234567 1 Корбина 33

1234567 1 Билайн 104

1234567 1 ... ...

Page 19: Управляемый Code injection  (Михаил Якшин)

Схема процесса работы с сегментами

Сайт

Отчёты

ЛогиВычисление

битмаска сегментов

Битмаскисегментов

Расчётстандартных

отчётов

Расчётстандартных

отчётов

Page 20: Управляемый Code injection  (Михаил Якшин)

Level 2: пользовательские отчёты

Сегменты не годятся в том случае, если:● Нужно посчитать статистику по характеристике,

изменяющейся в течении сессии● например, посещаемый раздел сайта

● Число интересных значений характеристики исчисляется сотнями, тысячами, миллионами

● например, ассортимент товаров в интернет-магазине

Page 21: Управляемый Code injection  (Михаил Якшин)

Традиционный отчётна базе Apache Hadoop

● Создается код для нескольких job'ов, реализующих цепочку:

mapper → reducer [→ mapper → reducer → …]● На вход подаются все логи● Нужно следить за корректным запуском job'ов в

нужном порядке и передаче промежуточных результатов от одного job'а к другому

Page 22: Управляемый Code injection  (Михаил Якшин)

Level up: Hadoop + Cascading

Переходим от терминов map-reduce к использованию готовых примитивов:

● Function – преобразование 1 строчки в 0..N● Filter – проверяет условие, оставляет или нет 1

строчку● Aggregator – реализует итератор над группой:

initializer, iterator, finalizer● Buffer – предоставляет Java Iterator по группе

Page 23: Управляемый Code injection  (Михаил Якшин)

Cascading: пример

// Вычисляем названия разделов из URLpipe = new Each(pipe, new DeriveSections(), Fields.ALL);// Задаем группировкуpipe = new GroupBy( pipe, new Fields(F_COUNTER_ID, F_SECTION_NAME), new Fields(F_VIS_ID, F_SESSION_ID, F_ID));// Делаем агрегацию того, что нагруппировали pipe = new Every( pipe, new BuildVPSE());

Page 24: Управляемый Code injection  (Михаил Якшин)

Насколько сложно написать такой отчёт?

● Основной код: 3 строчки (на предыдущем слайде)● DeriveSections: ~25 строчек● BuildVPSE: ~200 строчек● Трудозатраты: порядка часа-два

(50-70% времени – написание тестов)● BuildVPSE – единожды написанный агрегатор,

применяется во всех отчётах

Page 25: Управляемый Code injection  (Михаил Якшин)

Чем плох такой отчёт?

● Писать отдельную задачу для каждого отчёта затратно

● Запуск отдельных задач на каждый отчёт - затратен● Поднимать с диска все данные всех счётчиков для

обработки одного отчёта - затратно

Page 26: Управляемый Code injection  (Михаил Якшин)

Как можно улучшить ситуацию?

● Очевидно – найти у отчётов что-то общее и попробовать объединить расчёт всех отчётов в один проход

● На первый взгляд – это кажется сложным

Page 27: Управляемый Code injection  (Михаил Якшин)

Плоское представление двумерных отчётов

Пол →Раздел ↓

Мужской Женский Всего

Новости 80 15 95

Статьи 60 50 110

Магазин 50 100 150

Раздел Пол P

Новости Мужской 80

Статьи Мужской 60

Магазин Мужской 50

Новости Женский 15

Статьи Женский 50

Магазин Женский 100

Новости Всего 95

Статьи Всего 110

Магазин Всего 150

Page 28: Управляемый Code injection  (Михаил Якшин)

Плоское представлениетрёхмерных отчётов

Группа ↓ Источник →Товар ↓

SEO Поисковики

Кон­текст

Кабель

UTP 30 90 60

STP 40 20 0

Коннекторы

RJ45 80 150 300

Группа Товар

Источник V

Кабель UTP SEO 30

Кабель STP SEO 40

Коннекторы RJ45 SEO 80

Кабель UTP Поисковики 90

Кабель STP Поисковики 20

Коннекторы RJ45 Поисковики 150

Кабель UTP Контекст 60

Кабель STP Контекст 0

Коннекторы RJ45 Контекст 300

... ... ... ...

Page 29: Управляемый Code injection  (Михаил Якшин)

Основная идея

Все подобные отчёты можно уложить в одну общую схему:

● Некая специфичная для отчёта функция-обработчик● Группировка● Агрегация

Page 30: Управляемый Code injection  (Михаил Якшин)

Схематично, в терминах Cascading

// Вычисляем пользовательскую функциюpipe = new Each(pipe, new UserFunction(), Fields.ALL);// Задаем группировкуpipe = new GroupBy( pipe, new Fields(F_COUNTER_ID, GROUP_BY_FIELDS), new Fields(F_VIS_ID, F_SESSION_ID, F_ID));// Делаем агрегацию того, что нагруппировали pipe = new Every( pipe, new BuildVPSE());

В этом коде нужно изменять только эти2 вещи!

Page 31: Управляемый Code injection  (Михаил Якшин)

Маленькая проблема №1: разные размерности набора полей группировки

● Для одномерного отчета:● (раздел)

● Для двумерного:● (раздел, пол)

● Для трёхмерного:● (группа товаров, товар, источник трафика)

Page 32: Управляемый Code injection  (Михаил Якшин)

Решение проблемы разных размерностей: вложенные Tuple

Конвертируем исходный поток, собирая и все поля из условия группировки в одно поле с вложенным Tuple:

● Было:[ счётчик, раздел, … ][ счётчик, раздел, пол, … ]

● Стало:[ счётчик, [раздел], … ][ счётчик, [раздел, пол], … ]

Единое поле, которому можно присвоить название и задать это название в условии группировки

Page 33: Управляемый Code injection  (Михаил Якшин)

Проблема №2: code injection

Нужно дать возможность выполнять на кластере в рамках первоначального преобразования строчки (Function в терминах Cascading) произвольный пользовательский код:

// Вычисляем пользовательскую функциюpipe = new Each(pipe, new UserFunction(), Fields.ALL);

Page 34: Управляемый Code injection  (Михаил Якшин)

Чем это плохо?

● Функция может быть тяжелой и затратной● Разумеется, проблемы с безопасностью● Плохая локальность и много пересчёта: отчёты

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

Page 35: Управляемый Code injection  (Михаил Якшин)

Решение в реальном мире

● Аналогично работе с сегментами, разделим процесс на 2 этапа:

Сайт

Отчёты

Логи

Вычислениепользовательской

функции

Вычисленныезначения

Расчётпользовательских

отчётов

Page 36: Управляемый Code injection  (Михаил Якшин)

Этапы расчёта: первый этап

● Производится как можно чаще (например, раз в час)● Расчёт значений вынесен в отдельный job – легко

контролировать injection, устанавливать лимиты используемых ресурсов, выделять по JVM каждому

● Пользовательский Java-код по необходимости на лету компилируется с помощью Janino

Page 37: Управляемый Code injection  (Михаил Якшин)

Этапы расчёта: второй этап

● Производится по необходимости● Как правило – за отчётный период – раз в сутки, в

неделю, в месяц и т.д.● Только выбирает и использует нужные заранее

вычисленные значения● Не имеет никакого code injection● Имеет гарантированную алгоритмическую

сложность и чётко прогнозируемое время выполнения

Page 38: Управляемый Code injection  (Михаил Якшин)

Обеспечение безопасности выполнения чужого кода

● Каждому счётчику – отдельный mapper ⇒ выделенный процесс JVM

● Выполнение кода с максимально урезанными правами под Java security manager

● Контроль за потребляемыми ресурсами средствами ОС

● В перспективе – можно реализовать полноценную виртуализацию отдельных JVM

Page 39: Управляемый Code injection  (Михаил Якшин)

Потенциальные угрозы

● Выполнение ”опасных” действий (открытие сокетов, локальных файлов, запуск процессов, доступ к среде)

⇒ SecurityException● Перерасход ресурсов JVM

⇒ JVM аварийно завершается с OutOfMemoryException● Перерасход ресурсов процесса ОС (процессорное

время)

⇒ ОС убивает JVM по исчерпании ресурса

Page 40: Управляемый Code injection  (Михаил Якшин)

Примерпользовательского кода

public class SectionFirstPathComponent extends RST { @Override public void process(TupleEntry in, CVMap out) { String pagePath = in.getString(F_PAGE_PATH); long flag = in.getLong(F_FLAG); if (pagePath.length() >= 1 && ((flag & (FLAG_PAGE_EXTERNAL | FLAG_NOT_PAGE_VIEW)) == 0L)) { int p = pagePath.indexOf('/', 1); if (p == -1) { out.put("section", pagePath.substring(1)); } else { out.put("section", pagePath.substring(1, p)); } } }}

Page 41: Управляемый Code injection  (Михаил Якшин)

Настройкапользовательского отчёта

Настройка самого отчёта, т.е.:● что группировать● что агрегировать● как агрегировать● как отображать результат в веб-интерфейсе

реализуется в виде простого YAML/JSON файла.

Page 42: Управляемый Code injection  (Михаил Якшин)

Пример описанияпользовательского отчёта

ecommerce.geo.region:

gb:

- geo.country

- geo.region

sum:

- order.total

cd:

- order.id

Заказывается GroupByпо 2 полям

Заказывается суммаполя order.total

ЗаказываетсяCOUNT DISTINCT

поля order.id

Page 43: Управляемый Code injection  (Михаил Якшин)

Полная схема работы системы

Расчётстандартных

отчётов

Сайт

Отчёты

ЛогиВычисление

польз.функции

Значения

Расчётпользовательских

отчётов

Вычислениепольз.

сегментовБитмаскисегментов

Расчётстандартных

отчётов

Page 44: Управляемый Code injection  (Михаил Якшин)

Что в итоге получили пользователи?

● Сегменты могут создавать все пользователи из удобного конструктора в веб-интерфейсе

● Продвинутые пользователи могут сами настраивать себе отчёты практически произвольной сложности, написав несколько строчек кода на Java и описав вид желаемого отчёта

● Менее продвинутым пользователям это поможет сделать служба внедрения

Page 45: Управляемый Code injection  (Михаил Якшин)

А что получили мы?

Общий объем пользовательских отчётов ежедневно: ● 100 мегабайт● 1.2 млн строчек● ~10000 отчётов

Page 46: Управляемый Code injection  (Михаил Якшин)

Наша экономия

● Обычный отчёт за сутки считается за время от ~3 до ~30 машиночасов* в зависимости от сложности

● Все пользовательские отчёты считаются за один проход длиной 32-35 машиночасов* в сутки

● 10000 x (3..30) = (30000..300000) vs (32..35)● Итого – экономия на 3-4 порядка

* корректнее – ядрочасов

Page 47: Управляемый Code injection  (Михаил Якшин)

Спасибо за внимание!

Вопросы?

http://www.openstat.ru/[email protected]


Recommended