КулЛиб - Классная библиотека! Скачать книги бесплатно 

Грокаем стриминг [Нин Ван] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
грокаем
стриминг
Джош Фишер, Нин Ван

Ещё больше книг в нашем телеграм канале:
https://t.me/javalib

2023

Джош Фишер, Нин Ван
Грокаем стриминг
Серия «Библиотека программиста»
Перевел с английского Е. Матвеев

ББК 32.973.233-02
УДК 004.031.43

Фишер Джош, Ван Нин
Ф68 Грокаем стриминг. — СПб.: Питер, 2023. — 288 с.: ил. — (Серия «Библиотека
программиста»).
ISBN 978-5-4461-2306-3
Стриминговые системы позволяют сократить до минимума время между событием и обработкой информации, чтобы вы получали результаты в реальном времени. В приложениях для финансовой
сферы, в вопросах безопасности и IoT важна каждая миллисекунда, поэтому без стриминговых систем
не обойтись. А еще — это модно и приносит деньги ;) ! Неслучайно специалисты в Spark, Heron и Kafka
так востребованы.
Наконец, вы можете познакомиться с созданием стриминговых приложений и обработкой событий
в реальном времени не продираясь через технические подробности конкретных фреймворков, головоломные термины и сложные формулировки. Простой язык и яркие примеры позволят вам познакомиться
с базовыми концепциями, а чтобы усвоить описанные идеи и приемы, вы построите собственную простую
стриминговую программу с нуля.
От читателя не требуется опыт работы со стриминговыми системами. Примеры написаны на
языке Java.

16+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)
ISBN 978-1617297304 англ.
ISBN 978-5-4461-2306-3

©2022 by Manning Publications Co. All rights reserved
© Перевод на русский язык ООО «Прогресс книга», 2022
© Издание на русском языке, оформление ООО «Прогресс книга», 2022
© Серия «Библиотека программиста», 2022

Права на издание получены по соглашению с Manning Publications. Все права защищены. Никакая часть
данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения
владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как
надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не
может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за
возможные ошибки, связанные с использованием книги. Издательство не несет ответственности за доступность материалов, ссылки на которые вы можете найти в этой книге. На момент подготовки книги к изданию
все ссылки на интернет-ресурсы были действующими.

Изготовлено в России. Изготовитель: ООО «Прогресс книга». Место нахождения и фактический адрес:
194044, Россия, г. Санкт-Петербург, Б. Сампсониевский пр., д. 29А, пом. 52. Тел.: +78127037373.
Дата изготовления: 10.2022. Наименование: книжная продукция. Срок годности: не ограничен.
Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12 — Книги печатные
профессиональные, технические и научные.
Импортер в Беларусь: ООО «ПИТЕР М», 220020, РБ, г. Минск, ул. Тимирязева, д. 121/3, к. 214, тел./факс: 208 80 01.
Подписано в печать 12.08.22. Формат 70×100/16. Бумага офсетная. Усл. п. л. 23,220. Тираж 1300. Заказ 0000.

Оглавление

Предисловие . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Благодарности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
О книге . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Форум liveBook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
От издательства . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Об авторах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

ЧАСТЬ I. ЗНАКОМСТВО
Глава 1. Знакомство со стриминговыми системами . . . . . . . . . . . . . . . . . . . . . .
Что такое потоковая обработка? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Примеры событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Примеры стриминговых систем . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Стриминговые системы и реальное время . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Как работает стриминговая система . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Приложения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Серверные службы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Системы пакетной обработки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутри системы пакетной обработки данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Системы потоковой обработки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутри системы потоковой обработки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Преимущества многофазной архитектуры . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Многофазная архитектура в системах пакетной и потоковой обработки . . .
Сравнение систем . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22
23
23
24
25
26
26
27
29
30
31
32
33
34
35

6  Оглавление

Эталонная система обработки событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Упражнение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Глава 2. Привет, стриминговые системы! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Начальнику нужен современный пункт оплаты . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Вначале были запросы HTTP… и ничего не вышло . . . . . . . . . . . . . . . . . . . . . . . . . .
ЭйДжей и Миранда берут паузу, чтобы подумать . . . . . . . . . . . . . . . . . . . . . . . . . . .
ЭйДжей размышляет о стриминговых системах . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Сравнение серверных служб и потоковой обработки . . . . . . . . . . . . . . . . . . . . . . .
Очереди: фундаментальная концепция . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Передача данных в очередях . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Потоковый фреймворк (вернее, его начало) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обзор фреймворка Streamwork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Подробнее о ядре Streamwork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Основные стриминговые концепции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Подробнее о концепциях . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Последовательность выполнения стримингового задания . . . . . . . . . . . . . . . . .
Первое стриминговое задание . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Выполнение задания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ход выполнения задания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутри ядра . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Перемещение событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Жизненный цикл элемента данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Краткий обзор концепций стриминга . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38
39
40
41
42
43
45
46
47
48
49
50
51
52
53
59
60
61
65
66
67
68
68

Глава 3. Параллелизация и группировка данных . . . . . . . . . . . . . . . . . . . . . . . .
Датчик генерирует больше событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Даже в потоковых системах непросто добиться обработки
в реальном времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Новые концепции: параллелизм важен . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Новые концепции: параллелизм данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Новые концепции: независимость выполнения данных . . . . . . . . . . . . . . . . . . . .
Новые концепции: параллелизм задач . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Параллелизм данных и параллелизм задач . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Параллелизм и многозадачность . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Параллелизация задания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69
70
71
72
73
74
75
76
77
78

Оглавление   7
Параллелизация компонентов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Параллелизация источников . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Результат выполнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Параллелизация операторов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Результат выполнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
События и экземпляры . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упорядочение событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Группировка событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Случайная группировка . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Случайная группировка: внутренний механизм . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Группировка по значениям полей . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Группировка по значениям полей: внутренний механизм . . . . . . . . . . . . . . . . . .
Выполнение группировки событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Заглянуть в ядро: диспетчер событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Применение группировки по значениям полей в задании . . . . . . . . . . . . . . . . . .
Упорядочение событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Сравнение поведения группировок . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
96

Глава 4. Граф потока . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Система обнаружения мошеннических действий с кредитными картами . . . 98
Подробнее о системе обнаружения мошеннических действий
с кредитными картами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Процедура обнаружения мошеннических действий . . . . . . . . . . . . . . . . . . . . . . 100
Потоковая обработка не всегда прямолинейна . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Механизм работы системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Подробнее о задании обнаружения мошеннических действий . . . . . . . . . . . 103
Новые концепции . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Предшествующие и последующие компоненты . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Разветвление и объединение потока . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Графы, направленные графы и DAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
DAG в системах потоковой обработки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Все новые концепции на одной странице . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Разветвление потока к анализаторам . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Что происходит внутри ядра . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Проблема эффективности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Разветвление с несколькими потоками . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

8  Оглавление

Что происходит внутри ядра (еще раз) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Коммуникации между компонентами по каналам . . . . . . . . . . . . . . . . . . . . . . . . .
Несколько каналов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Объединение потока в агрегаторе оценок . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Объединение потоков в ядре . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Краткий обзор разновидности объединения потоков — соединения . . . .
Система в целом . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Графы и стриминговые задания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Примеры систем . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

114
115
116
117
118
119
120
121
122
123
124

Глава 5. Семантика доставки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Требования к задержке в системе обнаружения мошеннических
действий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Возвращаемся к заданию обнаружения мошеннических действий . . . . . . . 127
О точности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Частичный результат . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Новое стриминговое задание для контроля за использованием
системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
Новое задание контроля использования системы . . . . . . . . . . . . . . . . . . . . . . . . 131
Требования к заданию контроля . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
Новые концепции: количество доставок и обработок . . . . . . . . . . . . . . . . . . . . . 133
Новая концепция: семантика доставки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Выбор семантики . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
«Не более одного» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Задание обнаружения мошеннических действий . . . . . . . . . . . . . . . . . . . . . . . . . 137
«Не менее одного» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
«Не менее одного» с подтверждением . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Отслеживание событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
Управление сбоями при обработке событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Раннее обнаружение потерянных событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Код подтверждения в компонентах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Новая концепция: контрольные точки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Новая концепция: состояние . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Контрольные точки в задании контроля за использованием системы
для семантики «не менее одного» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Контрольные точки и функции управления состоянием . . . . . . . . . . . . . . . . . . 147
Код управления состоянием в компоненте источника транзакций . . . . . . . . 148

Оглавление   9
Ровно один или фактически один? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Вспомогательная концепция: идемпотентные операции . . . . . . . . . . . . . . . . . .
Наконец, «ровно один» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Код управления состоянием в компоненте анализатора
использования системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Повторное сравнение семантик доставки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Что дальше? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

149
150
151
152
153
154
154
154

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее . . . . . . . 155
Компоненты стриминговых систем . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Параллелизация и группировка событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
DAG и стриминговые задания . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Семантика (гарантия) доставки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Семантика доставки в системе обнаружения мошеннических действий
с кредитными картами . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Что дальше? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
Оконные вычисления . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Соединение данных в реальном времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Обратное давление . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
Вычисления с состоянием и без состояния . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

ЧАСТЬ II. ДВИЖЕМСЯ ДАЛЬШЕ
Глава 7. Оконные вычисления . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Сегментация данных в реальном времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Анализ задачи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Анализ задачи (продолжение) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Два разных контекста . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Окна в задании обнаружения мошеннических действий . . . . . . . . . . . . . . . . . .
Что именно называется окном? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Подробнее об окнах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Новая концепция: оконная стратегия . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Фиксированные окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Фиксированные окна в анализаторе оконного расстояния . . . . . . . . . . . . . . .
Обнаружение попыток мошенничества в фиксированном
временном окне . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Фиксированные окна: время и количество . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

168
169
170
171
172
173
174
175
176
177
178
179
180

10  Оглавление

Скользящие окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Скользящие окна: анализатор оконного расстояния . . . . . . . . . . . . . . . . . . . . . .
Обнаружение мошеннических действий благодаря использованию
скользящих окон . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Сеансовые окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Сеансовые окна (продолжение) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обнаружение мошеннических действий благодаря использованию
сеансовых окон . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обзор оконных стратегий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Разбиение потока событий на наборы данных . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Оконные системы: концепция или реализация . . . . . . . . . . . . . . . . . . . . . . . . . . .
Другой взгляд . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Хранилища пар «ключ — значение» . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Реализация анализатора оконного расстояния . . . . . . . . . . . . . . . . . . . . . . . . . . .
Время события и другие вехи . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Водяной знак окна . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Поздние события . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

181
182

Глава 8. Операции соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Соединение данных выбросов в реальном времени . . . . . . . . . . . . . . . . . . . . . .
Задание контроля выбросов, версия 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Преобразователь данных о выбросах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Точность становится проблемой . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обновленное задание контроля выбросов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Все внимание на соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Еще раз: что такое соединение? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Как работает стриминговое соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Cоединение (join) потоков — разновидность объединения (fan-in) . . . . . . .
События автомобилей и события температуры . . . . . . . . . . . . . . . . . . . . . . . . . . .
Таблица как материализованное представление стриминга . . . . . . . . . . . . . .
Материализация событий автомобилей менее эффективна . . . . . . . . . . . . . . .
Проблемы с целостностью данных . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Проблемы с оператором соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутреннее соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внешнее соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутренние и внешние соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

Оглавление  11
Разные типы соединений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внешние соединения в стриминговых системах . . . . . . . . . . . . . . . . . . . . . . . . . .
Новая проблема: ненадежное соединение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Оконные соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Соединение двух таблиц вместо соединения потока с таблицей . . . . . . . . . .
Снова о материализации представлений . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

216
217
218
218
219
221
222

Глава 9. Обратное давление . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Надежность критична . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обзор системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Усовершенствование стриминговых заданий . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Новые концепции: мощность, использование и резерв мощности . . . . . . . .
Подробнее об использовании и резерве мощности . . . . . . . . . . . . . . . . . . . . . .
Новая концепция: обратное давление . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Измерение использования мощности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление в ядре Streamwork . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление в ядре Streamwork: распространение . . . . . . . . . . . . . . . .
Стриминговое задание с обратным давлением . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление в распределенных системах . . . . . . . . . . . . . . . . . . . . . . . . .
Новая концепция: водяные знаки обратного давления . . . . . . . . . . . . . . . . . . .
Другой подход к управлению отстающими экземплярами:
отбрасывание событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Когда отбрасывание событий допустимо? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление как возможный симптом . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Останов и возобновление работы могут привести к пробуксовке . . . . . . . .
Решение проблемы пробуксовки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

223
224
225
226
227
228
229
230
231
232
233
234
239
240
241
242
243
244
245

Глава 10. Вычисления с состоянием . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Миграция стриминговых заданий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Компоненты с состоянием в задании контроля за использованием
системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
Снова о состоянии . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
Состояния в разных компонентах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
Данные состояния и временные данные . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Компоненты с состоянием и без состояния: код . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Источник с состоянием и оператор в задании контроля
за использованием системы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253

12  Оглавление

Состояния и контрольные точки . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Создание контрольных точек: сложность выбора момента времени . . . . . .
Событийный отсчет времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Создание контрольных точек с использованием событий
контрольных точек . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Событие контрольной точки обрабатывается исполнителями
экземпляров . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Путь события контрольной точки через задание . . . . . . . . . . . . . . . . . . . . . . . . . .
Создание контрольных точек с использованием событий контрольных
точек на уровне экземпляров . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Синхронизация событий контрольных точек . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Загрузка контрольных точек и обратная совместимость . . . . . . . . . . . . . . . . . .
Хранилище контрольных точек . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Компоненты с состоянием и компоненты без состояния . . . . . . . . . . . . . . . . . .
Ручное управление состоянием экземпляров . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Лямбда-архитектура . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Итоги . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Упражнения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Глава 11. Продвинутые концепции в стриминговых системах . . . . . . . . . . .
Это действительно все? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Оконные вычисления . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Основные виды окон . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Соединение данных в реальном времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
SQL и стриминговые соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Внутренние и внешние соединения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Неожиданности в стриминговых системах . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление: замедление источников или предшествующих
компонентов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Другой подход к управлению отстающими экземплярами:
отбрасывание событий . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Обратное давление может быть симптомом постоянной проблемы . . . . . .
Компоненты с состоянием и контрольные точки . . . . . . . . . . . . . . . . . . . . . . . . . .
Событийный отсчет времени . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Компоненты с состоянием и компоненты без состояния . . . . . . . . . . . . . . . . . .

254
255
256
257
259
260
261
263
264
265
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
282
284
285

У вас все получилось! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Ключевые концепции, рассмотренные в книге . . . . . . . . . . . . . . . . . . . . . . . . . 287

Предисловие

Когда я только начинал свой путь технического специалиста, мой наставник сказал мне: «Если ты хочешь выбрать что-то одно для развития карьеры, участвуй
в проектах с открытым исходным кодом». Все годы я хранил эту мысль где-то
на задворках памяти, но никогда не пытался следовать ей. Я думал: «Что я могу
создать такого, что будет полезно другим?». Во время работы в 1904labs я разработал ECO API для (на то время) Twitter Heron. Я исходил из потребностей
заказчика — и небольшой доли эгоизма; я действительно хотел написать этот
код и сделать его своим творческим вкладом в проект. Со временем компания
Twitter передала Heron в Apache Foundation, и меня пригласили войти в комитет
управления проектом Heron. Я заинтересовался этой возможностью, потому что
это был первый проект с открытым кодом, которым я занялся действительно
глубоко.
Год спустя, после сохранения главной ветви Heron, около 16 часов в понедельник, я получил письмо от Элеонор Гарднер (Eleonor Gardner) с темой
«Книга об Apache Heron, или Учебный курс». Пробежавшись по письму, я едва
не удалил его, приняв за розыгрыш. В конце концов, с чего бы кому-то желать,
чтобы я написал книгу или провел учебный курс? Но после обсуждения с Майком Стивенсом из Manning, и переписки с его заместителем Элеонорой я понял, что без помощи мне не обойтись. Я обратился к своему другу и коллеге по
Apache Heron Нину Вану в надежде, что он захочет написать книгу вместе со
мной. К счастью, он согласился — и это стало началом нашего долгого и плодотворного сотрудничества.
Сначала нам предлагали написать книгу, посвященную Heron. Но у Нина
были свои идеи относительно того, как сделать эту книгу лучше. В конце концов,
технологии быстро меняются и из-за качественных изменений в программном
обеспечении материал быстро устареет. Нам хотелось взять тему, выходящую
за рамки конкретных стриминговых фреймворков. Мы согласились написать

14  Предисловие

книгу, не привязанную к конкретному фреймворку, и изложить базовые концепции, чтобы читатель мог перейти к документации любого стримингового
фреймворка и сразу начать работу.
Итак, мы приступили к работе, используя только текст, после чего нам
с Нином «деликатно» предложили попробовать другой подход. Потом еще раз.
И еще. И еще. Мы поняли, что с иллюстрациями материал книги усваивается
намного быстрее. Первые схемы были нарисованы карандашом на бумаге и выглядели просто ужасно:

В ходе работы наши примитивные, нацарапанные от руки творения превратились в диаграммы, которые вы видите в книге. Мы с Нином разработали
их сами. Мы очень довольны тем, что у нас получилось, и надеемся, что и вы
оцените книгу по достоинству.
Джош Фишер, ноябрь 2021 г.

Благодарности

Прежде всего хочу поблагодарить своих детей и мою замечательную супругу
Мелиссу. Она самая терпеливая и прекрасная спутница, которую я мог только
пожелать. Она помогла справиться со всеми сложностями во время работы над
книгой. Мои дети — Эйден, Уэс, Холлин, Оливер, Деклан и Дилан — проявили
терпение и нередко обходились без меня по вечерам или утрам, когда я сидел
над книгой.
Спасибо тебе, Нин, за то, что ты оставался со мной в ходе работы. Возможность учиться у тебя была одним из самых положительных моментов в работе
над книгой.
Есть еще немало людей, которых я должен поблагодарить: Дэн Тамминелло
(Dan Tumminello), Дэйв Лодес (Dave Lodes), Лора Стоби (Laura Stobie), Джим
Тауи (Jim Towey), Стив Уиллис (Steve Willis), Майк Бэноси (Mike Banocy), Шон
Уолш (Sean Walsh), Паван Виерамакинени (Pavan Veeramachineni), Роберт Макмиллан (Robert McMillan), Чед Сторм (Chad Storm), Картик Рамасами (Karthik
Ramasamy) и Чандра Шекар (Chandra Shekar). Все они значительно повлияли
на меня — как в личном, так и в профессиональном плане.
Напоследок хочу поблагодарить Берта Бэйтса (Bert Bates). Несомненно, это
самый терпеливый, великодушный и вообще фантастический учитель из всех,
кого я знаю. Беки Уитни (Becky Whitney) всегда участвовала в обсуждениях,
которые бывали непростыми, но неизменно помогала нам не сбиться с пути,
чтобы у нас получилось то, что нужно Manning. Спасибо Майку Стивенсу (Mike
Stephens) за предоставленную возможность. Элеонор Гарднер (Eleonor Gardner)
организовала наше общение на этапе подготовки, и наконец, Энди Маринкович
(Andy Marinkovich) и Кери Хэйлз (Keri Hales) внесли завершающие штрихи.
Рецензенты: Андрес Сакко (Andres Sacco), Анто Аравинт (Anto Aravinth),
Анупам Сенгупта (Anupam Sengupta), Апурв Гупта (Apoorv Gupta), Бо Бендер
(Beau Bender), Брент Хонадел (Brent Honadel), Бриньяр Смари Бьярнасон
(Brynjar Smári Bjarnason), Крис Лундберг (Chris Lundberg), Чичеро Зандона

16  Благодарности

(Cicero Zandona), Дэмиен Эстебан (Damian Esteban), Дипика Фернандес (Deepika
Fernandez), Фернандо Антонио да Сильва Бернардино (Fernando Antonio da
Silva Bernardino), Иоханнес Лохманн (Johannes Lochmann), Кент Р. Спиллнер
(Kent R. Spillner), Кумар Унникришнан (Kumar Unnikrishnan), Лев Андельман
(Lev Andelman), Марк Рулло (Marc Roulleau), Массимо Сиани (Massimo Siani),
Матиас Буш (Matthias Busch), Мигель Монтальво (Miguel Montalvo), Себастиан
Пальма (Sebastián Palma), Симеон Лейзерсон (Simeon Leyzerzon), Саймон Сейаг
(Simon Seyag) и Саймон Верховен (Simon Verhoeven): ваши комментарии, вопросы и замечания сделали эту книгу лучше. Спасибо всем.
Джош Фишер, ноябрь 2021 г.
Два года! Я уже сбился со счета, кого мне следует благодарить. Эта книга никогда бы не увидела свет без тех людей, имена которых названы ниже, а также
многих других.
Прежде всего, я бы не смог завершить книгу без понимания и поддержки моей дочери. Я задолжал тебе выходные за два года, Синьи! Уже больше
двух лет я не навещал своих родителей Джили Ван (Jili Wang) и Сюцзюнь
Лю (Shujun Liu) и свою сестру Фен Ван (Feng Wang) в Китае. Я по ним очень
скучаю.
Большое спасибо моему соавтору Джошу. Какое же это было приключение!
Оно было бы невозможно без твоего творческого подхода и замечательных идей.
Я верю в потенциал обработки данных и благодарен за то, что мне довелось
работать со многими талантливыми инженерами. Многое из того, что я узнал
от этих людей, стало важным для книги: это Маосун Фу (Maosong Fu), Нэн
Лу (Neng Lu), Хуэйцзюнь У (Huijun Wu), Дмитрий Русаков (Dmitry Rusakov),
Сяояо Цянь (Xiaoyao Qian), Яо Ли (Yao Li), Чжэньсяо Ло (Zhenxiao Luo), Хао
Ло (Hao Luo), Майнак Гош (Mainak Ghosh), Да Чэн (Da Cheng), Фред Дай (Fred
Dai), Бэйнань Ван (Beinan Wang), Чуньсю Тан (Chunxu Tang), Жуньхан Ли
(Runhang Li), Ялян Ван (Yaliang Wang), Томас Купер (Thomas Cooper) и Фария
Калим (Faria Kalim) из команды Real-Time Compute в Twitter; Паван Патибандла (Pavan Patibandla), Фаршад Ростамабади (Farshad Rostamabadi), Курт
Норвуд (Kurt Norwood), Жюльен Дубо (Julien Dubeau), Кэти Нам (Cathy Nam),
Лео Чжан (Leo Zhang), Неха Бамбхани (Neha Bhambhani), Ник У (Nick Wu),
Робин Нейсон (Robyn Nason), Закери Миранда (Zachery Miranda), Джеффри
Ван (Jeffrey Wang) и Нирмал Утвани (Nirmal Utwani) из команды Data Pipeline
в Amplitude; а также многие другие участники сообщества Apache Heron.
Так как я впервые пишу книгу (да еще на английском!), я бы ни за что не
справился без той помощи, которую я получал от трудолюбивых редакторов
Manning. Огромное спасибо Берту Бэйтсу (Bert Bates), Беки Уитни (Becky
Whitney), Дженнифер Хаул (Jennifer Houle), Мэтью Спору (Matthew Spaur)
и многим другим редакторам и рецензентам, принимавшим участие в работе.
Я очень много узнал от вас!
Нин Ван, ноябрь 2021 г.

О книге

Книга «Грокаем стриминг» поможет вам разобраться в том, что такое стриминговые системы, как они работают и подходят ли они для ваших задач. Так
как книга написана без привязки к конкретному инструментарию, вы сможете
применить полученные знания независимо от того, какой фреймворк выберете. Мы начнем с основных концепций, а затем постепенно перейдем к более
сложным примерам, включая отслеживание в реальном времени счетчиков
событий от датчиков IoT и выявление мошеннических действий с кредитными
картами. Вы даже сможете поэкспериментировать с собственной стриминговой системой, для чего достаточно будет загрузить специально разработанный
для этой книги и предельно упрощенный стриминговый фреймворк. К концу
последней главы вы сможете пользоваться функциональностью стриминговых фреймворков и решать типичные задачи, возникающие при построении
стриминговых систем.

Для кого написана эта книга?
Мы написали эту книгу для разработчиков, имеющих хотя бы пару лет опыта
практического программирования и желающих расширить свои знания. Если
вы разрабатывали веб-клиенты, API, пакетные задания и т. д. и теперь хотите
двигаться дальше — эта книга для вас.

Структура книги
Книга имеет простую структуру: 11 глав разделены на две части; после того как
вы прочитаете по порядку главы с 1 по 5, изучайте остальные главы в любом
порядке на свое усмотрение. Краткое содержание глав:
zzВ главе 1 дан общий обзор стриминговых систем и их сравнение с други-

ми типичными разновидностями компьютерных систем.

18  О книге
zzВ

главе 2 рассматриваются фундаментальные принципы работы стриминговых систем.
zzВ главе 3 обсуждается параллелизация, группировка данных и возможности масштабирования стриминговых заданий.
zzГлава 4 посвящена потоковым графам и способам представления стриминговых заданий.
zzВ главе 5 рассматривается семантика доставки — например, возможности
использования стриминговых систем для надежной (и не очень) доставки
событий.
zzГлава 6 содержит обзор основных концепций, а также краткое содержание
дальнейших глав.
zzВ главе 7 рассматриваются окна — способы сегментации бесконечного
потока данных.
zzВ главе 8 обсуждаются стриминговые соединения, то есть слияние данных
в реальном времени.
zzВ главе 9 рассказано о том, как происходит обработка сбоев в стриминговых системах.
zzИз главы 10 вы узнаете, как стриминговые системы выполняют операции
с состоянием в реальном времени.
zzГлава 11 подводит итог второй части и содержит наши рекомендации
о том, как найти применение вашему интересу к стриминговым системам.

О коде
Главы 2, 3, 4, 5, 7 и 8 содержат примеры кода. Вы можете загрузить их по адресу
https://github.com/nwangtw/GrokkingStreamingSystems. Кроме того, исходный код
можно загрузить бесплатно на сайте издательства Manning: https://www.manning.
com/books/grokking-streaming-systems. Для запуска примеров вам понадобится
Java 11, Apache Maven 3.8.1 и программы командной строки Netcat или NMap.
Книга содержит множество примеров исходного кода — как в пронумерованных листингах, так и в строках с обычным текстом. В обоих случаях исходный
код выделяется моноширинным шрифтом, чтобы он отличался от обычного текста.
Иногда код также выделяется жирным шрифтом, которым обозначаются изменения по сравнению с предыдущими шагами, например при добавлении новых
функций в существующую строку кода. Во многих случаях исходный код был
переформатирован; мы добавили разрывы строк и изменили величину отступов
по ширине страниц книги.
Кроме того, мы часто опускали комментарии к коду в листинге, если код был
описан в тексте. Многие листинги сопровождаются аннотациями, выделяющими важные понятия.

О книге  19

Форум liveBook
Приобретая книгу «Грокаем стриминг», вы получаете бесплатный доступ к закрытому веб-форуму издательства Manning (на английском языке), на котором
можно оставлять комментарии о книге,задавать технические вопросы и получать помощь от авторов и других пользователей. Чтобы получить доступ к форуму, откройте страницу https://livebook.manning.com/book/grokking-streamingsystems/discussion/. Информацию о форумах Manning и правилах поведения на
них см. на https://livebook.manning.com/#!/discussion.
В рамках своих обязательств перед читателями издательство Manning предоставляет ресурс для содержательного общения читателей и авторов. Эти
обязательства не подразумевают конкретную степень участия авторов, которое
остается добровольным (и неоплачиваемым). Задавайте авторам интересные вопросы, чтобы они не теряли интереса к происходящему! Форум и архивы обсуждений доступны на веб-сайте издательства, пока книга продолжает издаваться.

От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com
(издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

Об авторах

Джош Фишер в настоящее время
руководит командой разработчиков
в 1904labs. Он работал над перемещением больших наборов данных
в реальном времени для сторонних
организаций, включая Monsanto
и Bayer.

Нин Ван — разработчик из Ampli­
tude, занимающийся построением
конвейеров данных реального времени. Он являлся одним из ключевых
участников проекта Apache Heron
в команде вычислений реального
времени Twitter.

Оба автора участвуют в проекте Apache и входят в комитет управления проектом для ядра распределенной потоковой обработки Apache Heron.

Часть I
Знакомство

В первой части книги вы с головой погрузитесь в мир стриминговых систем
и найдете ответы на такие вопросы, как «Почему стриминговые системы работают именно так, а не иначе?» и «Зачем вообще их использовать?». В главе 1
рассказано, чем стриминговые системы отличаются от любых других на высоком уровне. Глава 2 знакомит с азами стриминговых систем; в ней представлены
фундаментальные концепции их работы. В главе 3 рассматривается проблема
масштабирования таких систем, а глава 4 повествует о том, как данные перемещаются в стриминговых заданиях. Глава 5 рассказывает, как эти системы
обеспечивают надежную доставку данных в реальном времени, а глава 6 содержит обзор ключевых моментов каждой предыдущей главы. К концу части I вы
будете знать все необходимое, чтобы выбрать понравившийся фреймворк и без
промедлений приступить к работе.

1

Знакомство
со стриминговыми системами

В этой главе
99Общие сведения о потоковой обработке
99Отличия стриминговых систем от других систем

«Если бы не камни на дне, то не звучала бы песня потока».
Карл Перкинс

В этой главе мы попытаемся ответить на несколько основных вопросов, касающихся стриминговых систем: от вопроса «Что такое потоковая обработка?»
до «Для чего используются системы потоковой обработки, или стриминговые
системы?». Мы представим некоторые базовые концепции, которые рассмотрим
подробнее в следующих главах.

Глава 1. Знакомство со стриминговыми системами  23

Что такое потоковая обработка?
В последние годы потоковая обработка стала одной из самых популярных технологий работы с большими данными. К стриминговым системам относятся
компьютерные системы, обрабатывающие непрерывные потоки событий.
Важнейшая особенность потоковой обработки заключается в том, что события обрабатываются сразу (или почти сразу) после того, как они станут
доступными. Это делается для того, чтобы свести к минимуму задержку между
поступлением в систему исходного события и конечным результатом обработки этого события. В большинстве случаев задержка составляет от нескольких
миллисекунд до нескольких секунд, что можно считать реальным временем или
почти реальным временем; поэтому потоковая обработка также называется обработкой в реальном времени. Потоковая обработка обычно используется для
анализа разных типов событий. В результате стриминговые системы в разных
сценариях также могут называться «аналитика реального времени», «потоковая
аналитика» и «обработка событий». В этой книге мы используем популярный
в отрасли термин потоковая обработка.

Примеры событий
Вот несколько примеров того, что считается событием:
zzЩелчки мышью при работе на компьютере.
zzКасания и смахивания на экране мобильного телефона.
zzПрибытие и отправление поездов на станции.
zzОтправка сообщений и электронных писем пользователем.
zzТемпературные данные, поступающие с датчиков в лаборатории.
zzВзаимодействия

пользователей с веб-сайтом (просмотры страниц, вход
в систему и т. д.).
zzЖурналы серверов в центрах обработки данных.
zzТранзакции по всем счетам в банке.
Следует отметить, что для событий, обрабатываемых в стриминговых системах,
обычно не существует заранее установленного времени окончания. Их можно
рассматривать как непрекращающиеся; таким образом, поток событий часто
считается непрерывным и бесконечным. События встречаются на каждом шагу —
в буквальном смысле. Мы живем в информационную эпоху. В мире постоянно
генерируются, собираются и обрабатываются огромные объемы данных.

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

24  Часть I. Знакомство

Примеры стриминговых систем
Рассмотрим пару примеров:
zzПервый пример — система контроля температуры в лаборатории. Много-

численные датчики, установленные в разных местах, собирают данные
о температуре каждую секунду. Стриминговая система обрабатывает собранные данные и выводит информацию
на панель в реальном времени. Также система может
подавать сигналы тревоги при обнаружении аномалий.
Администраторы лаборатории используют систему для
наблюдения за всеми помещениями и проверки того, что
температура находится в заданном диапазоне.
zzВторой пример — системы мониторинга и анализа, которые обрабатывают взаимодействия пользователя с сайтом: просмотры страниц, авторизацию или нажатия кнопок. Когда вы заходите на веб-сайт, в логе обычно
регистрируется множество событий. Эти низкоуровневые события часто содержат множество полей данных,
поэтому сохранять их в исходном виде неэффективно. Кроме того, некоторые поля непонятны для человека, и перед тем, как их представить, их необходимо преобразовать. Стриминговые системы
чрезвычайно полезны для преобразования данных
низкоуровневых событий в более ценную информацию — например, в данном контексте это может быть
количество запросов, количество активных пользователей, просмотры
каждой страницы или подозрительное поведение пользователей.
В этих примерах стриминговая система может обрабатывать в реальном времени огромное количество событий и извлекать из них полезные данные.
Стриминговые системы очень актуальны, потому что события содержат массу
ценной информации и для многих сценариев важно обрабатывать ее в реальном
времени.

Глава 1. Знакомство со стриминговыми системами  25

Стриминговые системы и реальное время
Термином «стриминговая система» обозначается система, которая извлекает
полезную информацию из непрерывного потока событий. А конкретнее, как
упоминалось в начале раздела, стриминговая система должна обрабатывать события и генерировать результат после сбора данных как можно быстрее. И это
важно, потому что результаты становятся доступными с минимальной задержкой и своевременной реакцией. Благодаря тому что они работают в реальном
времени, стриминговые системы очень полезны во многих ситуациях — например, в лабораторных условиях и на сайтах, где желательно получать результаты
с минимальной задержкой.
Системы, отправляющие
данные стриминговой
системе

Системы, получающие
данные от стриминговой
системы

Стриминговая
система

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

26  Часть I. Знакомство

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

Сравнение четырех типичных компьютерных систем
У стриминговых систем много общего с другими компьютерными системами.
В конце концов, стриминговая система не перестает быть компьютерной. Вот
некоторые типичные системы, которые мы выбрали для сравнения:
zzПриложения.
zzСерверные службы.
zzСлужбы пакетной обработки.
zzСлужбы потоковой обработки.

Приложения
Приложение — компьютерная программа, напрямую взаимодействующая с пользователем. Программы, установленные на компьютере или смартфоне, являются приложениями. Калькулятор, текстовый редактор, видео- и аудиоплееры,
мессенджеры, браузер и игры — все это примеры приложений. Приложения
повсюду! Пользователи взаимодействуют с компьютерами при помощи разнообразных приложений.
Пользователи используют приложения, чтобы выполнять задачи. Вы можете
создать заметку или книгу в текстовом редакторе и сохранить ее в файле. Если
у вас есть видеофайл, его можно открыть и воспроизвести в приложении-проигрывателе. Браузер может использоваться для поиска информации, просмотра
видео и совершения покупок в интернете.

Что внутри
Приложения сильно отличаются друг от друга. Командная строка, текстовый
редактор, калькулятор, фоторедактор, браузер и видеоигра не похожи как по
внешнему виду, так и по поведению. Вы когда-нибудь рассматривали их как
программные продукты одной категории? По своему внутреннему строению
они различаются еще сильнее. Простой калькулятор можно реализовать в нескольких строках кода, а код веб-браузера или видеоигры может состоять из
миллионов строк.
Несмотря на все различия, основной процесс для большинства приложений
похож: начальная точка (в которой приложение запускается), конечная точка
(в которой приложение закрывается) и цикл (главный цикл) из трех шагов:

Глава 1. Знакомство со стриминговыми системами  27
1. Получение пользовательского ввода.
2. Выполнение логики.
3. Вывод результатов.
Начало

1. Получение ввода.

2. Выполнение логики.

3. Вывод результатов.

Конец

Серверные службы
Серверная служба — компьютерная программа, выполняемая скрыто от глаз
пользователя. В отличие от приложения серверная служба не взаимодействует
с пользователями напрямую. Вместо этого она реагирует на запросы и выполняет соответствующие задачи. Служба обычно является процессом с длительным
временем выполнения, и она постоянно ожидает входных запросов.
2. Веб-служба:
предоставь мне
объекты X, Y и Z.

1. Браузер: покажи
мне страницу XYZ.

Браузер

4. Веб-служба:
а вот и страница.

Вебслужба

3. Подсистема хранения
данных: вот они.

28  Часть I. Знакомство

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

Внутри серверной службы
Внутри серверной службы также есть основной цикл, но он работает по-другому,
потому что обрабатываемые службой запросы сильно отличаются от пользовательского ввода приложения. Так как с приложением чаще всего работает один
пользователь, проверки пользовательского ввода в начале основного цикла
обычно оказывается достаточно, но в серверную службу может в любой момент
поступить сразу несколько запросов. Чтобы запросы обрабатывались быстро,
в этом сценарии использования важно применять многопоточную обработку.
Программный поток представляет собой подзадачу, выполняемую внутри процесса; в контексте одного процесса могут существовать несколько потоков. Эти
потоки совместно используют ресурсы процесса (например, память) и могут
выполняться одновременно.

Начало

3. Воз
вр
и ожи ащение
дание
следую
щ
запрос его
а.

Ожидание
запроса

1. Поступае
т новый
запрос.

Создание потока
для обработки
запроса

2. Создание нового
потока для обработки
запроса.

Обработка
запроса

Глава 1. Знакомство со стриминговыми системами  29
Типичная служба работает по схеме, представленной на рисунке. При
получении запроса обработчик создает новый поток для выполнения реальной логики и немедленно возвращает управление, не дожидаясь результатов.
Вычисления, занимающие много времени (реальная логика), выполняются
параллельно в отдельном потоке. При такой схеме основной цикл выполняется
очень быстро, так что новые входящие запросы будут приниматься с максимальной скоростью.

Системы пакетной обработки
Как приложения, так и серверные службы спроектированы для обслуживания
клиентов (пользователей-людей или удаленных запросов) с максимальной быстротой. С системами пакетной обработки дело обстоит иначе. Они не должны
реагировать на любой ввод. Вместо этого системы пакетной обработки проектируются так, чтобы задачи выполнялись в запланированное время или когда
позволяет наличие ресурсов.
В реальной жизни системы пакетной обработки встречаются достаточно
часто. Например, на почте корреспонденцию собирают, сортируют, перевозят
и доставляют в запланированное время, потому что такая схема наиболее эффективна. Трудно представить систему, в которой сотрудник принимает ваше
письмо к отправке, выбегает из отделения и мчится доставлять его получателю.
Вообще говоря, это возможно, но крайне неэффективно, и для такого понадобятся очень веские причины.
В наши дни каждую секунду формируются все новые огромные объемы данных: статьи, электронные письма, взаимодействия с пользователями и данные
служб и устройств. Обработка таких данных и поиск полезной информации —
очень важная, хотя и достаточно сложная задача. Системы пакетной обработки
предназначены именно для этого.

Внимание!
Системы пакетной обработки
предназначены для эффективной обработки огромных объемов данных.

30  Часть I. Знакомство

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

Фаза
процесса
1

Входные данные

хранилище

хранилище

Фаза
процесса
2

Фаза
процесса
3

хранилище

Результаты

хранилище

Фаза
процесса
4

хранилище

В нашем примере входные данные обрабатываются группами, или пакетами
(пример — данные пользовательских взаимодействий на веб-сайте за каждый
час). При появлении новых данных (когда весь пакет получен и готов к обработке) запускается фаза 1 для загрузки данных и выполнения логики. Результаты
сохраняются в промежуточном хранилище, откуда они переходят на следующие
фазы. После того как данные пакета будут обработаны, фаза завершается и запускается следующая фаза (фаза 2 на приведенной выше диаграмме), на которой
ведется работа с промежуточными результатами, сгенерированными на фазе 1.
Обработка завершается после того, как пакет пройдет все фазы.

Глава 1. Знакомство со стриминговыми системами  31

Системы потоковой обработки
Архитектура пакетной обработки — исключительно мощный инструмент в мире
больших данных. Тем не менее у систем пакетной обработки есть один серьезный недостаток: задержка.
Системы пакетной обработки требуют, чтобы перед запуском данные собирались и сохранялись в пакетах с регулярными интервалами, например
ежечасно или ежедневно. Любые события, собранные в определенном временном окне, будут обработаны только после закрытия этого окна. В некоторых
случаях это может быть неприемлемо — например, если в системе наблюдения
в лаборатории сигнал тревоги сработает только через час. В таких случаях
предпочтительнее обрабатывать данные сразу же после их получения — чтобы получать результаты в реальном времени. Системы потоковой обработки
предназначены для сценариев, ориентированных на обработку в реальном
времени. В системах потоковой обработки события обрабатываются после их
получения как можно быстрее.
В качестве примера реальной системы пакетной обработки мы рассматривали почту. В этой системе корреспонденцию собирают, сортируют, перевозят
и доставляют несколько раз в день в запланированное время. Реальным примером системы потоковой обработки может быть сборочный конвейер на фабрике.
Процесс сборки тоже состоит из нескольких фаз, и конвейер постоянно работает
и получает новые детали. В каждой фазе операция применяется к одному продукту за другим. В конце со сборочной линии одно за другим выходят готовые
изделия.

Внимание!
Системы потоковой обработки
предназначены для обработки огромных объемов данных
с малой задержкой.

32  Часть I. Знакомство

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

хранилище

Executor

Executor

Входные
данные

Executor
хранилище

Результаты
Executor
хранилище

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

Глава 1. Знакомство со стриминговыми системами  33

Преимущества многофазной архитектуры
Системы как пакетной, так и потоковой обработки используют многофазную
архитектуру. Она обладает рядом преимуществ, благодаря которым хорошо
подходит для сценариев обработки данных:
zzГибкость — разработчики могут добавлять или исключать фазы по своему

усмотрению.
— фазы соединены, но каждая из них существует
независимо от других. Если одна фаза становится «узким местом» всего
процесса с существующими экземплярами (с 1-го по 3-й на следующей
диаграмме), проще подключить дополнительные экземпляры (с 4-го
по 5-й) для повышения пропускной способности.
zzСопровождаемость — сложные процессы могут состоять из простых
операций, которые создают меньше проблем с реализацией и сопровождением.
zzМасштабируемость

фаза

фаза

входное
хранилище

входное
хранилище

фаза
экземпляр
1

экземпляр
2

экземпляр
3

входное
хранилище

фаза

экземпляр
4

экземпляр
5

вится
стано ожно
а
з
а
м
ф
Если
том»,
м мес ть новые
и
к
з
у
и
«
добав
легко ляры.
п
экзем

34  Часть I. Знакомство

Многофазная архитектура в системах пакетной
и потоковой обработки
Системы пакетной обработки
В системах пакетной обработки фазы выполняются независимо друг от друга,
как и экземпляры одной фазы. Это означает, что они не выполняются одновременно. Все экземпляры в системе могут выполняться один за одним или
же пакет за пакетом — при условии, что порядок выполнения правилен. Это
позволяет построить систему пакетной обработки огромных (действительно
огромных!) объемов данных при весьма ограниченных ресурсах (хотя при недостатке ресурсов это потребует больше времени). Для компенсации затрат на
хранение промежуточных данных события обычно более эффективно обрабатываются пакетами большего размера. Например, на практике часто применяются
часовые или суточные пакетные окна. Событиям, происходящим в начале окна,
для обработки приходится целый час или сутки дожидаться его закрытия. Этим
и объясняется высокая задержка.
Одним из важных преимуществ систем пакетной обработки является простота преодоления сбоев. Если возникнет проблема (например, сбой компьютера или ошибка при чтении или записи данных), фазу со сбоем можно просто
запланировать на другой машине и выполнить заново.

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

Глава 1. Знакомство со стриминговыми системами  35

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

Система потоковой
обработки

Обработка
запросов

Обработка данных

Обработка данных

Прямое взаимодействие с пользователем

Прямое взаимо­
действие с клиентами и другими
службами. Косвенное взаимодействие с пользователем

Применение операций к данным.
Результаты предназначены для пользователя прямо или
косвенно

Применение операций к данным.
Результаты предназначены для пользователя прямо или
косвенно

Приложения запускаются и останавливаются
пользователями

Экземпляры
службы представляют собой долго
выполняемые
процессы

Планирование
запуска и остановки
экземпляров служб

Экземпляры службы
представляют собой
долго выполняемые
процессы

Один основной
цикл

Один основной
цикл с потоками

Многофазный
процесс

Многофазный
процесс

Приложение

Серверная служба

Обработка пользовательского
ввода

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

36  Часть I. Знакомство

Эталонная система обработки событий
Рассмотрев в общих чертах несколько разных систем, сосредоточимся на системах обработки событий. Из предыдущего раздела вы узнали, что стриминговая
система состоит из нескольких долго выполняемых процессов-компонентов.

Исполнители тут
и там — такую систему
будет нетрудно построить,
не правда ли?

Executor

Executor

Executor

Executor

Ответ на этот вопрос зависит от того, какая система вам нужна. Что вы хотите
сделать? Каков объем трафика? Сколько ресурсов у вас есть? Как вы будете
управлять этими ресурсами? Как восстанавливаться при сбое? Как убедиться
в том, что после восстановления получен правильный результат? Все эти вопросы приходится учитывать при построении системы потоковой обработки.
Ну как, вы ответите «да»?
Стриминговые системы могут быть довольно сложными, но строить их
не так сложно. В следующих главах вы узнаете, как строятся стриминговые
системы и как работают их механизмы. Готовы?

Глава 1. Знакомство со стриминговыми системами  37

Итоги
Из этой главы мы узнали, что потоковая обработка представляет собой технологию обработки непрерывно поступающих событий для получения результатов
в реальном времени. Чтобы понять, чем системы потоковой обработки отличаются от других, мы изучили и сравнили типичные архитектуры четырех разных
типов компьютерных систем.
zzПриложения.
zzСерверные службы.
zzСистемы пакетной обработки.
zzСистемы потоковой обработки.

Упражнение
Подумайте и приведите другие примеры приложений, служб, систем пакетной обработки и систем потоковой обработки.

Ещё больше книг в нашем телеграм канале:
https://t.me/javalib

2

Привет,
стриминговые системы!

В этой главе
99События в стриминговых системах.
99Разные стриминговые компоненты.
99Построение задания из стриминговых компонентов.
99Выполнение кода.

«Сначала решите задачу. Потом пишите код».
Джон Джонсон

Глава 2. Привет, стриминговые системы!  39

Начальнику нужен современный пункт оплаты

ник

Началь

Я хочу автоматизировать
процесс оплаты на мосту, чтобы
получить больше прибыли! И мне нужно
современное решение, которое справится
с самым интенсивным трафиком.

Сид

Спасибо за четкие
требования. Что значит
«с самым интенсивным»?
Я говорю
о движении
в час пик.

Еще раз спасибо
за четкость. Сначала нужно
решить потенциальную
проблему бесконечного
трафика.

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

Мост

Автомобили заезжают на мост.

1 миллиард
обслужен

Грузовик

Табло обновляется в реальном времени
после обработки результатов.

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

40  Часть I. Знакомство

Вначале были запросы HTTP…
и ничего не вышло

ЭйДжей

Со стремительным развитием технологий за последние годы больДавайте начнем
шая часть оборудования пунктов
с серверной службы.
оплаты, которые раньше обслуживались вручную, была заменена
устройствами IoT («Интернет веИнтересно,
щей»). Когда автомобиль въезжает
столкнемся ли мы
на мост, система получает данные
с какими-то проблемами,
если будем решать задачу
о его типе с датчика IoT. Первая
с помощью серверной
версия системы должна была подслужбы?
считывать общее количество автомобилей разных типов (легковых
автомобилей, грузовиков, микроавтобусов и т. д.), проехавших по мосту. Начальник хочет, чтобы результаты обновлялись в реальном времени, поэтому каждый
раз, когда проезжает новый автомобиль, соответствующий счетчик должен немедленно обновляться.
ЭйДжей, Миранда и Сид, как обычно, начали с проверенной архитектуры серверной службы и использовали запросы HTTP для передачи данных.
Датчик

чета

Служба подс

с

Запро

Из-за задержки с запросами
HTTP система не успевает
обрабатывать входящие события.
т

ан
ир

ве

М

От

да

Однако попытка завершилась неудачей.
В выходные трафик вырос,
и система столкнулась с нагрузкой, с которой не справилась. Из-за задержки при
обработке запросов система
начала отставать, на табло выводились неточные результаты, а у ЭйДжея и Миранды
возникли проблемы.

Система не справляется с количеством
автомобилей, проезжающих по мосту.

Из-за этого показания
счетчика не актуальны
и неверны.

Глава 2. Привет, стриминговые системы!  41

ЭйДжей и Миранда берут паузу, чтобы подумать
Мне кажется, что
модель «запрос/ответ» здесь
не подходит. При подсчете
автомобилей не обязательно
дожидаться ответа. Рассмотрим
естественный поток данных
в системе.

1 миллиард
обслужен

3. Текущее значение
счетчика получается
от удаленной системы
для вывода на табло.

Грузовик

Значение на табло обновляется
в реальном времени после
обработки результатов

Удаленная
система

2. Удаленная система
подсчитывает общее
количество автомобилей.

Поскольку датчику не
нужно получать ответ с общим
количеством автомобилей, нельзя ли
организовать естественную передачу данных
в системе? Очень важно, чтобы
возвращаемые ответы были точными
и быстрыми.

1. Данные
поступают
в систему
от датчика.

42  Часть I. Знакомство

ЭйДжей размышляет о стриминговых системах
Если нам удастся устранить задержку модели «запрос/
ответ», я уверен, что мы сможем справиться с интенсивностью движения и поддерживать
точные счетчики автомобилей в реальном времени.
Я слышал
о потоковой обработке.
Чем она отличается от
других и в чем ее
преимущества?
Одно из преимуществ в том, что стриминговые системы справляются
с такими потоками
данных лучше, чем модель
«запрос/ответ».

Если не углубляться в подробности сетевых взаимодействий и передачи пакетов, различие проявляется еще и в том, как организуется взаимодействие
стриминговых систем, использующих архитектуру серверных служб http.
Главное отличие архитектуры серверных служб заключается в том, что клиент
отправляет запрос, ожидает, пока служба выполнит некие вычисления, а затем
получает ответ. В стриминговых системах клиент отправляет запрос, но не дожидается его обработки, прежде чем отправлять следующий. Без необходимости
ожидать обработки данных системы быстрее реагируют на события.
Все еще непонятно? Вскоре мы все подробно объясним.

Глава 2. Привет, стриминговые системы!  43

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

Система
производит
обработку

Ожидание…

Система
завершает
обработку
Получение ответа

Потоковая обработка: асинхронная модель
Отправка запроса

просто
можно нужно
й
о
к
т
о
ых. Не а
обраб
м
у данн
ковой
С пото ить отправк енная систе
л
ж
а
л
д
о
у
д
а
о
пр
т.
пок
аться,
ответи
дожид ет запрос и
ота
обраб

Стриминговая
система

Резуль
та
сохран ты обработки
яются
в друг
ом ме

сте.

44  Часть I. Знакомство

Как использовать стриминговую систему в этой задаче
На верхнем уровне ЭйДжей отказывается от модели «запрос/ответ» и разделяет процесс на две фазы. Следующая диаграмма показывает, как стриминговая
система может вписаться в сценарий с подсчетом автомобилей, проезжающих
по мосту. Подробности — далее в этой главе.

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

микроавтобус

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

Блок чтения
данных
с датчика IoT
Счетчик автомобилей
получает события от блока
чтения данных с датчика
IoT и обрабатывает их.
Счетчик
автомобилей

Глава 2. Привет, стриминговые системы!  45

Очереди: фундаментальная концепция
Прежде чем идти дальше, стоит уделить особое внимание одной структуре
данных: очереди. Эта структура широко используется во всех стриминговых
системах.
В традиционных распределенных системах взаимодействие обычно происходит по модели «запрос/ответ», также называемой синхронной моделью. Это не
относится к потоковым системам, так как модель «запрос/ответ» подразумевает
ненужную задержку при работе с данными в реальном времени (формально правильнее говорить о «системах квазиреального времени» или «системах, близких
к реальному времени», но стриминговые системы часто рассматриваются как
системы реального времени). На верхнем уровне распределенные стриминговые
системы поддерживают долгосрочное подключение к компонентам в пределах
системы для сокращения времени передачи данных. Долгосрочное подключение
предназначено для непрерывной передачи данных, которая позволяет стриминговым системам реагировать на события по мере их возникновения.
Во всех распределенных системах существует некоторая форма внутренних
процессов передачи данных. Среди всех возможных вариантов очередь особенно
полезна для упрощения архитектуры стриминговых сценариев использования.
zzОчереди

помогают организовать разделение модулей в системе, чтобы
каждая часть могла работать в своем темпе, не беспокоясь о зависимостях
и синхронизации.
zzОчереди помогают организовать обработку событий по порядку, так как
они являются структурой данных FIFO (First In First Out, то есть «первым вошел, первым вышел»).
Очередь соединяет
и разъединяет две системы.

«микроавтобус» «автомобиль» «грузовик»

«грузовик»

Стриминговая
система

Очереди также широко применяются
в стриминговых системах.

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

46  Часть I. Знакомство

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

Процессы и потоки
В компьютерной теории термин «процесс» относится к выполнению программы, а термином «поток выполнения» (thread) обозначается отдельная
ветвь выполнения в процессе. Главное различие между ними заключается
в том, что разные потоки выполнения одного процесса совместно используют одно пространство памяти, а процессы обладают собственными
пространствами памяти. Как потоки выполнения, так и процессы могут
использоваться для выполнения операций с данными на приведенной диаграмме. Стриминговые системы могут выбрать тот или иной вариант (или их
комбинацию) с учетом своих требований. Чтобы избежать недоразумений,
в этой книге термин «процесс» будет использоваться (если явно не указано иное) для обозначения независимой последовательности выполнения
в любом варианте реализации.

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

e4

Процесс
обработки
данных

Получает элемент из входящей
очереди и выполняет с его
данными какую-либо операцию.

e3

e2

Эту схем
у переда
чи данны
можно пр
х
одолжит
ь.

Процесс
обработки
данных

Элемент помещается
в исходящую очередь для
следующего процесса в задании.

e1

Глава 2. Привет, стриминговые системы!  47

Потоковый фреймворк (вернее, его начало)
Во время планирования этой книги не раз обсуждалось, как объяснять концепции потоковой обработки без тесной привязки к конкретной стриминговой технологии для примеров. Технологии непрерывно развиваются, и постоянно дополнять материал было бы очень трудно. Мы подумали, что
упрощенный фреймворк, который мы назвали Streamwork, поможет изложить базовые концепции стриминговых систем без привязки к конкретному
фреймворку.
Фреймворк Streamwork имеет сильно упрощенное ядро, которое работает
на компьютере локально. Он может использоваться для построения и запуска
простых стриминговых заданий, которые, как мы надеемся, помогут вам в изу­
чении концепций. Функциональность, которая поддерживается в распространенных стриминговых фреймворках, таких как Apache Heron, Apache Storm или
Apache Flink, осуществляющих потоковую передачу в реальном времени между
физическими машинами, в данном фреймворке ограничена. С другой стороны,
так ее проще понять.
Один из самых интересных аспектов работы с компьютерными системами
(как нам кажется) заключается в том, что не существует единственно правильного способа решения всех проблем. В отношении функциональности
стриминговые фреймворки, включая наш фреймворк Streamwork, имеют
много общего, так как они используют общие концепции, но их внутренние
механизмы реализации могут сильно различаться из-за различных ограничений и зависимостей.

Подумайте об этом
Построение стриминговых систем с нуля потребовало бы слишком больших
усилий. Фреймворки берут на себя основную часть рутинной работы, что
позволяет нам сосредоточиться на бизнес-логике. Тем не менее иногда
важно знать, как работают внутренние механизмы фреймворка.

48  Часть I. Знакомство

Обзор фреймворка Streamwork
В общем случае стриминговые фреймворки должны решать две задачи.
zzПредоставлять

программный интерфейс (API), который будет использоваться разработчиками для подключения специализированной логики
и построения задания.
zzПредоставлять ядро для выполнения стримингового задания.
API мы рассмотрим далее. Следует понимать, что эта книга не ставит цель научить вас использовать API Streamwork. Мы используем этот фреймворк только
для того, чтобы не привязывать пояснения к конкретному фреймворку. Начнем
с ядра. Следующая диаграмма описывает на верхнем уровне все активные составляющие фреймворка Streamwork. Следует понимать, что существует еще
один процесс, который запускает каждого исполнителя, а каждый исполнитель
в свою очередь запускает источник данных или компонент. Каждый исполнитель существует автономно, он не останавливает и не запускает других исполнителей.
Исполнитель-источник отвечает
за непрерывную работу
компонента-источника.

Исполнительисточник
(источник)

Исполнители соединяются
очередями событий.

Исполнители-операторы отвечают
за непрерывную работу
компонентов-операторов.

Исполнительоператор
(оператор 1)

Исполнительоператор
(оператор 2)

Возможности добавления
операторов не ограничены.

Фреймворк, представленный в этой главе, очень прост. Тем не менее все его
обозначенные компоненты сравнимы с компонентами реальных стриминговых
фреймворков. В следующих главах во фреймворк Streamwork будет добавляться
новая функциональность.

Глава 2. Привет, стриминговые системы!  49

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

Поближе рассмотрим, как выполняется
логика для исполнителя-источника и двух
исполнителей-операторов.

Исполнительисточник
(источник)

Исполнительоператор
(оператор 1)

Исполнительоператор
(оператор 2)

Очередь событий
между двумя
исполнителями
Исполнитель-источник

Исполнитель-оператор
operator.apply(e2)

source.getEvents()
e4 e3
Источник

Оператор 1

getEvents()

apply(event)

Объект источника данных
принимает
внешние
события.
В объекте-источнике,
определяемом пользователем, пользовательская
логика реализуется
в функции getEvents().

В объекте-операторе,
определяемом пользователем, пользовательская
логика реализуется
в функции apply().

50  Часть I. Знакомство

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

Исполнитель-оператор

source.getEvents()

operator.apply(e2)
e4 e3

Источник

Оператор
apply(event)

getEvents()

Л

Источник

ания

лан зад

кий п
огичес

поток

Оператор

Если забыть об исполнителях и рассматривать только объекты, определяемые пользователем, мы получим новую диаграмму справа — более чистое (более
абстрактное) краткое представление стриминговой системы. Эта диаграмма
(назовем ее логическим планом) является высокоуровневой абстракцией, на
которой изображены компоненты и структуры системы, а также логические
потоки данных в них. Из этой диаграммы можно видеть, как объект-источник
и объект-оператор соединяются через поток для формирования стримингового
задания. Следует понимать, что поток — не что иное, как непрерывная передача
данных от одного компонента к другому.

Глава 2. Привет, стриминговые системы!  51

Подробнее о концепциях
На следующей диаграмме более подробно представлены пять ключевых концепций: событие, задание, источник, оператор и поток.
Задание, также называемое конвейером
или топологией, представляет собой
реализацию стриминговой системы.
Задание строится из компонентов
(источников и операторов) и потоков,
соединяющих компоненты.

Источник

Источник, который вводит внешние
данные в потоковую систему.
Другими словами, источники
являются точками входа данных
в стриминговых системах.

Потоком называется непрерывная
поставка событий. Событие, также
называемое кортежем, элементом
или сообщением в разных
сценариях, представляет собой
отдельный блок неделимых данных
в потоке.

Оператор

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

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

52  Часть I. Знакомство

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

Источник

Блок чтения
данных

Поток событий
с данными
об автомобилях

Оператор

Счетчик
автомобилей

zzБлок

чтения данных получает данные с датчика и сохраняетсобытия
в очереди. Это источник.
zzСчетчик автомобилей отвечает за подсчет автомобилей в потоке. Это
оператор.
zzНепрерывное перемещение данных от источника к оператору — поток
событий автомобилей.
Блок чтения данных с датчика становится началом задания, а счетчик автомобилей — его концом. Линия, соединяющая блок чтения (источник) со
счетчиком автомобилей (оператором), представляет поток типов автомобилей
(событий), проходящий от блока чтения данных к счетчику автомобилей.
В этой главе такая система будет описана более подробно. Она будет выполняться на ваших локальных компьютерах с двумя терминалами: один получает ввод пользователя (левый столбец), а другой — вывод задания (правый
столбец).
Ввод задания
car
truck
car

Вывод задания
SensorReader --> car
VehicleCounter -->
car: 1
SensorReader --> truck
VehicleCounter -->
car: 1
truck: 1
SensorReader --> car
VehicleCounter -->
car: 2
truck: 1

Глава 2. Привет, стриминговые системы!  53

Первое стриминговое задание
Стриминговое задание создается средствами Streamwork API и включает следующие шаги.
1.
2.
3.
4.

Создание класса задания.
Построение источника.
Построение оператора.
Соединение компонентов.

Первое стриминговое задание: создание класса события
Событие — один фрагмент данных в потоке, который должен обрабатываться
заданием. Во фреймворке Streamwork класс API Event отвечает за хранение или
инкапсуляцию пользовательских данных. Аналогичные концепции существуют
и в других стриминговых системах.
В задании каждое событие представляет один тип автомобилей. Для
простоты будем считать, что каждый тип представляет собой строку (например, car или truck ). В нашем примере будет использоваться класс события VehicleEvent, производный от класса Event из API. Каждый объект
VehicleEvent содержит информацию об автомобиле, которую можно получить
вызовом функции getData().
Внутренняя строка для
обозначения типа автомобиля.
public class VehicleEvent extends Event {
private final String vehicle;
public VehicleEvent(String vehicle) {
this.vehicle = vehicle;
}
Конструктор получает vehicle в виде

}

@Override
public String getData() {
return vehicle;
}

строки и сохраняет значение.

Получает данные, хранящиеся в событии.

54  Часть I. Знакомство

Первое стриминговое задание: источник данных
Источником (source) называется компонент, который вводит внешние данные
в стриминговую систему. Земной шар на следующей диаграмме обозначает данные, внешние по отношению к заданию. В стриминговом задании блок чтения
данных от датчика вводит данные, полученные от локального порта, в систему.
Счетчик
автомобилей

Блок чтения
данных

SensorReader: Source
getEvents()

VehicleCounter: Operator
apply(event)

Во всех стриминговых фреймворках присутствует API, дающий возможность
задавать для источников данных логику, которая представляет интерес только
для вас. Во всех API источников данных присутствует некая разновидность
перехватчика жизненного цикла (lifecycle hook), который будет вызываться
для получения внешних данных. В этой точке код выполняется фреймворком.

Что такое перехватчик жизненного цикла?
Перехватчиками жизненного цикла называются методы, которые вызываются по определенному повторяемому шаблону фреймворком, которому они
принадлежат. Как правило, эти методы позволяют разработчикам настроить
поведение приложения в фазах жизненного цикла фреймворка, на основе
которого строится приложение. В случае фреймворка Streamwork имеется
перехватчик жизненного цикла (или метод), называемый getEvents(). Он многократно вызывается фреймворком, чтобы вы могли получить внешние
данные. Перехватчики жизненного цикла позволяют разработчикам писать
логику, которая для них важна, и поручить рутинную работу фреймворку.

Глава 2. Привет, стриминговые системы!  55

Первое стриминговое задание: источник данных (продолжение)
В этом задании блок чтения данных
датчика будет читать события. В нашем упражнении вы будете моделировать датчик на мосту, самостоятельно
создавая события и передавая их на
открытый порт вашего компьютера,
прослушиваемый стриминговым заданием. Блок чтения получает данные
о типах автомобилей, отправленные
в порт, и передает их потоковому заданию — так выглядит бесконечный
(или неограниченный) поток событий.
1. getEvents() содержит логику чтения
данных от датчика,
определяемую
пользователем.

Датчик
Грузовик

getEvents()

Java-код класса SensorReader выглядит так:
public class SensorReader extends Source {
private final BufferedReader reader;
public SensorReader(String name, int port) {
super(name);
reader = setupSocketReader(port);
}

}

2. События
направляются
в исходящий
поток (очередь).

SensorReader: Source

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

@Override
public void getEvents(List eventCollector) {
String vehicle = reader.readLine();
eventCollector.add(new VehicleEvent(vehicle));
System.out.println("SensorReader --> " + vehicle);
}

Передача строки
получателю.

Чтение типа
автомобиля из
входных данных.

56  Часть I. Знакомство

Первое стриминговое задание: оператор
Вся пользовательская логика обработки содержится в операторах. Они отвечают
за получение входящих событий для обработки и генерирование исходящих
событий; следовательно, у них есть как ввод, так и вывод. Вся логика обработки данных в стриминговых системах обычно размещается в компонентах
операторов.
Блок чтения
данных

Счетчик
автомобилей

VehicleCounter:
VehicleCounter: Operator
Operator
apply(event)
apply(event)

Базо
ва
разм я логика
ещае
о
тся в бработки
опер
атора
х.

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

Глава 2. Привет, стриминговые системы!  57

Первое стриминговое задание: оператор (продолжение)
В компоненте VehicleCounter карта используется для хранения счетчиков типов
автомобилей в памяти. Она обновляется соответствующим образом
при получении нового события.
В стриминговом задании счетчик
автомобилей представляет собой
оператор, который подсчитывает
события. Этот оператор завершает
задание и не генерирует вывод для
последующих операторов.
1. Получение
входящих событий.

Ключ (Vehicle)

Значение (Count)

автомобиль

2

грузовик

1

микроавтобус

1

VehicleCounter: Operator
apply(event)
2. Логика, определяемая
пользователем, применяется
к событиям данных вызовом apply().

public class VehicleCounter extends Operator {
private final Map countMap =
new HashMap();
public VehicleCounter(String name) {
super(name);
}

}

Получение
счетчиков из карты

@Override
public void apply(Event event,List collector) {
String vehicle = ((VehicleEvent)event).getData();
Integer count = countMap.getOrDefault(vehicle, 0);
count += 1;
Увеличение счетчика
countMap.put(vehicle, count);
Сохранение счетчика
System.out.println("VehicleCounter --> ");
обратно в карте
printCountMap();
Вывод текущего
}
значения счетчика

58  Часть I. Знакомство

Первое стриминговое задание: сборка задания
Чтобы собрать стриминговое задание, необходимо добавить источник Sen­
sorRea­
der и оператор VehicleCounter и соединить их. В классах Job и Stream,
которые мы построили для вас, присутствуют перехватчики:
zzJob.addSource() добавляет источник данных в задание.
zzStream.applyOperator() добавляет оператор в поток.

Задание

1. Создание объекта
задания.

2. Добавление объекта
источника и получение
потока.

SensorReader

3. Применение
оператора к потоку.

VehicleCounter

Следующий код выполняет описанные выше шаги:
public static void main(String[] args) {
Создание объекта задания
Job job = new Job();
Stream bridgeOut=job.addSource(new SensorReader());

Добавление объекта потока и получение потока

}

bridgeOut.applyOperator(newVehicleCounter());
JobStarter starter = new JobStarter(job);
starter.start();
Запуск задания

Применение оператора
к потоку

Глава 2. Привет, стриминговые системы!  59

Выполнение задания
Все, что вам понадобится для выполнения задания, — машина с операционной
системой Mac, Linux или Windows с доступом к терминалу (командной строке
в Windows). Вам также понадобятся инструменты для компиляции и выполнения кода: git, пакет Java Development Kit (JDK) 11, Apache Maven, Netcat (или
Nmap в Windows). После установки всех инструментов вы сможете загрузить
код и откомпилировать его:
$ git clone https://github.com/nwangtw/GrokkingStreamingSystems.git
$ cd GrokkingStreamingSystems
$ mvn package

Команда mvn генерирует файл target/gss.jar. Наконец, для запуска стримингового
задания вам понадобятся два терминала: для запуска задания и для отправки
данных, обрабатываемых заданием.
1. Открытие сокета на порту
9990 терминала ввода.
Именно здесь данные отправляются в стриминговое
задание.
Терминал ввода

$ nc -lk 9990

2. Запуск стримингового
задания. Оно подключается
к сокету на порту 9990 для
моделирования источника
данных блока чтения данных
с датчиков.

Терминал задания

$ java -cp target/gss.jar \
com.streamwork.ch02.job.VehicleCountJob

Откройте новый терминал (терминал ввода) и выполните следующую команду
(обратите внимание: в Mac и Linux используется команда nc, а в Windows —
коман­да ncat):
$ nc -lk 9990

Команда запускает на порту 9990 небольшой сервер, к которому можно подключаться из других приложений. Весь пользовательский ввод в этом терминале
будет направляться в порт.
Затем в исходном терминале (терминале задания), который использовался
для компиляции задания, выполните задание следующей командой:
$ java -cp target/gss.jar com.streamwork.ch02.job.VehicleCountJob

60  Часть I. Знакомство

Ход выполнения задания
После запуска задания введите в терминале ввода строку car и нажмите Ввод.
Счетчик выводится в терминале задания.
Источник SensorReader
отправляет в систему
событие «car».
Терминал ввода

car

Терминал задания

SensorReader --> car
VehicleCounter -->
car: 1
Оператор VehicleCounter обрабатывает событие
«car» и добавляет к счетчику автомобилей (car).

Если теперь ввести в терминале ввода строку truck, в терминале задания будут
выведены счетчики для car и truck.
truck
В карте оператора
VehicleCounter появляется новая
запись для грузовика (truck).

SensorReader --> truck
VehicleCounter -->
car: 1
truck: 1

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

car
car
.
.
.

SensorReader --> car
VehicleCounter -->
car: 2
truck: 1
SensorReader --> car
VehicleCounter -->
car: 3
truck: 1

Глава 2. Привет, стриминговые системы!  61

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

Задание

Добавление
источника

SensorReader

Применение
оператора

VehicleCounter

Задания и компоненты не выполняются сами по себе. Ими управляет стриминговое ядро. Заглянем под капот и посмотрим, как задание выполняется в ядре
Streamwork. Всего существуют три активные части (на этом этапе), и мы рассмотрим их поочередно: исполнитель-источник, исполнитель-оператор и блок
запуска задания.
Исполнитель-источник
source.getEvents()

SensorReader: источник
getEvents()

Исполнитель-оператор
operator.apply()

VehicleCounter: оператор
apply(event)

62  Часть I. Знакомство

Внутри ядра: исполнители-источники
Во фреймворке Streamwork, который мы построили для вас, исполнитель-источник непрерывно управляет получением данных от источников. Для этого он
выполняет бесконечные циклы, которые извлекают внешние данные для включения в исходящую очередь внутри стримингового задания. Несмотря на то что
для блока Выход предусмотрен вариант Да, он никогда не будет реализован.
Объекты-источники — один из
компонентов стриминговых
систем, который реализуется
пользователями стримингового
фреймворка для получения
внешних данных.

Источник
(SensorReader)

Данные передаются
в этом направлении.
Исполнители-источники
вызывают перехватчиков,
чтобы выполнить методы
на объектах-источниках
и поместить все полученные
события в исходящую очередь.

Исполнитель-источник запускает
объект-источник, предоставленный
пользователем.

Процесс
начинается
здесь.

Начало

Вызов Source.
getEvents() для
получения новых
событий

Передача событий
в исходящую
очередь
Исходящая
очередь
Выход
Да
Конец

Нет
После
выполнения
источника
цикл
процесса
запускается
вновь.

Глава 2. Привет, стриминговые системы!  63

Внутри ядра: исполнители-операторы
В Streamwork исполнитель-оператор работает почти так же, как исполнитель-источник. Единственное отличие заключается в том, что ему приходится
управлять очередью входящих событий. И хотя для блока Выход предусмотрен
вариант Да, он никогда не будет реализован.
Исполнитель-оператор выполняет объектоператор, предоставленный пользователем.

Процесс
начинается
здесь.

Начало
Входящая
очередь
Извлечение
событий из
входящей очереди

Вызов
Operator.apply() для
обработки событий

Объект-оператор,
предоставленный
пользователем.

Оператор
(VehicleCounter)

Постановка
событий в исходящую очередь

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

Исходящая очередь
Выход
Да
Конец

Нет

После
выполнения
оператора цикл
процесса
запускается
вновь.

64  Часть I. Знакомство

Внутри ядра: блок запуска задания
JobStarter отвечает за подготовку всех активных частей (исполнителей) за-

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

2. Создание промежуточной
очереди событий для
соединения исполнителяисточника с исполнителемоператором.

Исполнитель-источник
source.getEvents()

operator.apply(E2)

SensorReader: источник

getEvents()

Исполнитель-оператор

VehicleCounter: оператор

apply(event)
3. Запуск исполнителей.

Помните!
Здесь описана архитектура типичного стримингового ядра, чтобы обобщить
работу фреймворков на верхнем уровне. Разные стриминговые фреймворки
могут работать по-разному.

Глава 2. Привет, стриминговые системы!  65

Перемещение событий
Рассмотрим подробнее ядро и его активные части, включая компоненты активного задания, определяемые пользователем.
Исполнитель-источник
многократно вызывает
функцию getEvents()
класса SensorReader,
чтобы получить ввод
и направить данные о типах
автомобилей в очередь
событий.

Блок запуска подготавливает исполнителей
и промежуточную
очередь событий. Затем
он запускает процессыисполнители.

Исполнитель-источник
source.getEvents()

Исполнитель-оператор
продолжает поочередно
извлекать типы автомобилей из входящей
очереди событий
и вызывать функцию
apply() объекта
VehicleCounter
с данными.

Исполнитель-оператор
operator.apply(E2)

SensorReader: источник

VehicleCounter: оператор

getEvents()

apply(event)

Объект-источник,
предоставленный
пользователем.

Объект-оператор,
предоставленный
пользователем.

После запуска задания все исполнители начинают выполняться параллельно —
иначе говоря, одновременно!

66  Часть I. Знакомство

Жизненный цикл элемента данных
Рассмотрим другой аспект стриминговых систем — жизненный цикл одного
события. Если добавить строку car и нажать клавишу Ввод в терминале ввода,
событие пройдет через стриминговую систему, как объясняется на следующей
диаграмме.
2. Исполнитель-источник
помещает событие данных
в промежуточную очередь
событий.

3. Исполнитель-оператор
извлекает событие данных
из промежуточной очереди
событий и передает его
оператору.

Исполнитель-источник

Исполнитель-оператор

source.getEvents()

operator.apply(E2)

SensorReader: источник

VehicleCounter: оператор

getEvents()

apply(event)

1. Логика, определяемая
пользователем, получает
события от внешнего
источника данных.

4. Применение логики, определяемой
пользователем, к событиям.

Глава 2. Привет, стриминговые системы!  67

Краткий обзор концепций стриминга
Поздравляем, вы справились с первым стриминговым заданием! А теперь
вернемся немного назад и сделаем обзор ключевых концепций стриминговых
систем.
Задание, также называемое
конвейером или топологией,
представляет собой реализацию
потоковой системы. Задание
строится из компонентов
(источников и операторов)
и потоков, соединяющих
компоненты.

Источник

Источник вводит внешние данные
в стриминговую систему.
Другими словами, источники
являются точками входа данных
в стриминговых системах.

Потоком называется непрерывная
поставка событий. Событие (event),
также называемое кортежем,
элементом или сообщением в разных
сценариях, представляет собой
отдельный блок неделимых данных
в потоке.

Оператор

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

68  Часть I. Знакомство

Итоги
Стриминговое задание представляет собой систему, обрабатывающую события
в реальном времени. Каждый раз, когда происходит событие, задание принимает
его в систему и обрабатывает его. В этой главе мы построили простое задание
для подсчета автомобилей, въезжающих на мост, и рассмотрели следующие
концепции.
zzПотоки и события.
zzКомпоненты (источники и операторы).
zzПотоковые задания.

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

Упражнения
1. Чем источник отличается от оператора?
2. Найдите в реальной жизни три примера, которые можно смоделировать
в виде стриминговых систем. (А если расскажете нам о них, возможно,
мы используем их в следующем издании этой книги!)
3. Загрузите исходный код и измените реализацию SensorReader для автоматической генерации событий.
4. Измените логику VehicleCounter для вычисления накопленной суммы
сбора в реальном времени. Размер сбора для каждого типа автомобилей
задайте самостоятельно.
5. Оператор VehicleCounter в первом задании выполняет две функции:
подсчет автомобилей и вывод результатов — и это не лучший вариант.
Попробуйте изменить реализацию и переместить логику вывода в новый
оператор.

Параллелизация
и группировка данных

3

В этой главе
99Параллелизация.
99Параллелизм данных и параллелизм задач.
99Группировка событий.

«Девять женщин не родят ребенка за один месяц».
Фредерик П. Брукс

В предыдущей главе ЭйДжей и Миранда взялись за задачу подсчета в реальном
времени количества проезжающих по мосту автомобилей с использованием
стримингового задания. В системе, построенной ими, возможности обработки
интенсивного трафика относительно ограничены. Представьте, что творится
в пункте оплаты на мосту с одной полосой движения в час пик. Ужас! В этой
главе мы изучим базовый прием для решения фундаментальной задачи в большинстве распределенных систем — масштабирования системы для повышения
пропускной способности задания или, другими словами, обработки большего
объема данных.

70  Часть I. Знакомство

Датчик генерирует больше событий
В предыдущей главе ЭйДжей и Миранда взялись за задачу подсчета в реальном
времени количества проезжающих по мосту автомобилей с использованием
стримингового задания. Был принят вариант сбора данных о движении одним
датчиком, сообщающим о событиях. Естественно, начальник хочет заработать
больше денег, поэтому он решает построить больше полос на мосту. По сути, он
требует, чтобы стриминговое задание масштабировалось по количеству событий
трафика, которые могут обрабатываться за один раз.
Мост

Система с одной полосой движения

Типичное решение для повышения пропускной способности в компьютерных
системах заключается в распределении вычислений по
нескольким процессам; это
называется параллелизацией.

Микроавтобус

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

Система с несколькими
полосами движения

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

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

Грузовик
Микроавтобус

Даже в потоковых системах непросто добиться обработки  71

Даже в потоковых системах непросто добиться
обработки в реальном времени
При увеличении количества полос задание перестало
справляться с нагрузкой

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

Разве нельзя
добавить побольше экземпляров
блока чтения данных и счетчика
автомобилей?

Но… в таком случае как
нам решить, какие данные куда
переходят?

Блок
чтения
данных

Счетчик
автомобилей

Масштабируем
задание от этой
архитектуры…
…вот к этой.

Блок
чтения
данных

Счетчик
автомобилей

Блок
чтения
данных

Счетчик
автомобилей

72  Часть I. Знакомство

Новые концепции: параллелизм важен
Параллелизация часто применяется в компьютерных системах. Ее идея заключается в том, что задачу, отнимающую много времени, часто удается разбить
на меньшие подзадачи, которые могут выполняться параллельно. Тогда другие
компьютеры могут работать над задачей совместно, что приведет к существенному сокращению времени выполнения.

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

Только один оператор
для извлечения
и обработки событий.

Несколько операторов для извлечения
и обработки событий. В этом конкретном сценарии степень параллелизации
равна 2. В одном временном окне
выполняется вдвое больший объем
работы.

Глава 3. Параллелизация и группировка данных  73

Новые концепции: параллелизм данных
Для решения задачи подсчета на одном компьютере не хватает скорости. К счастью, у начальника под рукой есть несколько компьютеров — да и в каком центре обработки данных их нет? Будет разумно поручать разные события разным
компьютерам, чтобы все они работали над вычислениями параллельно. Тем
самым вы обработаете данные всех автомобилей за один шаг вместо 100 шагов.
Другими словами, производительность повышается в 100 раз. Если данных
становится больше, распределение вычислений по нескольким компьютерам
вместо одного более мощного компьютера позволит быстрее решить задачу.
Такой подход называется горизонтальным масштабированием.
Представьте,
что здесь
100 операторов.
Представьте, что эта очередь
состоит из 100 элементов.

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

Каждый оператор выполняет одну и ту же операцию с определенным
подмножеством всего
набора данных.

74  Часть I. Знакомство

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

Умножение на 4
Умножение на 4
Умножение на 4

Каждая выполняемая операция
всегда дает результат 4 как при
синхронном, так и при последовательном выполнении, потому
что 1 * 4 = 4. Использовать
данные другого элемента
очереди не нужно. Следовательно, здесь имеет место независимость выполнения данных.

Умножение на 4
Все элементы
одновременно
умножаются на 4.

Умножение на 4

Умножение на 4

Глава 3. Параллелизация и группировка данных  75

Новые концепции: параллелизм задач
Параллелизм данных критичен для многих систем больших данных, а также
для распределенных систем вообще, потому что он позволяет разработчикам
решать задачи более эффективно, используя большее количество компьютеров. Наряду с параллелизмом данных существует другой тип параллелизации:
параллелизм задач, также называемый параллелизмом функций. В отличие
от параллелизма данных, который подразумевает выполнение одной задачи
с разными данными, параллелизм задач ориентирован на выполнение разных
задач с одними данными.
Чтобы понять суть параллелизма задач, можно взглянуть на стриминговое
задание, которое мы рассматривали в главе 2. Компоненты блока чтения данных
с датчика и счетчика автомобилей продолжают выполняться для обработки входящих событий. Когда компонент счетчика автомобилей обрабатывает событие
(выполняет подсчет), компонент блока чтения данных с датчика одновременно
получает другое, новое событие. Иначе говоря, две разные задачи выполняются
одновременно. Это означает, что событие генерируется блоком чтения данных,
а затем обрабатывается компонентом счетчика автомобилей.

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

Событие обрабатывается двумя
компонентами по очереди.

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

76  Часть I. Знакомство

Параллелизм данных и параллелизм задач
Повторим:
zzПараллелизм задач означает, что одна задача выполняется с разными на-

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

Блок
чтения
данных

Блок
чтения
данных

Счетчик
автомобилей

Счетчик
автомобилей

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

Стриминговые
системы сочетают
параллелизм данных
и параллелизм задач.

Глава 3. Параллелизация и группировка данных  77

Параллелизм и многозадачность
В чем отличия?
Этот абзац легко бы мог вызвать ожесточенные технические споры — пожалуй,
так же легко, как абзац, оправдывающий использование табуляции вместо пробелов. В ходе планирования книги этот вопрос поднимался несколько раз. Как
правило, наши встречи всегда завершались тем, что мы спрашивали себя, какой
же термин использовать.
Параллелизм — термин, который мы решили использовать для объяснения
того, как изменять стриминговые задания для улучшения производительности
и масштабирования. Если говорить точнее, в контексте этой книги термин «параллелизм» относится к количеству экземпляров конкретного компонента. Также можно сказать, что параллелизм — количество экземпляров, выполняемых
для завершения одной и той же задачи. С другой стороны, многозадачность —
более общий термин, которым обозначается одновременное выполнение двух
и более операций.
Следует заметить, что для выполнения разных задач в нашем стриминговом
фреймворке используются потоки выполнения, но в реальных стриминговых
заданиях обычно где-то работают несколько физических машин для поддержки
задания. В этом случае можно использовать термин «параллельные вычисления». Некоторые читатели могут усомниться, насколько хорошо термин «параллелизация» подходит для кода, выполняемого на одной машине. Это еще один
вопрос, который мы задавали себе. Правильно ли писать об этом? Мы решили
не затрагивать этот вопрос. В конце концов, эта книга написана для того, чтобы
вы хорошо разбирались в стриминге. Просто знайте, что параллелизация является важнейшим компонентом стриминговых систем, а для вас важно уверенно
понимать концепции и различия.

Параллелизация: много
экземпляров одного объекта
в одно время.

Многозадачность: много разных
объектов в одно время.

78  Часть I. Знакомство

Параллелизация задания
Самое время рассмотреть состояние последнего стримингового задания. Задание для анализа трафика состоит из двух компонентов: блока чтения данных
с датчика и счетчика автомобилей. Напомним схему задания на следующей
диаграмме.
Исполнитель-источник

Исполнитель-оператор

Блок чтения
данных

Счетчик
автомобилей

Эта реализация работала в предыдущей главе. Сейчас же мы добавим в схему
новый компонент, который мы решили назвать диспетчером событий (event
dispatcher). Он позволяет перенаправлять данные между разными экземплярами параллелизированного компонента. С компонентом eventDispatcher структура задания из главы 2 выглядит, как показано ниже. Следующая диаграмма
обобщает результат чтения этой главы и повторения действий по построению
задания. К концу главы вы добавите два экземпляра каждого компонента
и будете понимать, как система будет принимать решения об отправке данных
каждому экземпляру.

Исполнитель-источник

Предшествующий
компонентисполнитель

Исполнитель-оператор
Исполнитель-оператор,
экземпляр 0

Исполнитель-источник,
экземпляр 0

Счетчик
автомобилей

Блок чтения
данных

Диспетчер
событий
Исполнитель-оператор,
экземпляр 1

Исполнитель-источник,
экземпляр 1

Счетчик
автомобилей

Блок чтения
данных

Последующий
компонентисполнитель

Глава 3. Параллелизация и группировка данных  79

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

Исполнитель-оператор

Исполнитель-источник

Исполнитель-оператор,
экземпляр 0

Исполнитель-источник,
экземпляр 0

Счетчик
автомобилей

Блок чтения
данных

Диспетчер
событий
Исполнитель-источник,
экземпляр 1
Блок чтения
данных

Существуют два экземпляра
блока чтения данных с датчика.
Это можно считать параллелизированным компонентом.

Исполнитель-оператор,
экземпляр 1
Счетчик
автомобилей

Существуют два экземпляра счетчика автомобилей. Это можно считать
параллелизированным
компонентом.

Как
диспетчер событий
решает, какое событие
передается тому или иному
экземпляру каждого
компонента?

80  Часть I. Знакомство

Параллелизация источников
Сначала мы собираемся параллелизировать только источники данных в стриминговом задании: два
вместо одного. Для моделирования
параллелизированного источника
это новое задание должно прослушивать два разных порта для получения входных данных — порты
9990 и 9991. Мы обновили ядро для
поддержки параллелизма, а изменения в коде задания достаточно
просты:

Исполнитель-источник

Порт 9990

Порт 9991

экземпляр 0

экземпляр 1

Stream bridgeStream = job.addSource(
new SensorReader("sensor-reader", 2, 9990)
);

Чтобы выполнить задание, необходимо сначала создать два терминала ввода
и выполнить команду с разными портами:
Терминал ввода 1

$ nc -lk 9990

Терминал ввода 2

$ nc -lk 9991

После этого код компилируется и выполняется в отдельном терминале задания:
$ mvn package
$ java -cp target/gss.jar \
com.streamwork.ch03.job.ParallelizedVehicleCountJob1

К этому моменту у вас должны быть открыты три терминала для выполнения
задания: терминал ввода 1, терминал ввода 2 и терминал задания. В терминалах
ввода 1 и 2 вводятся события, которые будут извлекаться стриминговым заданием. Пример вывода приведен на следующей странице.

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

Глава 3. Параллелизация и группировка данных  81

Результат выполнения
Терминал ввода 1

Терминал ввода 2

car
truck

Терминал задания

SensorReader :: instance 0 -->
car
VehicleCounter :: instance 0 -->
car: 1

van
truck

SensorReader:: instance 0 -->
truck
VehicleCounter :: instance 0 -->
car: 1
truck: 1
SensorReader:: instance 1 -->
van
VehicleCounter :: instance 0 -->
car: 1
truck: 1
van: 1

Два блока чтения данных
получают данные.

Исполнитель-источник

Экземпляр 0

SensorReader:: instance 1 -->
car
VehicleCounter :: instance 0 -->
car: 1
truck: 2
van: 1
Один счетчик автомобилей получает
события от обоих экземпляров
блоков чтения данных.
Исполнитель-оператор

Экземпляр 0

Экземпляр 1

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

82  Часть I. Знакомство

Параллелизация операторов
Запуск нового задания
А теперь параллелизируем оператор VehicleCounter:
bridgeStream.applyOperator(
new VehicleCounter("vehicle-counter", 2));

Помните, что мы используем два параллелизированных источника, поэтому
необходимо использовать те же команды netcat, как ранее, в двух разных терминалах. Напомним, что каждая команда приказывает Netcat прослушивать
подключения на порту, заданном в команде.
Терминал ввода 1

$ nc -lk 9990

Терминал ввода 2

$ nc -lk 9991

После этого код компилируется и выполняется в отдельном, третьем терминале
задания:
$ mvn package
$ java -cp gss.jar \
com.streamwork.ch03.job.ParallelizedVehicleCountJob2

Выполняемое задание содержит два источника и два оператора, что наглядно
представлено на следующей диаграмме. Результат выполнения будет показан
далее.
Исполнитель-источник

Исполнитель-оператор

Экземпляр 0

Экземпляр 0

Экземпляр 1

Экземпляр 1

Глава 3. Параллелизация и группировка данных  83

Результат выполнения
Терминал ввода 1

Терминал ввода 2

car
truck

Терминал задания

SensorReader :: instance 0 -->
car
VehicleCounter :: instance 0 -->
car: 1

van
car

А вы
заметили закономерность: события автомобилей обрабатываются экземплярами оператора счетчика
автомобилей по
очереди?

SensorReader:: instance 0 -->
truck
VehicleCounter :: instance 1 -->
truck: 1
SensorReader:: instance 1 -->
van
VehicleCounter :: instance 0 -->
car: 1
van: 1
SensorReader:: instance 1 -->
car
VehicleCounter :: instance 1 -->
car: 1
truck: 1

Два блока чтения
данных с датчика
извлекают данные.
Исполнитель-источник

Исполнитель-оператор

Экземпляр 0

Экземпляр 0

Экземпляр 1

Два счетчика автомобилей получают данные для
обработки от диспетчера
событий.

Экземпляр 1

84  Часть I. Знакомство

События и экземпляры
Автомобиль обрабатывается
счетчиком VehicleCounter 0.
VehicleCounter :: экземпляр 0 -->
car: 1
… (Пропущено для краткости)

Другой автомобиль
обрабатывается счетчиком
VehicleCounter 1.

VehicleCounter:: экземпляр 1 -->
car: 1
truck: 1

Внимательно присмотревшись к результатам экземпляров счетчика автомобилей, вы увидите, что счетчики получают разные события. В зависимости от
того, как настроена система для реализации такого поведения, это может быть
нежелательно для стримингового задания. Позднее мы представим концепцию
группировки событий, которая поможет понять поведение и возможности для
улучшения системы. А пока достаточно знать, что любой автомобиль может
быть обработан любым из двух экземпляров пункта оплаты.
Еще одна важная концепция, которую необходимо понимать, — упорядочение
событий. У событий в системе существует определенный порядок — в конце
концов, все они обычно находятся в очередях. Как узнать, будет ли одно событие
обработано раньше другого? В общем случае действуют два правила.
zzВнутри

экземпляра порядок обработки гарантированно соответствует
исходному порядку (порядку во входящей очереди).
zzМежду экземплярами порядок обработки не гарантирован. Может оказаться, что более позднее событие будет обработано и/или завершено
раньше другого события, которое поступило до него, если два события
обрабатываются разными экземплярами.
Далее приводится более конкретный пример.

Глава 3. Параллелизация и группировка данных  85

Упорядочение событий
Терминал ввода 1

Терминал ввода 2

car
truck

Терминал задания

SensorReader :: instance 0 -->
car
VehicleCounter :: instance 0 -->
car: 1

van
car

SensorReader:: instance 0 -->
truck
VehicleCounter :: instance 1 -->
truck: 1
SensorReader:: instance 1 -->
van
VehicleCounter :: instance 0 -->
car: 1
van: 1

Исполнитель-оператор
Легковой
автомобиль,

Экземпляр 0

грузовик,

SensorReader:: instance 1 -->
car
VehicleCounter :: instance 0 -->
car: 1
truck: 1

микроавтобус,
легковой
автомобиль…

Экземпляр 1

Рассмотрим четыре события автомобилей, которые были введены в терминалах
ввода. Первый и третий автомобили — легковой (car) и микроавтобус (van); они
передаются экземпляру 0 VehicleCounter, тогда как второе и четвертое событие — грузовик (truck) и легковой автомобиль (car) — передаются экземпляру
1 VehicleCounter.
В ядре Streamwork два экземпляра оператора выполняются независимо. Ядра
Streaming обычно гарантируют, что первый и третий автомобили обрабатываются в порядке поступления, поскольку это происходит в одном экземпляре.
Тем не менее нет гарантии, что первый автомобиль (car) будет обработан до
второго (truck) или второй автомобиль (truck) будет обработан до третьего
(van), потому что процессы двух операторов не зависят друг от друга.

86  Часть I. Знакомство

Группировка событий
До сих пор параллелизированное стриминговое задание содержало экземпляры (instances) счетчика автомобилей, которые получали события в результате
случайной (вернее, псевдослучайной) диспетчеризации экземплярами счетчика
автомобилей.
Автомобиль обрабатывается счетчиком
VehicleCounter 0.
SensorReader:: экземпляр

0 -->

У стримингового
car
задания не существует VehicleCounter :: экземпляр 0 -->
car: 1
предсказуемого
поведения в отношении
… (пропущено для краткости)
того, будут ли данные
переданы
SensorReader:: экземпляр 1 -->
vehicleCounter 0
car
или vehicleCounter 1.

Другой автомобиль
обрабатывается
счетчиком
VehicleCounter 1.

VehicleCounter:: экземпляр 1 -->
car: 1
van: 1

Псевдослучайная маршрутизация приемлема во многих случаях, но иногда
предпочтительнее передавать события определенному последующему экземпляру. Эта концепция перенаправления событий экземплярам называется
группировкой событий. Термин «группировка» может показаться несколько
странным, поэтому попробуем пояснить: все события делятся на группы,
и каждая группа закрепляется за определенным экземпляром для обработки.
Существуют разные стратегии группировки событий. Наиболее часто применяются следующие:
группировка — события псевдослучайным образом распределяются между последующими компонентами.

zzСлучайная

по значениям полей — события предсказуемым образом
передаются одним и тем же последующим экземплярам на основании
значений конкретных полей события.

zzГруппировка

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

Глава 3. Параллелизация и группировкаданных  87

Случайная группировка
Если коротко, случайная группировка представляет собой случайное распределение элементов данных от компонента к последующему оператору. Она позволяет относительно равномерно распределять нагрузку между последующими
операторами.
Для реализации случайной группировки во многих фреймворках используется циклический алгоритм. В этой стратегии группировки последующие
экземпляры (то есть входящие очереди) выбираются равными частями и в циклическом порядке. По сравнению со случайной группировкой на основании
случайных чисел распределение становится более равномерным, а вычисления — более эффективными. Реализация представлена на следующей диаграмме. Обратите внимание: на диаграмме два грузовика (truck) подсчитываются
двумя разными экземплярами VehicleCounter.
Элементы очереди
попеременно передаются
разным экземплярам
счетчика.

Исполнитель-оператор
Экземпляр 0

Truck

Van

Car

Truck

Экземпляр 1

Очередь от предшествующего
компонента.

88  Часть I. Знакомство

Случайная группировка: внутренний механизм
Чтобы убедиться, что события равномерно распределяются между экземплярами, во многих стриминговых системах используется циклический алгоритм
для выбора следующего получателя события.
Счетчик начинается с 0. Его значение увеличивается
после каждого запроса для равномерного распределения
событий между экземплярами. После превышения
общего количества последующих экземпляров счетчик
обнуляется (параллелизм).
counter = 0;
while (e = readEvent()) {
event_group = counter;
counter++;
counter %= parallelism;
}

Группа событий 0

Исполнитель-оператор
Экземпляр 0

Входящие события
В ядре Streamwork
используется циклическая
реализация случайной
группировки.

Группа событий 1

Экземпляр 1

Глава 3. Параллелизация и группировка данных  89

Группировка по значениям полей
Случайная группировка хорошо работает во многих сценариях использования.
Тем не менее если вам потребуется механизм прогнозируемой отправки элементов, случайная группировка не подойдет. Для прогнозируемого шаблона
распределения, когда он потребуется для ваших целей, следует использовать
группировку по значениям полей. В этой стратегии решение о том, куда передавать данные, принимается на основе полей передаваемого элемента событий
(обычно эти поля выбирает разработчик). Во многих сценариях группировка
по значениям полей также называется группировкой по ключу.
Элементы
очереди р
аспредел
экземпляр
яются меж
ами на ос
ду
нове конк
информац
р
етной
ии (полей
или свойс
в событии
тв)
.

Легковые автомобили (car) и микроавтобусы
(van) всегда будут передаваться этому
экземпляру.
Исполнитель-оператор
Экземпляр 0

Van

Truck

Car

Van

Van

Truck

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

В стриминговом задании этой главы мы берем каждое событие автомобиля,
въезжающего на мост, и отправляем их счетчику автомобилей 0 или счетчику
автомобилей 1 в зависимости от типа автомобиля, так что события одного типа
автомобилей всегда будут передаваться одному экземпляру счетчика. При таком
подходе отдельные типы автомобилей подсчитываются на уровне экземпляров
(и с большей точностью).

90  Часть I. Знакомство

Группировка по значениям полей:
внутренний механизм
Чтобы события одного типа всегда назначались в одну группу (которая передается одному экземпляру), применяется хеширование. Это часто используемый
вычислительный метод, при котором большой диапазон значений (например,
строки) переносится на меньший набор значений (например, целые числа).
while (e = readEvent()) {
Группа событий 0
long hash = hashEvent(e);
event_group = hash % parallelism;
}

Исполнитель-оператор
Экземпляр 0

Входящие
события

Экземпляр 1
Группа событий 1

Важнейшая особенность хеширования заключается в том, что для постоянных
входных данных результат всегда будет неизменным. После получения результата хеширования (обычно это большое число, называемое ключом, например
98216) выполняются следующие вычисления:
key % parallelism
Делит key на parallelism и возвращает остаток,
на основании которого решается, какому экземпляру
последующего оператора должно быть назначено
событие. С двумя экземплярами событие
с ключом 98216 будет направлено во входящую
очередь экземпляра 0, потому что остаток от деления
98216/2 равен 0.

Глава 3. Параллелизация и группировка данных  91

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

Логика группировки
выполняется в фазе
перехода от входящей
очереди к исходящим
очередям.

Исполнитель экземпляра
предшествующего компонента
непрерывно направляет события
во входящую очередь.

Непрерывное движение
данных из одной очереди
в другую создает поток.

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

Исполнитель экземпляра
последующего компонента будет
непрерывно извлекать события из
назначенной ему очереди.

92  Часть I. Знакомство

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

Запуск процесса
диспетчера
событий.

Начало
Входящая очередь
Извлечение событий из входящей
очереди

Применение
стратегии
группировки

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

Выход
Да
Конец

Нет

Исходящая очередь

После выполнения
диспетчера
событий цикл
процесса начинается заново.

Глава 3. Параллелизация и группировка данных  93

Применение группировки по значениям полей
в задании
С применением группировки по значениям полей в задании намного проще поддерживать сводный счетчик для разных типов автомобилей, так как каждый тип
всегда будет направляться одному экземпляру. С использованием Streamwork
API группировку по значениям полей применить очень просто:
bridgeStream.applyOperator(
new VehicleCounter("vehicle-counter", 2, new FieldsGrouping() )
);
Применение группировки
по значениям полей

Единственное, что необходимо сделать, — добавить дополнительную переменную
при вызове функции applyOperator(). Все остальное сделает ядро Streamwork.
Помните, что стриминговые фреймворки помогают сосредоточиться на бизнес-логике, не отвлекаясь на реализацию ядер. Разные ядра могут использовать
разные способы применения группировки по значениям полей. Обычно стоит
поискать функцию с именем вида groupBy() или {операция}ByKey().
Чтобы выполнить пример кода, действуйте как раньше. Сначала выполните
приведенные ниже команды и откройте два терминала ввода, чтобы вводить
типы автомобилей. Затем компилируйте и выполняйте код в отдельном, третьем
терминале задания:
Терминал ввода 1

$ nc -lk 9990

Терминал ввода 2

$ nc -lk 9991

$ mvn package
$ java -cp target/gss.jar \
com.streamwork.ch03.job.ParallelizedVehicleCountJob3

94  Часть I. Знакомство

Упорядочение событий
Результат, который выводится при выполнении этих команд на терминале задания, выглядит примерно так:
Терминал
ввода 1

Терминал
ввода 2

Терминал задания

SensorReader :: instance 0 -->
car
VehicleCounter :: instance 0 -->
car: 1

car
truck
van

SensorReader:: instance 0 -->
truck
VehicleCounter :: instance 1 -->
truck: 1
car
truck

SensorReader:: instance 0 -->
van
VehicleCounter :: instance 0 -->
car: 1
van: 1
SensorReader:: instance 0 -->
car
VehicleCounter :: instance 0 -->
car: 2
van: 1
SensorReader:: instance 1 -->
truck
VehicleCounter :: instance 1 -->
truck: 2

Глава 3. Параллелизация и группировка данных  95

Сравнение поведения группировок
Сравним результаты выполнения заданий со случайной группировкой и группировкой по значениям полей и рассмотрим различия в поведении с одними
и теми же входными данными. На самом деле неважно, от какого терминала
поступил ввод, поэтому мы объединим их. Заметите ли вы, чем отличается
результат в этих двух заданиях?
Ввод задания: car truck van car truck …
Результат выполнения задания
со случайной группировкой

Результат выполнения задания
с группировкой по значениям полей

SensorReader :: instance 0 ->
car
VehicleCounter :: instance 0 ->
car: 1

SensorReader :: instance 0 ->
car
VehicleCounter :: instance 0 ->
car: 1

SensorReader:: instance 0 ->
truck
VehicleCounter :: instance 1 ->
truck: 1

SensorReader:: instance 0 ->
truck
VehicleCounter :: instance 1 ->
truck: 1

SensorReader:: instance 0 ->
van
VehicleCounter :: instance 0 ->
car: 1
van: 1
SensorReader:: instance 0 ->
car
VehicleCounter :: instance 1 ->
car: 1
truck: 1
SensorReader:: instance 1 ->
truck
VehicleCounter:: instance 0 ->
car: 1
truck: 1
van: 1

SensorReader:: instance 0 ->
van
VehicleCounter :: instance 0 ->
car: 1
van: 1
SensorReader:: instance 0 ->
Car
VehicleCounter :: instance 0 ->
car: 2
van: 1
SensorReader:: instance 1 ->
truck
VehicleCounter:: instance 1 ->
truck: 2

96  Часть I. Знакомство

Итоги
В этой главе были представлены основные концепции масштабирования стриминговых заданий. Масштабируемость — одна из главных сложностей для всех
распределенных систем, а параллелизация — основной метод масштабирования.
Вы узнали, как осуществлять параллелизацию компонентов в потоковом задании, а также познакомились с взаимосвязанными концепциями параллелизма
данных и заданий. Если в стриминговых системах термин параллелизм используется без уточнения «данных/задач», обычно он относится к параллелизму
данных.
При параллелизации компонентов также необходимо знать, как управлять
маршрутизацией событий или прогнозировать маршрутизацию в стратегиях
группировки событий для получения ожидаемых результатов. Для достижения прогнозируемости можно воспользоваться случайной группировкой или
группировкой по значениям полей. Также мы рассмотрели стриминговое ядро
Streamwork; вы увидели, как параллелизация и группировка событий реализуются с концептуальной точки зрения. Этот материал подготовит вас к следующим главам и использованию реальных стриминговых систем.
Параллелизм и группировка событий чрезвычайно важны, потому что они
помогают решать один из важнейших вопросов распределенных систем: вопрос
пропускной способности. Если в стриминговой системе обнаруживается узкое
место, его можно горизонтально масштабировать, повышая степень параллелизма, и система сможет обрабатывать события быстрее.

Упражнения
1. Почему так важна параллелизация?
2. Придумайте другую стратегию группировки. Попробуйте реализовать
ее в Streamwork.
3. В нашем примере для группировки по значениям полей используется
результат хеширования строки. Попробуйте реализовать другую группировку по значениям полей, в которой вместо хеширования используется
первый символ. Какими преимуществами и недостатками обладает новая
стратегия группировки?

Граф потока

4

В этой главе
99Разветвление потока по выходу.
99Объединение потока по входу.
99Графы и DAG (направленные ациклические графы).

«Плохие программисты думают о коде. Хорошие программисты
думают о структурах данных и их взаимосвязях».
Линус Торвальдс

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

98  Часть I. Знакомство

Система обнаружения мошеннических действий
с кредитными картами
Система подсчета автомобилей, построенная ЭйДжеем, произвела впечатление на Сида, и он думает о новых задачах, которые можно решить с помощью
технологий потоковой обработки. Сейчас его больше всего интересует задача
обнаружения попыток мошенничества. Но его беспокоит одно обстоятельство:
новая система будет более сложной и требует очень низкой задержки. Можно
ли решить задачу при помощи стриминга?

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

Пожалуй, эта
система будет посложнее
пункта оплаты, но проблем
быть не должно. Давайте
разберемся.

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

Блок чтения
данных

Счетчик
автомобилей

Глава 4. Граф потока   99

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

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

Анализаторы
оценивают риски
транзакций. В конце все
показатели рисков
объединяются в один
результат. Начнем
с нескольких простых
правил.

Раньше наши
задания выполнялись
последовательно; при высокой
нагрузке эта схема может
стать узким местом. Как
выполнять операции по
обнаружению мошеннических
действий более
эффективно?

100  Часть I. Знакомство

Процедура обнаружения
мошеннических действий

Сеть кредитных карт находится
между магазинами и банками. При входе
транзакций в сеть выполняется логика, которая
предоставляет банкам-плательщикам всю
возможную информацию. Эта информация
помогает банку решить, производить
оплату или нет.

Физические магазины,
интернет-магазины
и даже мобильные
устройства принимают
оплату по кредитным
картам.

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

Банки принимают
решение об одобрении
транзакции на основании информации,
собранной сетью
кредитных карт.

Глава 4. Граф потока   101

Потоковая обработка не всегда
прямолинейна
Систему можно построить по образцу системы пунк­
тов оплаты. Сначала компонент — источник транзак­
ций отвечает за получение событий транзакции от
внешних систем. Затем анализаторы применяются
по очереди, а в события добавляются оценки рисков.
Наконец, агрегатор оценок принимает решение.
Такая схема работает, но она не идеальна. В дальнейшем в нее будут добавляться новые анализаторы,
список будет расти, а время задержки между конечными точками будет возрастать. Кроме того, большое
количество анализаторов затрудняет обслуживание
системы.

Источник
транзакций

Анализатор
среднего чека

Анализатор оконного
расстояния
Анализатор оконного
числа транзакций

Агрегатор оценок

Первое решение
не идеально. Добавление каждого нового
анализатора повышает
задержку.

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

Анализатор
среднего чека

Анализатор оконного
расстояния

Агрегатор оценок

Анализатор оконного
числа транзакций

102  Часть I. Знакомство

Механизм работы системы

1. Шлюз API
принимает
транзакции.

6. Банки принимают решение об
оплате транзакции на основании
оценки риска.

2a. Шлюз API направляет
транзакции презентатору
транзакций через HTTP.

Презентатор
транзакций

Шлюз API
2b. Транзак­
ция асинхронно направляется заданию
обнаружения
мошеннических действий.

7. Решения
отправляются обратно.
Источник
транзакций
Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

3. Стриминговое задание
обрабатывает транзакции, создает
оценку риска мошеннических
действий для каждой транзакции
и сохраняет ее в базе данных.

5. Презен­
татор
транзакций
считывает
оценку риска
из базы
данных
и передает
ее банкуплательщику
наряду
с самой
транзакцией.

4. В базе данных оценок
хранится оценка риска
мошеннических действий
для каждой транзакции.

Глава 4. Граф потока   103

Подробнее о задании обнаружения
мошеннических действий
Рассмотрим подробнее задание обнаружения мошеннических действий и функции всех компонентов.

Как распознать подозрительную транзакцию?
Оценки лежат в диапазоне 0–3. Оценка 0 означает, что анализатор не обнаружил признаков мошенничества, а оценка 3 — что признаки мошенничества
обнаружены всеми тремя анализаторами. Каждый анализатор увеличивает
оценку на 1 балл. Транзакция считается потенциально мошеннической, если
ее оценка составляет 2 и выше.

Источник транзакций извлекает события при
поступлении их в шлюз API системы кредитных
карт. Он создает 3 разных экземпляра одной
транзакции и распределяет их между
анализаторами.
Шлюз
API
Анализатор среднего
чека проверяет сумму
транзакции. Если она не
соответствует обычной
истории расходов
клиента, то оценка
увеличивается на 1.
Анализатор
среднего
чека

Источник
транзакций

Анализатор
оконного
расстояния

Анализатор оконного числа
транзакций проверяет транзакции
с одного счета в заданном
временном окне. Маловероятно,
что клиент будет использовать
одну карту несколько раз за
небольшой промежуток времени.
Если анализатор обнаруживает
такое поведение, то оценка
увеличивается на 1.

Анализатор
оконного числа
транзакций

Агрегатор
оценок
Анализатор оконного расстояния проверяет
транзакции с одного счета в заданном
временном окне. Маловероятно, что кто-то
прокатает карту в одном считывателе,
а вскоре в другом, расположенном за
200 километров от первого. Если анализатор обнаруживает такое поведение, то
оценка увеличивается на 1.

Агрегатор оценок некоторое время
ожидает данные от каждого пред­
шествующего анализатора, после чего
вычисляет общую итоговую оценку
риска мошеннических действий.
По завершении времени ожидания
оценка записывается в базу данных.

104  Часть I. Знакомство

Новые концепции
В главе 2 вы узнали об активных частях стриминговой системы, источниках
данных и операторах, а также связях между ними. Кроме того, мы рассмотрели,
как они реализуются внутри ядра. Все это очень важные концепции, которые
будут использованы на протяжении всей книги.
В этой главе мы рассмотрим стриминговые задания, имеющие более сложную структуру. Новая диаграмма кажется более сложной по сравнению с прежней линейной диаграммой. Это действительно так, но не стоит беспокоиться.

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Прежде чем продолжить, рассмотрим несколько новых концепций, представленных на диаграмме.
zzПредшествующие и последующие компоненты.
zzРазветвление потока по выходу
zzОбъединение потока по входу.
zzГрафы и DAG1 (направленный ациклический граф).

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

1

DAG — directed acyclic graph. — Примеч. ред.

Глава 4. Граф потока   105

Предшествующие и последующие компоненты
Начнем с двух новых концепций: предшествующих (upstream) и последующих
(downstream) компонентов. Они довольно просты.
В целом стриминговое задание выглядит как серия событий, проходящих
через компоненты. Для каждого компонента другой компонент (или компоненты), находящийся непосредственно перед ним, называется предшествующим, а компонент после него называется последующим. События проходят от
предшествующих компонентов к последующим. Если взглянуть на диаграмму
стримингового задания, построенного в предыдущей главе, мы увидим, что
события проходят от блока чтения данных с датчика к счетчику автомобилей.
Следовательно, блок чтения данных является предшествующим компонентом,
а счетчик автомобилей — последующим.
Стриминговое
задание

Блок чтения данных
является предшествующим компонентом
счетчика автомобилей.

Блок чтения
данных

Счетчик
автомобилей

Счетчик автомобилей
является последующим
компонентом блока
чтения данных с датчика.

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

106  Часть I. Знакомство

Разветвление и объединение потока
Рассмотрим новую диаграмму, которую предлагает ЭйДжей. В целом она сильно отличается от предыдущего задания. Главное отличие — в том, что у одного
компонента может быть несколько предшествующих или последующих компонентов.
Компонент — источник транзакций соединен с тремя последующими компонентами; это называется разветвлением потока (stream fan-out). Аналогичным образом агрегатор оценок имеет три предшествующих компонента (также
можно сказать, что у трех анализаторов один последующий компонент). Это
называется объединением потока (stream fan-in).
Разветвление потока
означает, что компонент
имеет несколько последующих компонентов. В данном
случае поток разветвляется
между источником транзакции и анализаторами.

Источник
транзакций

Анализатор
среднего
чека
Объединение потока
означает, что компонент
имеет несколько предшествующих компонентов. В данном случае
поток сливается между
анализаторами и агрегатором оценок.

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Не уверена, что
я правильно понимаю эту
диаграмму. Одно событие
передается всем трем
анализаторам?

Глава 4. Граф потока   107

Графы, направленные графы и DAG
A
B

E

C

D

Последние три концепции, которые будут рассмотрены в этой главе, — графы, направленные графы
и направленные ациклические графы (DAG). Граф
представляет собой структуру данных, состоящую
из вершин (узлов) и ребер (сторон). Две структуры
данных, часто используемые разработчиками, — дерево и список — являются примерами графов.

Если каждое ребро графа имеет определенное направление (от одной вершины к другой), такой граф наB
A
зывается направленным. Диаграмма справа — пример
направленного графа с пятью вершинами и семью
направленными ребрами.
C
Особую разновидность направленных графов составA
ляют направленные ациклиD
ческие графы (DAG). DAG
представляет собой направленный граф, в котором нет
E
B
C
D
ни одного направленного
цикла; это означает, что в таком графе невозможно начать с некоторой вершины
и вернуться к ней по направленным ­ребрам.
E
Граф, изображенный на диаграмме слева, является
DAG, потому что ни от одной вершины невозможно
провести путь, который бы возвращался к ней самой. На диаграмме с направленным графом вершины C, D и E образуют цикл; следовательно, этот граф не
является DAG. Обратите внимание: в графе существует еще один цикл, потому
что от вершины B отходит ребро, которое возвращается к этой же вершине.
Многие стриминговые
задания можно представить
в виде DAG.

108  Часть I. Знакомство

DAG в системах потоковой обработки
Структура данных DAG играет важную роль в компьютерной теории и системах
потоковой обработки. Мы не будем вдаваться в математические подробности,
но важно знать, что термин DAG часто встречается в области стриминга.
Прохождение событий в системе удобно представлять в виде направленного графа. Цикл в направленном графе означает, что события могут возвращаться и снова обрабатываться тем же компонентом. К таким ситуациям следует
подходить очень осторожно из-за дополнительной сложности и рисков. Иногда циклы бывают необходимы, но такие случаи встречаются относительно
редко.
В большинстве систем потоковой обработки отсутствуют циклы; следовательно, такие системы могут быть представлены в виде DAG.
Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Начиная с этой главы, каждый раз, когда мы будем рисовать диаграмму задания,
это будет DAG. Он будет включать только логические компоненты задания без
объектов ядра — таких как исполнители и диспетчеры событий (если только
они не являются необходимыми) — см. приведенную выше диаграмму. Таким
образом, мы можем сосредоточиться на бизнес-логике, не беспокоясь о подробностях уровня ядра. Параллелизм сюда не включен, потому что он не связан
с бизнес-логикой.

Глава 4. Граф потока   109

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

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

В общем случае стриминговое задание можно представить
в виде графа, а конкретно — направленного ациклического
графа (DAG), то есть направленного графа без направленных
циклов. Вершины представляют компоненты, а ребра — связи
между компонентами.

Аналогичным образом
компонент агрегатора
оценок имеет несколько
предшествующих
компонентов. Такая
структура называется
объединением потока.

110  Часть I. Знакомство

Разветвление потока к анализаторам
Пришло время внимательнее
присмотреться к нашей системе,
начиная с места разветвления
потока. В системе обнаружения
мошеннических действий разветвление потока происходит
между компонентом-источником и операторами-анализаторами. Средства Streamwork API
позволяют просто связать поток, поступающий от компонента-источника, с анализаторами.
Схема связывания выглядит,
как показано ниже.

Разветвление
потока

Анализатор
среднего
чека

Источник
транзакций

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Job job = new Job();
Stream transactionOut = job.addSource(new TransactionSource());
Stream evalResults1 = transactionOut.applyOperator(new
AvgTicketAnalyzer());
Stream evalResults2 = transactionOut.applyOperator(new
WindowedProximityAnalyzer());
Stream evalResults3 = transactionOut.applyOperator(new
WindowedTransactionAnalyzer());
Применение нескольких
операторов к одному потоку.

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

Разветвление потока — один компонент с несколькими последующими компонентами.

Глава 4. Граф потока   111

Что происходит внутри ядра
Настоящая работа выполняется внутри ядра. В ядре Streamwork при подключении к потоку нового оператора создается новая очередь между диспетчером
событий оператора и исполнителями экземпляров компонента, генерирующего
поток. Иначе говоря, один исполнитель экземпляра может отправлять события
нескольким исходящим очередям.
Исполнитель экземпляра
компонента.

Диспетчер событий
извлекает события из
связанной входящей
очереди и перенаправляет их экземплярам.

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

Извлечение событий
из входящей
очереди

Выход

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

Выход

112  Часть I. Знакомство

Проблема эффективности
Теперь у каждого анализатора имеется своя копия событий транзакции, к которой он
может применять свою логику оценПохоже, это
ки. Тем не менее такое решение не
решение требует
очень эффективно.
слишком больших затрат
Каждое событие является запамяти. Как сделать его
писью транзакции. Оно содержит
более эффективным?
большой объем информации о транзакции: идентификатор товара, идентификатор транзакции, время транзакции,
сумму, счет, категории товаров, местонахождение покупателя и т. д. В результате события имеют довольно большой
размер:
class TransactionEvent extends Event {
long transactionId;
float amount;
Date transactionTime;
long merchandiseId;
long userAccount;
……
}

В текущем варианте каждое событие многократно дублируется, поскольку дубликаты направляются в разные очереди. Это позволяет разным анализаторам
обрабатывать каждое событие асинхронно. Эти «расширенные» события передаются по сети, загружаются и обрабатываются анализаторами. Кроме того,
некоторые анализаторы не собираются (или не могут) обрабатывать некоторые
события, но эти события все равно передаются и обрабатываются. В результате
память и ресурсы сети используются неэффективно. Схему можно усовершенствовать, что важно при высоком уровне трафика.

Глава 4. Граф потока   113

Разветвление с несколькими потоками
При разветвлении потока исходящие очереди могут различаться. Слово различаться здесь имеет два смысла.
zzСгенерированное

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

zzКроме того, события в разных исходящих очередях, направленных к раз-

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

Источник
транзакций

В первой версии потоки
имеют одинаковый набор
событий с одинаковыми
структурами данных.
Анализатор
среднего
чека
Два DAG внешне похожи,
но потоки различаются.

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Источник
транзакций

Во второй версии потоки
могут различаться
(использовать разные
структуры данных
события и разные наборы
событий).

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

114  Часть I. Знакомство

Что происходит внутри ядра (еще раз)
Вы узнали, что один исполнитель компонента может иметь несколько исходящих очередей. Ранее исполнитель просто направлял одно событие во все исходящие очереди, связанные с диспетчерами событий последующих компонентов.
Теперь для поддержки нескольких потоков исполнитель должен взять события,
сгенерированные каждым компонентом, и поместить их в соответствующие
исходящие очереди.
Объект-компонент предоставляет эту информацию по каналам (channels).
Разные события направляются в разные каналы, и последующие компоненты
могут выбрать, из какого канала получать события.
В первой версии все
очереди использовали один
набор событий с одинаковой
структурой данных.

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

Исполнитель-источник
Источник

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

Исполнитель-источник
Источник

Это многоканальное разветвление обес­
печивает большую гибкость. Она предоставляет дополнительные возможности
повысить эффективность задания.

Каждая исходящая очередь
может получать события от
любого исходящего канала
объекта-источника.

Глава 4. Граф потока   115

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

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

нужный канал, подключаясь к нему вызовом applyOperator().
Ранее на выходе компонента
формировался список событий.
Теперь это карта, связывающая
имена каналов со списками
событий:
default: [……]
amount_only: [……]
location_related: [……]

Когда компонент добавляется
в задание (к исходящему
потоку его предшествующего
компонента), он может выбрать
нужный канал и зарегистрироваться в нем, например:
"location_related"

Выполнение
логики компонента
События направляются в исходящую очередь

Выход

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

116  Часть I. Знакомство

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

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Выбирается другой канал
для передачи событий.

eventCollector.add(new DefaultEvent(transactionEvent));
eventCollector.add("location _ based",
new LocationalEvent(transactionEvent);
События в канале используют
разные структуры данных.

Тогда при добавлении анализатора в стриминговое задание при помощи функции applyOperator() можно сначала указать канал:
Job job = new Job();
Stream transactionOut = job.addSource(new TransactionSource());
Если канал не указан при применении оператора, используется канал по умолчанию.
Stream evalScores1 = transactionOut
.applyOperator(new AvgTicketAnalyzer());
Stream evalScores2 = transactionOut
Для применения
.selectChannel("location _ based")
.applyOperator(new WindowedProximityAnalyzer()); оператора указывается конкретный канал.
Stream evalScores3 = transactionOut
.applyOperator(new WindowedTransactionAnalyzer());

Глава 4. Граф потока   117

Объединение потока в агрегаторе оценок
Анализаторы получают события
Источник
транзакций и выполняют вычистранзакций
ления. Результат работы каждого
анализатора представляет собой
оценку риска (risk score) для каждой транзакции. В нашей системе
Анализатор
Анализатор
Анализатор
оценки рисков для всех транзаксреднего
оконного
оконного числа
ций передаются компоненту —
чека
расстояния
транзакций
агрегатору оценок для принятия
решения. Если обнаруживаются
признаки мошенничества, в базу
данных записывается информаАгрегатор
Объединение
оценок
ция о потенциальном нарушении.
потока
Из диаграммы видно, что оператор агрегатора оценок получает ввод от нескольких предшествующих компонентов — анализаторов. Также
можно рассмотреть эту структуру под другим углом: исходящие потоки анализаторов объединяются, а все их события передаются оператору агрегатора
оценок. Это называется объединением потока.
Стоит упомянуть о том, что в операторе агрегатора оценок события от разных
потоков обрабатываются одинаково. Также возможен другой случай: события
разных входящих потоков могут содержать разные данные и должны использоваться по-разному. Во втором случае используется более сложное объединение
потока, заслуживающее отдельной главы. Сейчас сосредоточимся на простом
случае.
Stream evalScores1 = ……
Stream evalScores2 = ……
Stream evalScores3 = ……

Несколько потоков объединяются
в один объект Streams.

Operator aggregator = new ScoreAggregator(
"aggregator", 2, new GroupByTransactionId());
Streams.of(evalScores1, evalScores2, evalScores3)
.applyOperator(aggregator);

Оператор ScoreAggregator применяется к объекту Streams.
Обратите внимание: GroupByTransactionId должен выступать
подклассом FieldsGrouping, чтобы оценки конкретной
транзакции гарантированно передавались одному экземпляру
агрегатора.

118  Часть I. Знакомство

Объединение потоков в ядре
В ядре Streamwork объединение потоков реализовано достаточно просто. Входящая очередь компонента (связанная с его диспетчером событий) может использоваться несколькими предшествующими компонентами. Когда событие
генерируется любым из его предшествующих компонентов (а на самом деле
экземпляром самого компонента), событие помещается в очередь. Последующий
компонент извлекает события из очереди и обрабатывает их. Он не различает,
кто именно поместил события в очередь.

Исполнитель компонента

Исполнитель компонента

Компонент

Компонент

Затем события в очереди
поступают в последующий
компонент.

Исполнитель компонента

Компонент

Исполнитель компонента

Компонент

События, генерируемые каждым
предшествующим компонентом,
помещаются в одну исходящую
очередь.

Как упоминалось ранее, очередь реализует логическую изоляцию между предшествующими и последующими компонентами.

Глава 4. Граф потока   119

Краткий обзор разновидности объединения
потоков — соединения
Все это хорошо,
конечно… Но если
потребуется объединять
события разных типов?

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

Ранее говорилось о том, что кроме объединения потока, использованного в рассмотренном примере, существует более сложная разновидность объединения.
Мы вкратце познакомим вас с ней, чтобы вы лучше понимали все типы объединений и разветвлений.
При простом объединении все входящие события имеют одну структуру данных и обрабатываются одинаково. Иначе говоря, входящие потоки одинаковы.
А если они отличаются друг от друга и их необходимо объединить? Если вы
работали с базами данных, то хотя бы в общих чертах пред{
ставляете себе операцию соединения (join), выполняемую
user_id: 001,
с несколькими таблицами. Если вы не знаете, о чем идет
name: Tim
речь, или успели забыть (ведь всем известно, насколько }
надежна человеческая память), не беспокойтесь — это не обязательно.
В работе с базами данных операция соединения используется для комбинирования столбцов нескольких таблиц. Например, если у вас есть таблица со столбцами
Оператор
user-id и name и еще одна таблица со
соединения
столбцами user-id и phonen u m b e r , эти таблицы можно {
user_id: 001,
соединить по столбцу user-id
phone_number: 12345
двух исходных таблиц и создать }
новую таблицу со столбцами
user-id, name и phone-number.
{
В мире потоковой обработки операция соединения реuser_id: 001,
name: Tim,
шает похожую задачу соединения полей нескольких
phone_number: 12345
источников данных.
}

120  Часть I. Знакомство

Тем не менее по сравнению с таблицами баз данных потоки намного динамичнее.
События принимаются и обрабатываются непрерывно, а соответствию полей нескольких непрерывных источников данных приходится уделять намного больше
внимания. Здесь мы объясним базовую концепцию соединения, а дальнейшее
исследование этой темы будет вынесено в отдельную главу.

Система в целом
Мы рассмотрели разветвление и объединение потоков по отдельности, а сейчас
взглянем на систему в целом. На высоком уровне задание можно представить
в виде следующего графа; иногда такой граф называется логическим планом.
Он представляет логическую структуру задания (компоненты и связи между
ними).
Компонент — источник транзакций
принимает события транзакций от
внешних систем. События проходят
предварительную обработку и направляются в различные исходящие потоки.

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

Каждый компонентанализатор оценивает
транзакцию и создает
оценку риска мошеннических действий.

Источник
транзакций

Анализатор
среднего
чека

Оценки всех анализаторов передаются агрегатору для принятия
окончательного решения.

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций
Агрегатор принимает
решение и отправляет
его во внешние
системы.

Существующие системы обнаружения мошеннических действий постоянно
развиваются, и в них добавляются новыеанализаторы. Благодаря использованию Streamwork или других потоковых фреймворков добавлять, удалять или
заменять анализаторы становится довольно просто.

Глава 4. Граф потока   121

Графы и стриминговые задания
Понимая объединение и разветвление потоков, мы можем строить потоковые
системы на уровне более сложных и общих структур графов. Это очень важный
шаг вперед, потому что эта новая структура позволяет решать прикладные задачи.
Ниже приведены DAG двух стриминговых систем. Подумайте, какие виды
систем они могут представлять?

122  Часть I. Знакомство

Примеры систем
Сказать по правде, эти графы
могут представлять массу вещей! Назовем несколько возможных вариантов.
Первая диаграмма может
представлять простую систему контроля и наблюдения
за дорожным движением.
События, поступающие от
датчиков, передаются трем
основным обработчикам:
детектору происшествий,
детектору пробок и оптимизатору узлов. У детектора
пробок имеется геолокационный агрегатор, используемый
в качестве препроцессора.
Блок чтения
данных
с датчика 1

Датчик
дорожного
движения

Детектор
происшествий

Геолокационный
агрегатор

Оптимизатор
узлов

Детектор
пробок

Блок чтения
данных
с датчика 1

Блок чтения
данных
с датчика 1

Блок чтения
данных
с датчика 1

Адаптер

Система
обнаружения
сбоев

Вторая диаграмма может представлять систему обнаружения сбоев, которая обрабатывает события от блоков чтения данных с датчиков в нескольких версиях.
События, генерируемые первыми двумя версиями, несовместимы с датчиком;
следовательно, для них нужен адаптер. В такой системе все блоки чтения данных с датчиков могут работать совместно, в ней легко добавлять новые версии
или убирать старые.

Глава 4. Граф потока   123
В общем и целом стриминговые задания не очень сложны. Приведенные примеры значительно упрощены по сравнению с реальными системами. Тем не менее хочется верить, что вы получили некоторое представление о возможностях
стриминговых систем. В простейшем варианте стриминговые задания состоят из
компонентов и связей между ними. После настройки и запуска стримингового
задания события протекают через компоненты по связям постоянно.

Итоги
В этой главе мы перешли от списочной структуры систем, которые рассматривались в предыдущих главах, к более общему типу структуры системы — графам.
Так как события в системах направляются от источников к операторам, в большинстве случаев стриминговое задание можно представить в виде направленного ациклического графа (DAG). Большинство прикладных заданий имеет
архитектуру графа, следовательно, этот этап чрезвычайно важен.
Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

В отличие от компонентов в списочной структуре, в заданиях со структурой
графа компонент может связываться с несколькими предшествующими и последующими компонентами. Такие типы соединений называются объединением
и разветвлением потока. Потоки, входящие в компонент или выходящие из него,
могут использовать одинаковые или разные типы событий.
Кроме того, мы подробнее рассмотрели фреймворк Streamwork, чтобы показать, как ядро обрабатывает связи между компонентами. Надеемся, это поможет
вам понять общий принцип работы стриминговых систем.

124  Часть I. Знакомство

Упражнения
1. Попробуйте добавить новый анализатор в задание обнаружения мошеннических действий.
2. Каждый анализатор получает событие транзакции от компонента —
источника транзакций и создает оценку. Два анализатора используют
одинаковые вычисления для формирования своих оценок. Попробуйте
изменить задание для этого случая. Результат будет выглядеть так, как
показано на схеме:

Семантика доставки

5

В этой главе
99Семантика доставки и ее влияние.
99Семантика доставки «не более одного».
99Семантика доставки «не менее одного».
99Семантика доставки «ровно один».

«Времени на то, чтобы сделать все как следует, никогда
не хватает, зато всегда есть время на то,
чтобы все переделать».
Джек Бергман

Компьютеры хорошо справляются с точными вычислениями. Тем не менее
при совместной работе компьютеров в распределенных системах (каковыми
являются многие стриминговые системы) проблема точности немного (а на
самом деле очень заметно!) усложняется. Иногда стопроцентная точность не
нужна, потому что необходимо выполнять более важные требования. Кто-то
спросит: «Зачем нужны неправильные ответы?» Это отличный вопрос, и его
необходимо задавать себе при проектировании стриминговой системы. В этой
главе обсуждается важная тема, относящаяся к точности в потоковых системах, — семантика доставки.

126  Часть I. Знакомство

Требования к задержке в системе обнаружения
мошеннических действий
В предыдущей главе наша команда
построила систему обнаружения
мошеннических действий с кредитными картами, которая может принимать решение в пределах 20 миллисекунд для каждой
транзакции и сохранять результат
в базе данных. При построении
любой распределенной системы
следует задать важный вопрос: что
делать, если произойдет сбой?
Анализатор
среднего чека

Презентатор
транзакций

Шлюз API

Источник
транзакций

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Низкая задержка критична для
нашей системы. Похоже, наша система
способна завершить процесс в пределах 20 миллисекунд для каждой транзакции. Выглядит
неплохо, не правда ли?
Нам придется
учесть обработку ошибок и пожертвовать точностью для соблюдения
требований, если
что-то пойдет
не так.
Звучит неплохо…
Постойте. Пожертвовать точностью?!!
В каком смысле?

Глава 5. Семантика доставки  127

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

ние
т в зада
я входя
и
т
й
ы
и
б
ц
о
1. С
там.
анзак
мпонен
нике тр
в источ еляются по ко
ед
и распр

Источник
транзакций

Анализатор
среднего
чека

3. Оцен
ки соби
раются
агрега
в
торе дл
я вычис итоговом
результ
ления
ата.

Презентатор
транзакций

Шлюз
API

Анализатор
оконного
расстояния

2. Собы
ти
распре я
деляют
ся
по посл
едующ
им
операт
орам.

Анализатор
оконного числа
транзакций

Агрегатор
оценок

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

128  Часть I. Знакомство

О точности
В распределенных системах часто приходится идти на компромисс. Главной
проблемой любой стриминговой системы становится надежная обработка событий. Стриминговые фреймворки помогают обеспечить надежное выполнение
задания в большинстве случаев, но вам нужно правильно определить потребности. Мы привыкли, что компьютеры выдают точные результаты; а значит, очень
важно понимать, что в стриминговых системах точность не абсолютна. Там, где
это необходимо, ею приходится жертвовать.

Выходит, результаты могут
быть неточными?

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

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

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Без паники! Сейчас мы подробно рассмотрим вышесказанное.

Глава 5. Семантика доставки  129

Частичный результат
Частичный результат появляется из-за неполноты данных; его точность не
гарантирована. На диаграмме ниже приведен пример частичного результата
при возникновении проблемы в анализаторе среднего чека.
2. Анализатор оконного расстояния
и анализатор оконного числа
транзакций обрабатывают все три
транзакции и успешно отправляют
оценки.

2, T3]
и [T1, T
и
ц
к
а
з
ран
.
1. Три т
стему
нентам
ют в си
а
п
у
о компо
т
п
я
пос
с
т
ю
еделя
и распр

Источник
транзакций

Анализатор
среднего
чека

3. Однако у анал
изатора средне
го
чека возникаю
т проблемы
с обработкой тр
анзакции T2.
Только оценки
транзакций T1
и T3
были успешно
обработаны
и отправлены аг
регатору оценок
.

В стриминговых системах
часто приходится принимать
компромиссные решения.

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок
4. Агрегатор оценок получает оценки T1 и T3
от всех анализаторов, но у него нет одной
оценки T2. Так как требования нарушаться не
должны, агрегатор объединяет оценки на
основании существующих данных и сохраняет
результаты в базе данных. Результаты T1 и T3
точны, но результат T2 является частичным,
и его точность не гарантирована.

Понятно. Потенциально
неточный результат T2
предпочтительнее ожидания точных
результатов и нарушения
требования о 20-миллисекундной
задержке.

130  Часть I. Знакомство

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

Шлюз
API

Источник
транзакций

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Анализатор
использования
системы

Блок записи
данных об
использовании

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

Задание контроля
использования
системы предоставляет информацию об
использовании
всей системы
в реальном
времени. Оно
помогает оценить
текущую нагрузку системы
в любой момент
времени.

Глава 5. Семантика доставки  131

Новое задание контроля использования системы
Новое задание используется внутренними механизмами для наблюдения за текущей нагрузкой на систему. Начнем с двух критических показателей, которые
нас интересуют в первую очередь.
zzСколько транзакций было обработано? Это число важно для понимания

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

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

Логика подсчета сосредоточена в операторе SystemUsageAnalyzer:
class SystemUsageAnalyzer extends Operator {
private int transactionCount = 0;
private int fraudTransactionCount = 0;
public void apply(Event event, EventCollector collector) {
String id = ((TransactionEvent)event).getTransactionId();
transactionCount++;
Thread.sleep(20);

Подсчет
транзакций.

Приостановка на 20 миллисекунд для завершения обработки заданием обнаружения мошеннических действий.

boolean fraud = fraudStore.getItem(id); Чтение результата обнаружения
if (fraud) {
fraudTransactionCount++;
}

}

}

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

collector.emit(new UsageEvent(
transactionCount, fraudTransactionCount));

Подсчет мошеннических транзакций, если результат равен true.

Оператор выглядит очень просто:
zzДля каждой транзакции значение transactionCount увеличивается на 1.
zzЕсли для транзакции обнаруживается попытка мошенничества, то значе-

ние fraudTransactionCount увеличивается на 1.
Однако вызов getItem() в функции может завершиться неудачей. Поведение
задания при обнаружении ошибки — ключевое различие разных семантик доставки.

132  Часть I. Знакомство

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

zzЗадержка

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

zzТочность

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

Можно ли
настроить точность
в нашем стриминговом
ядре?

Да, это важнейшая
настройка, которая поддерживается в большинстве ядер.
Обычно она называется семантикой
доставки или гарантией
доставки.

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

Глава 5. Семантика доставки  133

Новые концепции: количество доставок
и обработок
Концепции количества обработок и количества доставок помогают понять
реальный смысл семантики доставки.
zzКоличество обработок показывает, сколько раз событие было обработано

компонентом.
zzКоличество доставок показывает, сколько раз результат был сгенерирован

компонентом.
Эти два числа совпадают в большинстве случаев, но не всегда. Например, на
приведенной ниже блок-схеме логики оператора SystemUsageAnalyzer может
оказаться, что на шаге получения результата возникли проблемы из-за недоступности базы данных. Если возникает ошибка, событие обрабатывается один
раз (но безуспешно) и результат не генерируется. В результате количество обработок будет равно 1, а количество доставок — 0. Также можно рассматривать
количество доставок как количество успешных обработок.

Начало

1. Иногда на этом ша
ге
происходит ошибка
. В таком
случае событие об
рабатывается, но без успеха
, и результат не генериру
ется.
В итоге количество
обработок и количество до
ставок
не совпадают.

Получение
результата
Подсчет
мошеннических
транзакций
Подсчет общего
количества
транзакций

Отправка
результатов

Конец

енерильтат г
я
2. Резу отправляетс
и
я
й
с
о
т
руе
пешн
при ус
только е события.
тк
обрабо

134  Часть I. Знакомство

Новая концепция: семантика доставки
Мы подходим к ключевой концепции этой главы: семантике доставки, также называемой гарантией доставки. Это очень важная концепция стриминговых заданий, которую необходимо понять, прежде чем переходить к более сложным темам.
Семантика доставки определяет то, как стриминговые ядра гарантируют
доставку (или успешную обработку) событий в стриминговых заданиях. Существуют три основные категории семантики доставки. Сейчас мы рассмотрим их
кратко, а подробности разберем позже.
zz«Не

более одного» (at-most-once) — стриминговые задания гарантируют,
что каждое событие будет обработано не более одного раза, но не гарантируют, что оно будет обработано успешно.
zz«Не менее одного» (at-least-once) — стриминговые задания гарантируют,
что каждое событие будет успешно обработано по крайней мере один раз,
но не гарантируют количество его обработок.
zz«Ровно один» (exactly once) — стриминговые задания гарантируют, что
каждое событие будет успешно обработано один и только один раз (по
крайней мере внешне). В некоторых фреймворках используется термин
«фактически один» (effectively-once). Если вам кажется, что это слишком
хорошо, чтобы быть правдой, поскольку условия «ровно один» чрезвычайно трудно добиться в распределенных системах, или что два термина
противоречат друг другу, — вы определенно не одиноки. О том, что в действительности означает семантика «ровно один», мы расскажем позже
в соответствующем разделе.
Вариант «ровно
один» кажется идеальным.
Зачем вообще использовать варианты
«не более одного» и «не менее
одного»?

Хороший
вопрос! Несомненно,
«ровно один» — отличный
вариант. Однако за удобство приходится расплачиваться затратами
ресурсов, к тому же есть и другие
факторы. Разработчику очень важно
понимать, что же ему на самом
деле нужно. Пора поговорить
об этом.

Глава 5. Семантика доставки  135

Выбор семантики
Правда ли, что семантика «ровно один» идеально подходит для всего? Ее преимущества очевидны: результаты гарантированно точны, а правильный ответ
лучше неправильного.
С семантикой «ровно один» стриминговое ядро сделает все за вас, и беспокоиться не о чем. Для чего тогда два других варианта? Зачем нужно знать о них?
Дело в том, что все варианты полезны, потому что в разных стриминговых
системах действуют разные требования.
Ниже приведена простая таблица достоинств и недостатков каждого варианта. Мы еще вернемся к этой таблице позже.
Семантика
доставки

Не более одного

Не менее одного

Ровно один

Точность

Точность не гарантирована из-за пропуска событий

Точность не гарантирована из-за дублирования событий

(На первый взгляд)
гарантирован точный
результат

Задержка
(при возникновении
ошибки)

Устойчивость
к сбоям; отсутствие
задержки при возникновении ошибок

Чувствительность
к сбоям; возможны
задержки при возникновении ошибок

Чувствительность
к сбоям; возможны
задержки при возникновении ошибок

Сложность

Очень простая

Средняя (в зависимо- Высокая
сти от реализации)

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

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

136  Часть I. Знакомство

«Не более одного»
Начнем с простейшей семантики: «не более одного». В заданиях с такой семантикой события не отслеживаются. Ядро прикладывает максимум усилий
к тому, чтобы обрабатывать каждое событие успешно, но если все же произойдет ошибка, ядро переключается на другое событие. На следующей диаграмме
показана реализация обработки событий ядром Streamwork для заданий «не
более одного».
Исполнители и диспетчеры событий механически передают
события последующим процессам (имеется в виду, что они не
сохраняют информацию о том, что куда направлено). Они просто
принимают и перемещают события как можно быстрее.

Исполнитель экземпляра

Экземпляр

Диспетчер
событий

Исполнитель экземпляра

Экземпляр

Исполнители и диспетчеры событий механически передают
события последующим процессам (имеется в виду, что они не
сохраняют информацию о том, что куда направлено). Они просто
принимают и перемещают события как можно быстрее.

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

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

Глава 5. Семантика доставки  137

Задание обнаружения мошеннических действий
Рассмотрим задание обнаружения мошеннических действий
с семантикой «не более одного».
Задание отвечает за суммирование оценок риска для каждой
транзакции в сети кредитных
карт и должно выдавать результат в течение 20 миллисекунд.

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Плюсы
С гарантией «не более одного»
Агрегатор
система упрощается, а транзакоценок
ции обрабатываются с меньшей
задержкой. Когда в системе чтото идет не так (например, происходит ошибка при обработке или передаче
транзакции либо какой-то экземпляр временно становится недоступным), соответствующие события просто пропускаются, а агрегатор оценок обрабатывает
доступные данные, так что критичное требование задержки не нарушается.
Низкое потребление ресурсов и затраты на сопровождение — другая причина для выбора семантики «не более одного». Например, если вам приходится
обрабатывать огромный объем данных в реальном времени с ограниченными
ресурсами, стоит присмотреться к семантике «не более одного».

Минусы
А теперь обсудим оборотную сторону: неточность. Она определенно является
важным фактором при выборе семантики «не более одного». Эта семантика
подходит для случаев, в которых временная неточность приемлема. При рассмотрении этого варианта важно задать себе вопрос: к каким последствиям
приведет временная неточность результатов?

Надежды
Если вам нужны преимущества семантики «не более одного» в сочетании
с точными результатами, не отчаивайтесь. Хотя ожидать всего и сразу слишком
оптимистично, это ограничение все же можно преодолеть (в некоторой степени).
Полезные приемы для этого мы рассмотрим в конце главы, а пока перейдем
к двум другим семантикам доставки.

138  Часть I. Знакомство

«Не менее одного»
Какой бы удобной ни была семантика «не более одного», у нее есть очевидный
недостаток: отсутствие гарантий того, что каждое событие будет надежно обработано. Во многих ситуациях это попросту неприемлемо. Другой недостаток
заключается в том, что события теряются и для повышения точности почти
ничего нельзя сделать.
Следующая семантика доставки — «не менее одного» — может пригодиться
для преодоления недостатков, упомянутых выше. С семантикой «не менее одного» стриминговые ядра гарантируют, что событие будет обработано хотя бы
один раз. Побочный эффект семантики «не менее одного» заключается в том,
что события могут обрабатываться более одного раза. На следующей диаграмме
показано, как происходит обработка событий ядром Streamwork для заданий
«не менее одного».
Обратите внимание: может показаться, что отслеживать события и обеспечивать успешную обработку каждого из них несложно, но в распределенных
системах эта задача далеко не тривиальна. Ее мы рассмотрим далее.
Исполнители и диспетчеры событий передают события последующим процессам, а события отслеживаются. Если событие
будет потеряно, то оно отправляется заново.

Исполнитель экземпляра

Экземпляр

Диспетчер
событий

Исполнитель экземпляра

Экземпляр

Если какие-либо события не будут переданы или обработаны, ядро
заново отправляет их из источника. В результате появляется вероятность
того, что эти события будут обработаны более одного раза.

о
ы ничег
Вроде б го.
сложно

приятель…
Не торопись,
событий
Отслеживание
тающем на
в задании, рабо
ютерах, —
пь
нескольких ком
.
сложная задача

Глава 5. Семантика доставки  139

«Не менее одного» с подтверждением
Типичный подход к поддержке семантики доставки «не менее одного» заключается в том, что каждый компонент стримингового задания подтверждает, что он
успешно обработал событие или потерпел неудачу. Стриминговые фреймворки
обычно предоставляют механизм контроля в виде нового обработчика подтверждений (acknowledger). Этот обработчик подтверждений отвечает за отслеживание текущих и завершенных обработок для каждого события. Когда все
обработки будут завершены и для события не останется ни одной незавершенной
обработки, обработчик выдает сообщение об успехе или неудаче источнику данных. Рассмотрим задание контроля за использованием системы, выполняемое
с семантикой «не менее одного».

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

Некоторые читатели могут спросить: почему
бы не отправлять подтверждающее сообщение
источнику напрямую? Главная причина связана
с принципом единой ответственности. Источник отвечает за связь стримингового задания
с внешним миром, и его не стоит усложнять.

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

Анализатор
использования
системы

Блок записи
данных об
использовании

Обработчик
подтверждений

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

После того как компонент-источник сгенерирует событие, он сохраняет его
в буфере. При получении сообщения об успехе от обработчика подтверждений
событие удаляется из буфера, так как оно было успешно обработано. Если компонент-источник получит сообщение об ошибке для события, он воспроизводит
это событие, повторно отправляя его в задание.

140  Часть I. Знакомство

Отслеживание событий
Рассмотрим отслеживание событий на примере. В тот момент, когда базовое
событие покидает источник данных, ядро добавляет к нему метаданные, в том
числе идентификатор события, по которому оно отслеживается в задании.
Компоненты сообщают обработчику подтверждений о завершении обработки
события.
Обратите внимание: последующие компоненты включаются в данные подтверждения, поэтому обработчик подтверждений знает, что ему нужно ожидать
данных отслеживания от всех последующих компонентов, прежде чем пометить
событие как полностью обработанное.
1. Источник транзакций получает транзакцию и отправляет ее для обработки, присвоив ей идентификатор 101. Транзакция остается готовой к повторной отправке, пока все компоненты не подтвердят, что событие обработано
успешно. Подтверждение может выглядеть примерно так:
{

}
}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: источник транзакций,
Последующие компоненты: [
Анализатор использования системы
]

Источник
транзакций

2. Анализатор отправляет подтверждение для идентификатора после
завершения обработки полученного
события.
{

}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: анализатор
использования системы,
Последующие компоненты: [
блок записи данных
]

Анализатор
использования
системы

Блок записи
данных об
использовании

3. Блок записи данных отправляет другое подтверждение для идентификатора после завершения
обработки.
{

}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: блок записи данных,

Обработчик
подтверждений

4. Обработчик подтверждений получает
все необходимые
подтверждения (от
последующих компонентов) и оповещает
источник транзакций
о том, что транзакция
с идентификатором 101
полностью обработана.

Глава 5. Семантика доставки  141

Управление сбоями при обработке событий
В противном случае, если событие не будет обработано в каком-либо компоненте, обработчик подтверждений приказывает компоненту-источнику отправить
его заново.
Источник транзакций получает транзакцию и отправляет ее для обработки,
присвоив ей идентификатор 101. Транзакция остается готовой к повторной
отправке, пока все компоненты не подтвердят, что событие обработано успешно.
При получении оповещения о сбое событие отправляется заново с новым присвоенным идентификатором. Подтверждение может выглядеть примерно так:
{

}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: источник транзакций,
Последующие компоненты: [
Анализатор использования системы
]

2. Анализатор отправляет подтверждение для идентификатора после
завершения обработки полученного
события.
{

}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: анализатор
использования системы,
Последующие компоненты: [
блок записи данных
]

Источник
транзакций

Обработчик
подтверждений

Анализатор
использования
системы

4. Обработчик подтверждений получает
все необходимые
подтверждения (от
последующих компонентов) и оповещает
источник транзакций,
что событие с идентификатором 101 не
может быть полностью
обработано. Необходимо повторить попытку.

Блок записи
данных об
использовании

3. У блока записи данных возникают проблемы с обработкой события,
и после обнаружения сбоя он отправляет другое подтверждение для
идентификатора. Подтверждение может выглядеть примерно так:
{

}

Идентификатор события: 101,
Результат: ошибка при обработке,
Компонент: блок записи данных,

142  Часть I. Знакомство

Раннее обнаружение потерянных событий
Последний случай, который необходимо рассмотреть, — случай, когда не все
события проходят через все компоненты. Некоторые события могут завершить
свое перемещение преждевременно. Вот почему так важна информация от
последующих компонентов в подтверждающем сообщении. Например, если
транзакция недействительна и не может быть записана в хранилище, анализатор
использования системы будет последней остановкой на пути события и обработка на этом завершится.
1. Источник транзакций получает транзакцию и отправляет ее для обработки,
присвоив ей идентификатор 101. Транзакция остается готовой к повторной
отправке, пока все компоненты не подтвердят, что событие обработано успешно. Подтверждение может выглядеть примерно так:
{

}

Идентификатор события: 101,
Результат: успешно обработано,
Компонент: источник транзакций,
Последующие компоненты: [
Анализатор использования системы
]

Источник
транзакций

Обработчик
подтверждений

Анализатор
использования
системы

4. Обработчик подтверждений
получает все необходимые
подтверждения (от последующих компонентов) и оповещает
источник транзакций, что
событие с идентификатором
101 полностью обработано.

Блок записи
данных об
использовании

2. Анализатор отправляет подтверждение для идентификатора после завершения обработки полученного события.
ка
нент бло
Если это последний компонент для события, в данных
3. Компо
входит
нных не
а
д
и
с
и
п
подтверждения последующий компонент отсутствует.
за
ющих
последу
Подтверждение может выглядеть примерно так:
в список в, поэтому
нто
компоне
вержде{
чик подт
т
о
б
а
р
б
о
дать
и
Идентификатор события: 101,
дет ож
ния не бу
т него.
Результат: успешно обработано,
о
я
и
жден
подтвер
Компонент: анализатор использования системы
}

]

Глава 5. Семантика доставки  143

Код подтверждения в компонентах
У вас появился вопрос, как ядро узнает, успешно ли была выполнена обработка
компонентом? Хорошо! Ниже приводятся фрагменты кода, которые будут реализованы в компонентах SystemUsageAnalyzer и UsageWriter.
class SystemUsageAnalyzer extends Operator {
public void apply(Event event, EventCollector collector) {
if (isValidEvent(event.data)) {
Подтверждение будет отправif (analyze(event.data) == SUCCESSFUL) {
ляться при отправке события для
collector.emit(event);

подтверждения того, что событие

}

}

обработано успешно.
collector.ack(event.id);
} else {
// Сигнал о сбое при обработке события
Анализ завершился неудачей. Событие
collector.fail(event.id);
подтверждается как неуспешное.
}
} else {
// Сигнал об успешной обработке события
collector.ack(event.id);
Событие должно быть пропущено.
}
Событие подтверждается как успешное,
чтобы компонент-источник не отправлял
его заново.

class UsageWriter extends Operator {
public void apply(Event event, EventCollector collector) {
if (database.write(event) == SUCCESSFUL) {
это событие не нужно.
// Сигнал об успешной обработке события Отправлять
ся успешная
подтверждает
Вручную
collector.ack(event.id);
обработка события.
} else {
// Сигнал о сбое при обработке события
collector.fail(event.id);
У базы данных возникли проблемы
}
с записью. Событие подтверждает}
ся как неуспешное.
}

Новая концепция: контрольные точки
Подтверждение хорошо работает для семантики «не более одного», но у него
есть свои недостатки.
zzДля него нужна логика подтверждения (то есть изменения в коде).
zzПорядок

обработки событий может отличаться от порядка ввода, что
создает проблемы. Например, если имеются три события [A, B, C] для
обработки и в задании обработки произошел сбой при обработке события A, то позднее источник воспроизведет другую копию события A;
таким образом, заданию будут переданы четыре события [A (сбой), B,
C, A] и событие A успешно обрабатывается после B и C.

144  Часть I. Знакомство

К счастью, существует другой вариант поддержки семантики «не менее
одного» (со своими достоинствами и недостатками, как у всего остального
в распределенных системах): контрольные точки (checkpointing). Это важный
метод обеспечения ошибкоустойчивости (fault tolerance) в стриминговых
системах, то есть продолжения нормального функционирования системы
после сбоев. Так как в схеме задействовано много составляющих, подробно
рассматривать контрольные точки в стриминговых системах будет непросто.
Попробуем подойти к этому немного иначе. Хотя концепция контрольных
точек может показаться довольно сложной, на самом деле вы наверняка
сталкивались с ней в жизни, если играли в видеоигры. Впрочем, даже если не
играли, ничего страшного — вспомните любой текстовый редактор (или попробуйте сыграть в видеоигру).
Допустим, в видеоигре вы сражаетесь с ордами зомби и спасаете мир. Вряд
ли вам удастся завершить игру от начала до конца без перерыва; с этим справится только супергерой, никогда не совершающий ошибок. Большинство
из нас время от времени ошибается (а некоторые даже чаще). К счастью, вы
сохраняете свое игровое состояние, чтобы его можно было загрузить заново
и продолжить, а не начинать все с самого начала. В некоторых играх прогресс
автоматически сохраняется в критических точках. Теперь представьте, что
вы живете в игровой вселенной. Время должно течь непрерывно, хотя в реальной жизни вы несколько (или много) раз возвращались к более ранним
состояниям. Операция сохранения игры очень похожа на создание контрольной точки.
Время
в реальной
жизни

Игра
1. Сох

ранен

Сохра
не
поздн нные данны
ее мо
е
жно
зовать
для во испольния пр
с
огрес становлеса игр
ы.

Время в игровой
вселенной

ие.

2. Есл
и
пошло что-то
н
загру е так,
жаетс
я
сохра
ненна
я игра
.

Игра

Глава 5. Семантика доставки  145

Новая концепция: состояние
Каждый, кто играет в видеоигры, знает, насколько важны сохраненные данные.
Я не представляю, как бы я прошел любую игру (или завершил любую работу)
без этой возможности. Более формально контрольную точку можно определить
как блок данных, обычно находящийся в долгосрочном хранилище, который может использоваться экземпляром для восстановления предыдущего состояния.
Итак, разберем концепцию состояние.
Вернемся во вселенную с зомби и посмотрим, какие данные могут понадобиться, чтобы восстановить и продолжить приключение. Данные сильно зависят
от конкретной игры, но можно предположить, что в любом случае необходимо
сохранить:
zzтекущий счет и уровни навыков героя;
zzимеющееся снаряжение;
zzвыполненные задачи.

У этих данных есть одно важнейшее свойство: они изменяются в ходе игры.
Данные, которые остаются неизменными, пока вы усердно трудитесь над спасением мира (например, карта и внешний вид зомби), включать в сохраненные
не нужно.
А теперь вернемся к определению состояния в стриминговых системах: это
внутренние данные каждого экземпляра, изменяющиеся при обработке событий.
Например, в задании контроля за использованием системы каждый экземпляр
анализатора использования отслеживает количество обработанных транзакций.
Этот показатель изменяется при обработке новой транзакции и становится
одной из составляющих состояния. При перезапуске экземпляра счетчик необходимо восстановить.
Хотя концепции контрольных точек и состояния не особенно сложны, следует понимать, что в распределенных системах управление контрольными точками — непростая задача. В системе над обработкой событий могут одновременно
работать сотни и тысячи экземпляров. Ядро должно управлять контрольными
точками всех экземпляров и следить за тем, чтобы все они были правильно
синхронизированы. Сейчас мы временно оставим эту тему и вернемся к ней
позже, в главе 10.

Контрольные точки в задании контроля
за использованием системы для семантики
«не менее одного»
Прежде чем вводить контрольные точки для семантики «не менее одного», необходимо добавить полезный компонент между шлюзом API и заданием контроля за использованием системы — журнал событий. На практике этот термин
применяется не так часто; здесь он нужен нам для целей книги, но ­понять его

146  Часть I. Знакомство

суть несложно. Журнал событий представляет собой очередь событий, в которой
каждое событие отслеживается со смещением (или временной меткой). Потребитель (или читатель) может перейти к конкретному смещению и начать
загрузку данных с этого момента. В реальной жизни события могут быть разбиты на несколько разделов с независимым управлением смещениями в каждом
разделе, но для простоты будем считать, что в системе только одно смещение
и один источник транзакций.
Если перед компонентом — источником транзакций находится журнал событий, каждую минуту (или с другим интервалом) экземпляр-источник создает
контрольную точку с текущим состоянием — текущим смещением, с которым
он работает. При перезапуске задания ядро определяет правильное смещение
для экземпляра, к которому необходимо вернуться (откат) и начать обработку
событий с этой точки. Следует заметить, что события, обработанные экземпляром от времени контрольной точки до момента перезапуска, будут обработаны
снова, но в семантике «не менее одного» это нормально.
Шлюз
API

Журнал
со
заданию бытий по­зволя
ет
воспрои
з
транзак
ции, нач вести
иная
с некот
оро
в прошл го момента
ом.

Хранилище
контрольных
точек
Источник
транзакций

1. Каждую мину
ту (или с другим
интервалом) каж
дый экземпляр
компонента — ис
точника транзакц
ии
создает новую ко
нтрольную точку
со смещением, с
которым он сейч
ас
работает, и сохр
аняет ее в хранил
ище. Контрольная
точка может
выглядеть пример
но так:

{
}

Event offset:
100

Анализатор
использования
системы

Блок записи
данных об
использовании

2. При перезапуске задания
инициируется откат. Ядро
определяет правильные
смещения для экземпляров
источников транзакции.
Экземпляры начинают
загружать события от точек
смещений и продолжают
обработку.

Глава 5. Семантика доставки  147

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

точку с их текущими состояниями.
zzКонтрольные точки сохраняются в хранилище (желательно отказоустой-

чивом).
zzСтриминговое задание должноавтоматически перезапуститься при обнаружении сбоя.
zz Задание должно идентифицировать последние контрольные точки,
и каждый перезапущенный экземпляр источника должен загрузить свой
файл контрольной точки и восстановить свое предыдущее состояние.
zzОбъем хранилища ограничен, поэтому старые контрольные точки необходимо удалять для экономии ресурсов.

Кажется, контрольные
точки — это что-то
сложное, а для их реализации
придется основательно
поработать.

Если этот список вас пугает, без паники! Действительно, механизм контрольных
точек в целом не так прост, а для его запуска необходимо потрудиться. К счастью, основную работу берут на себя стриминговые фреймворки, а владельцу
стримингового задания придется побеспокоиться только об одном: о состоянии.
А конкретнее — о двух функциях управления состоянием.
zzПолучение

текущего состояния экземпляра. Эта функция будет вызываться периодически.
zzИнициализация экземпляра объектом состояния, загруженным по контрольной точке. Функция будет вызвана в процессе запуска стримингового задания.
Если вы предоставите две функции, описанные выше, стриминговый фреймворк
незаметно выполнит всю черную работу — например, упаковку состояний в контрольной точке, сохранение их на диске и использование контрольной точки
для инициализации экземпляров.

148  Часть I. Знакомство

Код управления состоянием в компоненте
источника транзакций
Ниже приведен пример кода компонента TransactionSource в фреймворке
Streamwork:
zzБазовый класс Source заменяется на StatefulSource.
zzС новым базовым классом появляется новая функция getState(), кото-

рая получает состояние экземпляра и возвращает его ядру.
zzЕще одно изменение — функция setupInstance() получает дополнительный объект State для настройки экземпляра после его конструирования
(у операторов без состояния его не было).
Классы Source и

public abstract class Source extends Component {
StatefulSource.
public abstract void setupInstance(int instance);
public abstract void getEvents(EventCollector eventCollector);
}
public abstract class StatefulSource extends Component {
public abstract void setupInstance(int instance, State state);
public abstract void getEvents(EventCollector eventCollector);
public abstract State getState();
Новый объект
}

Эта новая функция используется
для получения состояния экземпляра.

состояния используется для настройки
экземпляра.

class TransactionSource extends StatefulSource {
MessageQueue queue;
int offset = 0;
......
public void setupInstance(int instance, State state) {
SourceState mstate = (SourceState)state;
Данные из объекта состояния
if (mstate != null) {
используются для настройки
offset = mstate.offset;
экземпляра.
log.seek(offset);
}
}
public void getEvents(Event event, EventCollector eventCollector) {
Transaction transaction = log.pull();
eventCollector.add(new TransactionEvent(transaction));
offset++;
Смещение (offset) изменяется при
}

извлечении нового события из журнала

событий и его отправке последующим
public State getState() {
SourceState state = new SourceState(); компонентам.
State.offset = offset;
return new state;
Объект состояния экземпляра содержит
}
текущее значение смещения (offset)
}
из журнала событий.

Глава 5. Семантика доставки  149

Ровно один или фактически один?
Для задания контроля за использованием системы не идеальна ни семантика «не
более одного», ни семантика «не менее одного», потому что точные результаты
не гарантированы, однако они нужны нам для принятия правильных решений.
Для достижения этой цели можно выбрать последнюю семантику: «ровно один».
Эта семантика гарантирует, что каждое событие будет успешно обработано
один и только один раз. Следовательно, результаты с такой семантикой будут
точными.
Сначала обсудим, что же имеется в виду под семантикой «ровно один».
Чрезвычайно важно понимать, что каждое событие на самом деле не обрабатывается или успешно обрабатывается ровно один раз, как предполагает название.
Настоящий смысл в том, что если рассматривать задание как «черный ящик»
(иначе говоря, если рассматривать только ввод и вывод, не касаясь внутреннего
механизма работы задания), все выглядит так, словно каждое событие успешно
обрабатывается один и только один раз. Тем не менее если заглянуть внутрь
системы, выясняется, что каждое событие может обрабатываться более одного
раза. Взгляните на тему этой главы: в ней не случайно речь идет о семантике
доставки, а не о семантике обработки.
Когда ранее в этой главе мы кратко поясняли, что такое семантика, мы упоминали о том, что в некоторых фреймворках используется термин «фактически
один». Формально термин «фактически один» может быть более точным, но
термин «ровно один» получил широкое распространение; мы решили выбрать
его в качестве основного для книги, чтобы вы не путались в будущем.
«Точно» и «эффективно» очень похожи по смыслу.
В чем разница?

Если все эти оговорки «выглядит, словно» (или «фактически») все еще вас
смущают, это вполне нормально. Чтобы вы лучше поняли, о чем идет речь, сделаем шаг в сторону и немного поговорим о следующей интересной концепции:
идемпотентности. Хочется надеяться, что она поможет вам лучше понять, что
имеется в виду под «фактически один».
Реальную семантику «ровно один» в высшей степени трудно
реализовать в распределенных системах — и это реальность.

150  Часть I. Знакомство

Вспомогательная концепция:
идемпотентные операции
Термин идемпотентная операция (idempotent operation) звучит сложно, верно?
Этот термин из области математики и вычислительной теории означает, что
сколько бы раз функция ни получала входные данные, ее результат всегда будет
неизменным. Иначе: несколько идентичных вызовов операции приводят к тому
же результату, что и один вызов. Непонятно? Не огорчайтесь. Рассмотрим
пример в контексте класса, представляющего кредитную карту.
Этот класс содержит два метода: setCardBalance() и charge().
zzФункция setCardBalance() присваивает балансу карты новое значение,

заданное параметром.

zzФункция charge() увеличивает баланс на заданную величину.

сколько бы раз
т одинаковыми,
Результаты буду
функция
о) ни вызывалась
(больше 0, конечн
ием параметра.
ен
ач
зн
() с одним
setCardBalance

lass CreditCard {
double balance;
public void setCardBalance(double balance) {
this.balance = balance;
}

}

public void charge(float amount) {
balance += amount;
}

яется
e()
измен
)
е
и
charg
н
я
я
и
о
т
ц
с
к
о
н
фу
с (с
Балан раз, когда начением
з
й
ы
м
д
и
н
каж
с од
ается
в
ы
з
ы
в
етра.
парам

Одна интересная особенность функции setCardBalance() заключается в том,
что после ее однократного вызова состоянию объекта кредитной карты (балансу) присваивается новое значение. Если функция затем вызывается во второй
раз, то балансу снова будет присвоено новое значение — но состояние (баланс)
при этом не изменится. Если рассматривать баланс карты, все выглядит так,
словно функция вызывается только один раз, потому что вы не можете определить, вызывается она однократно или многократно. Иначе говоря, функция
может быть вызвана один или несколько раз, но фактически она вызывается однократно, потому что эффект будет одинаковым. Из-за этого функция
setCardBalance() является идемпотентной операцией.
С другой стороны, функция charge() идемпотентной операцией не является. Когда она вызывается только один раз, баланс увеличивается на заданную
величину. Если случайно повторить вызов, баланс увеличится снова и объект
карты перейдет в состояние ошибки.
Семантика «ровно один» в стриминговых системах работает так же, как приведенная выше функция setCardBalance(). По состояниям всех экземпляров
задания все выглядит так, словно каждое событие обрабатывается ровно один
раз, но во внутренней реализации событие может обрабатываться каждым компонентом более одного раза.

Глава 5. Семантика доставки  151

Наконец, «ровно один»
Теперь вы знаете реальный смысл семантики и концепцию идемпотентной
операции, а также понимаете важность возвращения точных результатов. Вас
больше интересует, как работает семантика «ровно один»? Название может
показаться фантастическим, но на самом деле все не так сложно. Как правило,
семантика «ровно один» поддерживается посредством контрольных точек,
которые очень похожи на поддержку «не менее одного». Отличие в том, что
контрольные точки создаются как для источников, так и для операторов, чтобы
они могли вместе «вернуться во времени» при откате. Обратите внимание: контрольные точки необходимы только для операторов с внутренним состоянием.
м
с други
уту (или мпляр
н
и
м
ю
у
е
1. Кажд
дый экз
лом) каж ии создает
ц
интерва
к
а
ка транз
у, сохра
источни
ую точк
н
ь
ет
л
у
о
р
р
и
т
р
н
о
гене
новую к
илище и трольной
н
а
р
х
в
н
няет ее
ытие ко
ьное соб
специал
точки.

Хранилище
контрольных
точек

2. Когда экзе
мпляр анализ
атора
получает собы
тие контрольно
й
точки, он созд
ает новую контрольную точк
у, сохраняет
ее
в хранилище
и передает со
бытие
контрольной то
чки последую
щим
компонентам
.

Шлюз
API

Источник
транзакций

Анализатор
использования
системы
Блок записи
данных об
использовании

4. Когда задание перезапускается, ядро определяет
правильную контрольную точку
для загрузки. Все экземпляры
(имеющие данные контрольных
точек) в задании восстанавливают свои состояния из
соответствующих контрольных
точек из хранилища и снова
начинают обрабатывать
события.

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

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

152  Часть I. Знакомство

Код управления состоянием в компоненте
анализатора использования системы
Чтобы со фреймворком Streamwork компонент SystemUsageAnalyzer создавал
и использовал состояние экземпляра, необходимо внести изменения, похожие
на изменения в TransactionSource, представленные выше.
zzБазовый класс Operator заменяется на StatefulOperator.
zzФункция setupInstance()

получает один дополнительный параметр
с состоянием.
zzДобавляется новая функция getState().
public abstract class Operator extends Component {
public abstract void setupInstance(int instance);
public abstract void getEvents(EventCollector eventCollector);
public abstract GroupingStrategy getGroupingStrategy();
}

Новый объект
состояния
используется
для настройки
экземпляра.

public abstract class StatefulOperator extends Component {
public abstract void setupInstance(int instance, State state);
public abstract void apply(Event event, EventCollector eventCollector);
public abstract GroupingStrategy getGroupingStrategy();
public abstract State getState();
Эта новая функция используется
}

для получения состояния экземпляра.

class SystemUsageAnalyzer extends StatefulOperator {
int transactionCount;

public void setupInstance(int instance, State state) {
AnalyzerState mstate = (AnalyzerState)state;
transactionCount = state.count;
При конструировании экземпляра
……
объект состояния используется
}
для инициализации экземпляра.
public void apply(Event event, EventCollector eventCollector) {
transactionCount++;
Переменная count изменяется при обработке событий.
}

}

eventCollector.add(transactionCount);

public State getState() {
AnalyzerState state = new AnalyzerState();
State.count = transactionCount;
Новый объект состояния периодиreturn state;
чески создается для хранения
}
данных экземпляров.

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

Глава 5. Семантика доставки  153

Повторное сравнение семантик доставки
У всех семантик доставки существуют свои сценарии использования. Теперь,
когда мы рассмотрели все семантики доставки, снова сравним их (в несколько
упрощенном виде). Из следующей таблицы видно, что у каждой семантики доставки есть свои плюсы и минусы. Иногда ни один вариант не идеален. В таких
случаях необходимо понимать достоинства и недостатки и принимать соответствующие решения. Возможно, вам также придется менять семантику при
изменении требований.
Семантика
доставки

Не более одного

Не менее одного

Ровно один

Точность

Точность не гарантирована из-за пропуска событий

Точность не гарантирована из-за дублирования событий

(На первый взгляд)
гарантирован точный результат

Задержка (при
возникновении ошибки)

Устойчивость
к сбоям; отсутствие
задержки при возникновении ошибок

Чувствительность
к сбоям; возможны
задержки при возникновении ошибок

Чувствительность
к сбоям; возможны
задержки при возникновении ошибок

Сложность/
использование
ресурсов

Очень простая, минимальные затраты

Средняя (в зависимости от реализации)

Высокая, значительные затраты ресурсов

Сложность сопровождения

Низкая

Средняя

Высокая

Пропускная
способность

Высокая

Средняя

Низкая

Код

Изменения в коде не
требуются

Некоторые изменения в коде

Значительные изменения в коде

Зависимости

Без внешних зависимостей

Без внешних зависимостей (с подтверждениями)

Требуется внешнее
хранилище для сохранения контрольных точек

Резонное беспокойство тех, кто размышляет о выборе семантики «не более
одного» или «не менее одного» с учетом задержки и эффективности, состоит
в том, что точность не гарантирована. Существует популярный подход, чтобы
избежать этой проблемы: лямбда-архитектура. В лямбда-архитектуре сопутствующий процесс пакетной обработки выполняется на тех же данных, чтобы
получить точные результаты, но с более высокой задержкой. Поскольку в этой
главе у нас и так много материала для усвоения, мы поговорим об этом подробнее в главе 10.

154  Часть I. Знакомство

Итоги
В этой главе мы рассмотрели новую важную концепцию стриминговых систем:
семантику доставки (или гарантию доставки). Для стриминговых заданий вы
можете выбирать три типа семантик:
zz«Не

более одного» — каждое событие гарантированно будет обработано
не более одного раза. Это означает, что события могут быть пропущены
при сбоях в стриминговых заданиях.
zz«Не менее одного» — событие гарантированно будет обработано стриминговыми заданиями, но может оказаться, что при сбоях некоторые события
будут обработаны несколько раз.
zz«Ровно один» — при такой семантике результат выглядит, как будто каждое событие обрабатывается только один раз. Также используется термин
«фактически один».
В этой главе мы обсудили достоинства и недостатки каждой из этих семантик,
кратко обсудили важный метод поддержки семантик «не менее одного» и «ровно один» — контрольные точки. Выбирайте наиболее подходящую семантику
доставки для своих кейсов.

Упражнения
1. Какую семантику доставки вы выберете при построении следующих заданий? Почему?
yy Поиск наиболее популярных хештегов в Твиттере.
yy Импортирование записей из потока данных в базу
данных.
2. В этой главе мы рассмотрели компонент анализатора
использования в задании контроля за использованием
системы и преобразовали его в идемпотентную операцию. А что вы скажете о компоненте блока записи
данных об использовании? Является ли он идемпотентной операцией?

Источник
транзакций

Анализатор
использования
системы

Блок записи
данных об
использовании

Что дальше?
В главах со 2-й по 5-ю были представлены наиболее распространенные и базовые концепции, которые понадобятся вам, когда вы начнете строить стриминговые системы. В следующей главе мы прервемся и обобщим то, что вы узнали
к настоящему моменту. После этого перейдем к более сложным темам, таким
как оконные вычисления и операции соединения.

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

6

В этой главе
99Обзор изученных концепций.
99Знакомство с более сложными концепциями,

которые будут рассматриваться в части 2.

«Технология позволяет людям взять под контроль
все что угодно, кроме самой технологии».
Джон Тьюдор

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

156  Часть I. Знакомство

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

ейе-

нв
ое ко
ваем авляет
ы
з
а
ен
дст
такж ией, пре говой
ние,
г
а
ин
о
д
л
м
а
о
и
мпоЗ
топ
стр
и
ю
л
из ко
и
и
ц
ром реализа е состоит торов)
.
а
и
й
собо ы. Задан ов и опер мпоненты
м
о
к
е
к
и
т
н
х
с
ч
и
и
о
с
т
щ
в (ис
иняю
ненто ков, соед
о
и пот

Источ
ник —
ко
внешн
ие дан мпонент, пе
редаю
ные в
систем
стр
щий
у. Дру
гими с иминговую
ки явл
ловам
яются
и,
то
в стри
минго чками входа источнивую си
стему. данных

Источник

Оператор 1

Оператор 2
Пото
к
собы ом назыв
мый
ае
тий и
ывае мпоз
а
н
комп
связь тся исхо
о
е
онен
д
—к
акж
тами между д ящая по
ор, т анием», брабаты
т
а
.
став
р
вумя
Собы
е
в
о
п
о
и
з
к
О
а
т
но
обра
щий
испо ие (такж
«пре получаю я. Имен ся вся
льзуе
евр
и
т
,
т
а
тся т
или «
зных
нент ий собы ыполняе т
ер
с
с
в
щ
г
фраг ообщени мин «ко ценария
х
о
м у
ваю атора
них
рт
х
е»
мент
торы из внеш
ер
а
п
неде ) предст еж», «эл
р
о
е
п
в
я
О
лимы
емен
и
авля
.
ыт
ка
ет
т»
х дан
логи мать соб других торов.
ных в собой о
и
а
и
л
н
д
и
и
пото
ин
опер
пр
ов
ке.
чник вующих
о
т
с
и
т
шес
пред

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее  157

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

Исполнитель-источник
Исполнитель-источник,
экземпляр 0

И у блока чтения данных
с датчика (предшест­
вующий компонент),
и у счетчика автомобилей (последующий
компонент) степень
параллелизма равна 2.

Исполнитель-оператор
Исполнитель-оператор,
экземпляр 0
Счетчик
автомобилей

Блок чтения
данных

Диспетчер
событий

Исполнитель-оператор,
экземпляр 2

Исполнитель-источник,
экземпляр 1

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

Экземпляр
счетчика
автомобилей 0

Экземпляр
счетчика
автомобилей 0

Van

Van

Car

Truck

Truck

Truck

Van

Car

Truck

Экземпляр
счетчика
автомобилей 0

Экземпляр
счетчика
автомобилей 0

158  Часть I. Знакомство

DAG и стриминговые задания
DAG (направленный ациклический граф) используется для представления
логической структуры стримингового задания и направлений передачи данных
в нем. В более сложных стриминговых заданиях, таких как система обнаружения мошеннических действий, один компонент может иметь несколько предшествующих компонентов (объединение) и/или последующих компонентов
(разветвление).
В главе 2 мы построили свою первую реализацию
стримингового задания для подсчета автомобилей. Логическим представлением задания
является простой направленный ациклический
граф.

DAG хорошо подходят для
представления стриминговых заданий.

Блок чтения данных
с датчика

Счетчик автомобилей
В главе 3 стриминговое зад
ание было
параллелизировано для тог
о, чтобы
справиться с повышением
трафика
на мосту. И хотя каждый ком
понент
теперь существует в нескол
ьких
экземплярах, DAG все рав
но представляется в том же виде, что
и прежде.

Действует то же
правило: количество
экземпляров каждого
компонента не влияет
на DAG задания.

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

Источник
транзакций

Анализатор
среднего чека

Презентатор
транзакций

Шлюз
API

Анализатор
оконного
расстояния

Агрегатор
оценок

Разве

твлен

ие

Анализатор
оконного числа
транзакций

нение

Объеди

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее  159

Семантика (гарантия) доставки
Разобравшись с основными компонентами стриминговых заданий, мы вернулись к практической задаче. Какие требования действуют? Что важно для
решения задачи? Пропускная способность, задержка и/или точность?
После выяснения требований необходимо соответствующим образом настроить семантику доставки.
Существуют три варианта семантики доставки:
zz«Не

более одного» — стриминговые задания обрабатывают события без
гарантий успешной обработки.
zz«Не менее одного» — стриминговые задания гарантируют, что каждое событие будет успешно обработано по крайней мере один раз, без гарантии
количества его обработок.
zz«Ровно один» — стриминговые задания гарантируют, что каждое событие
будет успешно обработано один и только один раз (по крайней мере внешне). В некоторых фреймворках используется термин «фактически один».
Семантика «ровно один» гарантирует точные результаты, но существуют определенные затраты, которые нельзя игнорировать, такие как задержка и сложность. Важно понимать, какие требования критичны в каждом случае, чтобы
выбрать правильный вариант.

Семантика доставки в системе
обнаружения мошеннических действий
с кредитными картами
В главе 5 в систему обнаружения мошеннических действий с кредитными
картами было добавлено новое задание для контроля за использованием системы. Оно предоставляет данные об использовании всей системы в реальном
времени. Задание обнаружения мошенничества и новое задание имеют разные
требования:
zzДля

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

160  Часть I. Знакомство

В результате для них были выбраны разные семантики доставки.

жения
я обнару вий
и
н
а
д
а
з
Для
дейст
ических нтика «не
мошенн
а
м
е
с
уется
нения
использ
я выпол
л
д
»
о
г
о
н
д
о
.
более
держке
ний к за
требова

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

Шлюз
API

Презентатор
транзакций

Источник
транзакций

Источник
транзакций

Анализатор
среднего чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

Анализатор
использования
системы

Агрегатор
оценок

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее  161

Что дальше?
До сих пор мы рассматривали базовые концепции стриминговых систем. Эти
концепции должны помочь вам начать строить стриминговые задания для различных целей в выбранном фреймворке.
Но вы узнали далеко не все, что нужно знать о стриминговых системах!
Когда вы начнете решать более масштабные и сложные задачи, то с большой
вероятностью столкнетесь со сценариями, требующими более глубокого знания
стриминговых систем. В главах части II будут рассматриваться более сложные
темы:
zzОконные вычисления.
zzСоединение данных в реальном времени.
zzОбратное давление.
zzВычисления с состоянием и без состояния.

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

162  Часть I. Знакомство

Оконные вычисления
До сих пор события в примерах обрабатывались последовательно. Однако в задании обнаружения мошеннических действий анализаторы зависят не только
от текущего события, но и от информации о том, когда, где и как недавно использовалась карта, — эта информация помогает выявить несанкционированное
использование карт. Например, анализатор оконного расстояния выявляет
попытки мошенничества, обнаруживая операции с кредитной картой, удаленные на большие расстояния, но совершенные за короткий период времени. Как
строить стриминговые системы для решения подобных задач?
Сан-Рамон (штат
Калифорния)
Сент-Луис (штат
Миссури)
Нью-Йорк (штат
Нью-Йорк)

Время

Портленд
(штат Орегон)

Овалы со сплошной
заливкой представляют карту 1.

Многократное использование
кредитной карты в разных
местах за короткий период
времени может быть
признаком мошенничества.

Ромбы со сплошной
заливкой представляют карту 2.

Чтобы сегментировать события на отдельные наборы для обработки, в стриминговых системах могут потреКак определяется
боваться оконные вычисления.
сегмент?
В главе 7 будут рассмотрены различные оконные стратегии с анализатором оконного расстояния
в заданиях обнаружения мошеннических действий.
Кроме того, у оконных вычислений часто существуют ограничения, которые играют важную роль для этого анализатора
и многих других прикладных проблем. В главе 7 также рассматривается популярный
прием — использование храВ стриминговых системах оконные операнилищ «ключ — значение»
торы обрабатывают наборы событий (вме(систем баз данных, похожих
сто отдельных событий).
на словари) для реализации
оконных операторов.

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее  163

Соединение данных в реальном времени
В главе 8 мы построим новую систему для отслеживания в реальном времени
выбросов CO2 всех автомобилей Кремниевой долины. Автомобили ежеминутно
передают информацию о своей модели и местонахождении. События соединяются с другими данными для генерирования карты выбросов CO2 в реальном
времени.

Мы буде
м отслеж
прохож
ив
д
е
н
ие автом ать
в каждо
обилей
й зоне и
собират
ьд
о выбро
сах CO . анные
2

А в чем проблема?
Просто соединяем данные
в реальном времени.

Соединение данных
в реальном времени сложнее,
чем можно ожидать. Эта тема
рассматривается в главе 8.

Для людей, уже работавших с базами данных, концепция соединения (join)
должна быть знакома. Она используется при обращении к данным из нескольких таблиц. В стриминговых системах существует похожий оператор соединения со своими особенностями, он также рассматривается в главе 8. Обратите
внимание: соединение — тип объединения потоков, который упоминался (но
подробно не рассматривался) в главе 4.

164  Часть I. Знакомство

Обратное давление
После запуска стримингового задания для обработки
данных вы столкнетесь (хочется надеяться, что не сразу) с проблемой: комВ разработку
пьютеры ненадежны! Точнее говоря,
вложены большие
компьютеры по большей части надежденьги, и мы не можем
ны, но обычно стриминговые системы
работают годами и в них возникают са- упустить ни копейки.
Ваша система это
мые разные проблемы.
гарантирует?
Ваша команда получила запрос от
банка — необходимо проанализировать
систему обнаружения мошеннических действий и составить отчет о надежности системы. А конкретно, перестанет ли работать задание при возникновении проблем
с компьютерами или сетью и что с результатами — они будут неточными или
их вообще не будет? Это разумное требование, так как в разработку системы
вложены значительные средства. Собственно, этот вопрос важен даже без запроса от банка, не так ли?
Шлюз
API

Источник
транзакций

Анализатор
среднего чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

В этой системе
работает множество
компьютеров.
Программные или
аппаратные проблемы могут возникать
в разных местах
и в любой момент.

Агрегатор
оценок

Обратное давление (backpressure) — стандартный защитный механизм, поддерживаемый многими стриминговыми фреймворками. Процессы временно

Глава 6. Краткий обзор стриминговых систем и взгляд в будущее  165
замедляются и пытаются дать системе возможность восстановиться от возникших проблем, таких как временные сетевые проблемы или внезапные выбросы
трафика, создающие чрезмерную нагрузку на компьютеры. Иногда потеря
событий лучше, чем замедление. Обратное давление — полезный инструмент
для построения надежных систем. В главе 9 вы увидите, как стриминговые ядра
обнаруживают и решают проблемы при помощи обратного давления.

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

Но ведь это рискованно?
Как обеспечить правильность
результатов во время миграции и после нее?

ы, на которых
Старые машин
минговые
работают стри
заменены
т
задания, буду
щными
мо
е
ле
новыми, бо
и.
и эффективным

166  Часть I. Знакомство

В главе 5, посвященной семантике доставки, осталась одна нераскрытая тема:
компоненты с состоянием (stateful components). Мы кратко обсудили, что такое
компоненты с состоянием и как они используются в семантике доставки «не
менее одного» и «ровно один». Тем не менее важно понимать плюсы и минусы
разных вариантов, чтобы принимать более эффективные технические решения
при построении и обслуживании стриминговых систем.
В главе 10 внутренняя работа компонентов с состоянием рассматривается
более подробно. Также будут представлены альтернативные решения, которые
позволят избежать некоторых затрат и ограничений.

Часть II
Движемся дальше

Во второй части книги мы углубимся в теорию, рассматривая реализацию
более сложных задач в стриминговых системах без привязки к конкретному
фреймворку. Глава 7 показывает, как разбить непрерывный поток данных на
содержательные блоки, а в главе 8 описан процесс соединения данных в реальном времени. В главе 9 вы узнаете, как в стриминговых системах происходит
восстановление после сбоев, а в главе 10 погрузитесь в сложности управления
состоянием в стриминговых системах в реальном времени. Наконец, глава 11
подводит краткий итог изложенного в книге, а также дает рекомендации о том,
что делать после прочтения.

7

Оконные вычисления

В этой главе
99Стандартные оконные стратегии.
99Временные метки в событиях.
99Оконный предел и поздние события.

«Период устойчивого внимания компьютера
не длиннее его шнура питания».
Автор неизвестен

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

Глава 7. Оконные вычисления  169

Сегментация данных в реальном времени
С ростом популярности нового продукта он стал привлекать внимание
­злоумышленников. Группа хакеров запустила новую мошенническую схему на
бензоколонках. Схема работает так: хакеры захватывают данные карты жертвы
и дублируют ее на нескольких новых физических кредитных картах. Вновь
созданные поддельные карты распространяются среди других участников
группы, и одновременно совершаются покупки бензина по одной кредитной
карте в нескольких точках мира. Хакеры надеются, что при одновременном
снятии средств владелец карты этого не заметит, пока не будет слишком поздно.
Результат — бесплатный бензин. Зачем затевать мошенничество в глобальном
масштабе, чтобы бесплатно заправить машину? Не спрашивайте нас.

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

Если одна карта используется в нескольких
точках мира, весьма вероятно, что эти
транзакции являются мошенническими.

Как предотвратить мошенничество?
Мы будем использовать круглые числа для упрощения математических вычислений. Также будем считать, что максимальная скорость перемещения человека
на самолете составляет 500 миль в час. К счастью, наша команда уже продумала
возможность подобных мошеннических схем.

170  Часть II. Движемся дальше

Анализ задачи
Мы пытаемся решить две задачи. Сначала система ищет перемещения на значительные расстояния для одной кредитной карты. Затем — значительные перемещения при использовании для нескольких кредитных карт. В первом сценарии
конкретные транзакции по кредитным картам помечаются как мошеннические,
а во втором продавцы (бензоколонки) помечаются как находящиеся под атакой
со стороны опасных злоумышленников.
Из-за ограничения скорости пере
мещения (500 миль
в час) можно уверенно сказать, что
человек не сможет
физически расплатиться картой
в Сан-Рамоне, штат
Калифорния, а через два часа —
в Сент-Луисе, штат
Миссури, потому что расстояние
между этими населенными пунктами человек не может
преодолеть за 2 часа.

он
Сан-Рам
я)
лифорни
а
К
т
(шта

Около 2000 миль

Сен
т
(шт -Луис
ат М
исс
ури

Наша формула:
final double maxMilesPerHour = 500;
final double distanceInMiles = 2000;
final double hourBetweenSwipes = 2;
if (distanceInMiles > hourBetweenSwipe * maxMilesPerHour) {
// Транзакция помечается как потенциально мошенническая
}

Как анализатор связывает
текущую транзакцию с прошлыми транзакциями в реальном
времени?

)

Глава 7. Оконные вычисления  171

Анализ задачи (продолжение)
Мошенники хотят проводить масштабные атаки по всему миру, чтобы одновременно заправляться за чужой счет. А значит, важно отслеживать не только
поведение одной кредитной карты в системе, но и поведение системы в целом.
При проведении таких масштабных атак необходимо иметь возможность блокировать операции по атакованным картам, чтобы повысить безопасность
системы. На приведенной ниже диаграмме для примера используются города
США, в которых могут проводиться операции по карте.

Каждый значок
представляет карту,
с которой снимаются
деньги.

Из этого графика видно, что деньги
с карты снимаются в разных местах
по всей территории США. Теперь мы
выбираем способ разбиения этого
графика по оси x (время), который
станет определяемым окном операции.

Сан-Рамон (штат
Калифорния)
Сент-Луис (штат
Миссури)
Нью-Йорк (штат
Нью-Йорк)
Портленд (штат
Орегон)

Овалы со сплошной
заливкой представляют карту 1.

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

Ромбы со сплошной заливкой
представляют
карту 2.

Подобные мошеннические схемы можно предотвращать двумя способами:
zzБлокировать снятие средств с отдельных кредитных карт.
zzБлокировать обработку кредитных карт на бензоколонках.

Но какими инструментами в стриминговой системе можно воспользоваться для
обнаружения мошеннической активности?

172  Часть II. Движемся дальше

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

Каждый значок
представляет карту,
с которой снимаются
деньги.
го
оконно
затор
и
л
а
н
А
яния
рассто

Анал

изато
р око
пров
нно
моше еряет то го расст
ояния
лько
нниче
слу
ск
в кон
текст ого испо чаи
ль
е отд
ельно зования
й кар
ты.

Сан-Рамон (штат
Калифорния)
Сент-Луис
(штат Миссури)

Нью-Йорк
(штат Нью-Йорк)

Время

Портленд
(штат Орегон)

Анализа

з

Анали

онок

нзокол

ля бе
атор д

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

Сан-Рамон (штат
Калифорния)
Сент-Луис (штат
Миссури)
Нью-Йорк (штат
Нью-Йорк)
Портленд (штат
Орегон)

Время

Глава 7. Оконные вычисления  173

Окна в задании обнаружения
мошеннических действий
Многие компоненты-анализаторы в задании обнаружения мошеннических
действий используют ту или иную разновидность окна (об этом подробнее
чуть позже) для сравнения текущей транзакции с предыдущими. В этой главе
мы остановимся на анализаторе оконного расстояния (windowed proximity
analyzer), который обнаруживает быстрые перемещения кредитных карт в пространстве. Задачу для бензоколонок оставим нашим читателям для самостоятельной работы.
Анализ
ато
переме р оконного ра
щения
сстояни
на
я обнар
сте одн
уживае
ой карт большие расс
т
тояния
ы. В это
провер
в конте
м конте
яет дан
кксте ан
ные тол
в задан
ал
ько для
ном окн
одной к изатор
е.
арты

Презентатор
транзакций

Шлюз
API

Источник
транзакций

Анализатор
среднего чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

174  Часть II. Движемся дальше

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

Источник
транзакций

Др

уги

е ок

Окна позволяют разбить
непрерывный поток событий
на блоки меньшего размера.

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

на

Анализатор
оконного числа
транзакций
Непрерывный поток
событий между
компонентами.

Глава 7. Оконные вычисления  175

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

Время

В этой главе события будут обрабатываться по группам,
которые формируются окнами.
window 1

window 2

window 3

Время

Размер окна мо
жет определять
ся
периодом врем
ени или количе
ством
элементов — по
выбору разраб
отчика.

176  Часть II. Движемся дальше

Новая концепция: оконная стратегия
Разобравшись с тем, как используются окна в стриминговых системах, рассмотрим способы группировки событий с применением оконной стратегии. Мы
разберем три разных типа оконных стратегий и обсудим различия между ними
в анализаторе оконного расстояния. Существуют три типа оконных стратегий:
zzФиксированное окно.
zzСкользящее окно.
zzСеансовое окно.

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

Как разделить бесконечный поток событий
для обнаружения больших смещений по
расстоянию? Повлияет ли выбор оконной
стратегии наточность?

Зависит ли что-то от
способа разбиения событий?

Глава 7. Оконные вычисления  177

Фиксированные окна
Первая и самая простая разновидность окон — фиксированное окно. Иногда
их еще называют переворачивающимися окнами (tumbling windows). События,
получаемые от начала до конца каждого окна, группируются в пакет, который
обрабатывается как единое целое. Например, если настроить фиксированное
временное окно с продолжительностью в 1 минуту (минутное окно), все события этого окна будут группироваться для обработки. Фиксированные окна
просты и прямолинейны, они очень полезны во многих ситуациях. Вопрос в том,
подойдут ли они для анализатора оконного расстояния?

Источник
транзакций

Каждый прямоугольник
представляет 1-минутное
временное окно.

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

178  Часть II. Движемся дальше

Фиксированные окна в анализаторе
оконного расстояния
В следующем примере рассматривается применение фиксированного окна для
поиска повторяющихся списаний с одной карты. Для простоты мы используем
минутные окна, чтобы понять, как будет выглядеть каждая группа событий.
Цель — выявление повторяющихся транзакций по каждой кредитной карте
внутри минутного окна. Другие аспекты (например, логику максимального
перемещения в 500 миль в час) рассмотрим позже.
Важно заметить, что использование фиксированного временного окна означает лишь то, что временной интервал фиксирован. В каждом окне может быть
больше или меньше событий в зависимости от того, сколько событий проходит
через задание.
Метка
в
транза ремени каж
кции.
дой
С
минут
ы и се одержит то
кунды
лько
.

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

Первое минутное окно:
00:00-00:59

Второе минутное окно:
01:00-01:59

Третье минутное окно:
02:00-02:59

00:12

card no:

00:55

card no:

00:49

card no:

01:10

card no:

01:26

card no:

01:42

card no:

01:37
02:22

02:38

card no:
card no:

card no:

...1234
...6789

...1212

...6789

...2345

...1212

...1212

...7865

...4433

Временные интервалы всех окон одинаковы,
но количество событий на окно различное.

Глава 7. Оконные вычисления  179

Обнаружение попыток мошенничества
в фиксированном временном окне
Посмотрим, как будет вести себя анализатор расстояния при использовании
фиксированных временных окон. Мы ограничили количество транзакций на
окно, чтобы не усложнять описание основ работы с окнами.
Если внимательно присмотреться к диаграмме, становится ясно, как фиксированные временные окна влияют на потенциальные оценки рисков. С фиксированным временным окном вы просто отсекаете другие транзакции, проходящие
через систему, даже если они находятся всего за секунду от границы окна. Как
вы думаете, позволяет ли такой тип окон выявлять попытки мошенничества
с максимальной точностью?
Нет, фиксированное временное окно в этом случае не идеально. Если две
транзакции по одной карте происходят с интервалом всего в несколько секунд,
но попадают в два разных временных окна (как две транзакции по карте ...6789),
к ним нельзя будет применять функцию проверки расстояния.

В этом окне транзакции
по карте не повторяются,
поэтому мошеннические действия не обнаруживаются.
В одном окне встречаются
две повторяющиеся
транзакции по карте …1212.
В этом случае применится
функция проверки расстояния для оценки риска
мошенничества.
В этом окне повторяющихся
транзакций по карте нет,
поэтому и оценка риска
не выполняется.

00:12

card no:

00:55

card no:

00:49

card no:

01:10

card no:

01:26

card no:

01:42

card no:

01:37
02:22

02:38

card no:
card no:

card no:

...1234
...6789

...1212

...6789

...2345

...1212

...1212

...7865

...4433

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

Две транзакции
по карте ...6789
происходят
с интервалом
в 21 секунду, но
принадлежат
разным фиксированным окнам.

У этих двух
транзакций по
карте …1212 та
же проблема.

180  Часть II. Движемся дальше

Фиксированные окна: время и количество
Прежде чем переходить к следующей оконной стратегии, выделим две разновидности фиксированных окон:
zzВременные окна определяются неизменяемым интервалом времени.

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

zzКоличественные

Временные окна. В данном случае интервал составляет 3 минуты. Количество событий
в каждом окне может различаться.
Первое 3-минутное окно

e2

e1

e3

Второе 3-минутное окно

e4

e5

e6

e7
6 минут

3 минуты

0 минунт

Помните! Количество событий во временных окнах может быть разным.
Вполне может оказаться, что в одном окне 3 события, а в другом 4.

С количественными окнами количество событий в каждом окне будет одинаковым.
При этом временные интервалы окон могут различаться.
Первое окно на 3 события

e2

e1
0 элементо

в

e3

Второе окно на 3 события

e4

e5

3 элемента

e6

e7
6 элементо

в

Глава 7. Оконные вычисления  181

Скользящие окна
Другая популярная оконная стратегия — скользящее окно (sliding window).
Скользящие окна похожи на фиксированные временные окна, но отличаются от них наличием определенного интервала смещения (slide interval).
Новое окно создается не при завершении предыдущего окна, а через каждый
интервал смещения. Интервал окна и интервал смещения позволяют окнам
перекрываться, из-за этого каждое событие может быть включено в несколько
окон. Формально можно сказать, что фиксированное окно является особой
разновидностью скользящего окна, в которой оконный интервал равен интервалу смещения.

Источник
транзакций
Каждая дуга представляет
2-минутное окно с минутным
интервалом смещения. Окна
перекрываются, масштаб
в изображении не соблюдается.

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

182  Часть II. Движемся дальше

Скользящие окна: анализатор
оконного расстояния
Скользящее окно может использоваться для поиска повторяющихся списаний
с одной карты в перекрывающихся временных окнах. На следующей диаграмме
изображены минутные скользящие окна с 30-секундными интервалами смещения. При использовании скользящих окон важно понимать, что одно событие
может войти в несколько окон.

00:12

card no:

00:55

card no:

Окно 1:
00:00 - 00:59

00:49

card no:

Окно 2:
00:30-01:29

01:10

card no:

Окно 3:
01:00-01:59
Окно 4:
01:30-02:29
Окно 5:
02:00-02:59

01:26

card no:

01:42

card no:

01:37
02:22

02:38

card no:
card no:

card no:

С прим
енение
м сколь
щих ок
зя­
он две
т
ра
ции по
карте .. нзак.6
обраба
тывают 789
ся вмес
в окне
те
2.

...1234
...6789

...1212

...6789

...2345

...1212

...1212

...7865

...4433

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

Глава 7. Оконные вычисления  183

Обнаружение мошеннических действий
благодаря использованию скользящих окон
Скользящие окна отличаются от фиксированных тем, что они перекрываются
с заданным интервалом. Смещение обеспечивает удобный механизм более
равномерно распределенного объединения событий, который позволяет определить, должна ли транзакция быть помечена как мошенническая. Скользящие
окна помогают решить проблему отсечения событий, которую мы описывали
для фиксированных окон.
00:12

card no:

00:55

card no:

00:49

card no:

01:10

card no:

01:26

card no:

01:42

card no:

01:37
02:22

02:38

...1234

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

...6789

...1212

...6789

...2345

card no:

...1212

...1212

card no:

...7865

card no:

...4433

Со смещением окна набор элементов данных, с которыми оно может выполнять
операции, изменяется. Постепенное смещение данных, к которым может обращаться система, обеспечивает более последовательное и планомерное представление данных.
1234

6789

1212

3476

1234

1212

1212

3476

Как вы думаете, к чему приведет перекрытие скользя­
щих окон при вычислении
1234
среднего значения — результат улучшится или ухудшится? Почему?

6789

1212

3476

1234

1212

1212

3476

1234

6789

1212

3476

1234

1212

1212

3476

Вопрос на засыпку!

184  Часть II. Движемся дальше

Сеансовые окна
Последняя оконная стратегия, которую нам хотелось бы рассмотреть, прежде
чем переходить к реализации, — сеансовые окна. Сеансом (session) называется
период активности, ограниченный определенными промежутками отсутствия
активности; сеанс может использоваться для группировки событий. Как правило, сеансовые окна связываются с конкретным ключом и не являются глобальными для всех событий (в отличие от фиксированных и скользящих окон).

Источник
транзакций
Поток относится к конкретной
карте. Каждая дуга представляет сеансовое окно. Обратите
внимание на промежуток
между ними.

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

Глава 7. Оконные вычисления  185

Сеансовые окна (продолжение)
Сеансовые окна обычно определяются тайм-аутом — максимальным промежутком времени, в течение которого сеанс может оставаться открытым. Можно представить, что для каждого ключа устанавливается таймер. Если после получения
ключа до истечения срока таймера не происходит ни одного события, сеансовое
окно закрывается. В следующий раз при получении события с ключом будет запущен новый сеанс. На следующей диаграмме рассмотрим транзакции по двум
картам (сеансовые окна обычно связаны с ключом, которым в нашем случае является номер карты). Порог отсутствия активности составляет 10 минут.
ом.
ым ключ
онкретн
к
с
м
я
а
т
с
т
р
а
ю
связыва по кредитным к
я
ые окна
тикальна
ми
р
я
е
и
в
ц
я
к
а
Сеансов
а
з
д
н
е.
ж
а
т
а
р
р
т
К
а
.
с
к
ы
ре
тной
р карт
В приме
о конкре
ся номе
п
т
е
ю
я
и
л
ц
в
к
я
за
ключом
яет тран
едставл
линия пр

У каждой карты
существует
собственное
сеансовое окно.

card no: ....1212

card no: ....6789
Окно 2

Окно 1

00:49

00:55

Интервал между
двумя транзакциями меньше
10-минутного
порога, по этой
причине они
группируются
в одно сеансовое
окно.

Время

01:10

01:37

01:42

Окно 3

15:21

15:33

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

186  Часть II. Движемся дальше

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

01:40
01:42

18:11

01:40
01:41

01:43

01:48

01:52

02:05
42:24

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

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

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

Глава 7. Оконные вычисления  187

Обзор оконных стратегий
Мы рассмотрели принципы трех разных оконных стратегий. Давайте сравним
их. Обратите внимание: при сравнении используются временные окна, но с таким же успехом можно использовать фиксированные и скользящие окна.
zzФиксированные окна имеют фиксированный размер, и новое окно откры-

вается при закрытии предыдущего. Окна не пересекаются.
окна имеют одинаковый фиксированный размер, но новое
окно открывается до закрытия предыдущего. Таким образом, окна пересекаются.
zzСеансовые окна обычно отслеживаются для каждого ключа по отдельности. Каждое окно открывается активностью и закрывается по промежутку
отсутствия активности.
zzСкользящие

Фикс
и
фикси рованные о
кна им
рован
ны
ею
не пер й размер, и т
есека
ются. окна

е имеют
Скользящие окна тож
но окна
р,
ме
фиксированный раз
.
тся
каю
пересе

Сеансовые окна определяются
активностью и отсутствием
активности по каждому ключу.

188  Часть II. Движемся дальше

Разбиение потока событий на наборы данных
После рассмотрения всех концепций перейдем к конкретной реализации.
С оконными стратегиями события обрабатываются небольшими группами
(вместо изолированных событий). Из-за этого различия интерфейс Win­
d o­
wedOperator слегка отличается от обычного интерфейса Operator.

Исполнитель экземпляра
оператора
Экземпляр оператора
apply(Event)

тия
рах собы
х операто
.
и
д
е
р
е
В обычны
ч
тся по о
ю
а
в
ты
а
б
обра

public interface Operator {
public void apply(Event event, EventCollector eventCollector);
}
Исполнитель экземпляра оконного оператора
Блок формирования окон

Экземпляр оконного
оператора
apply(EventWindow)

ры,
я на набо
я для
азбиваютс
р
равляютс
я
тп
ти
о
ы
и
б
о
м
с
о
х
р
а
д
р
я
то
д
w
. Каж ый
tWindo
х опера
ователем
В оконны
ьз
екты Even
л
ъ
о
б
п
о
и—
в
м
ы
я
аютс
еляем
о времен
упаковыв
ам, опред чает информацию
р
то
а
р
е
п
ио
лю
обработк
также вк
ntWindow
e
.
v
а
E
н
к
т
о
к
е
ц
ъ
е
об
кон
, начало и
например

public interface WindowedOperator {
public void apply(EventWindow window, EventCollector eventCollector);
}

Глава 7. Оконные вычисления  189

Оконные системы: концепция или реализация
По сути, оконный оператор представляет собой механизм, преобразующий события в наборы событий, а стриминговые ядра обычно отвечают за управление
наборами событий. По сравнению с заданиями, которые рассматривались до
этой главы, стриминговым ядрам нужно больше ресурсов для оконных операторов. Чем больше событий в каждом окне, тем больше ресурсов понадобится
стриминговому ядру. Иначе говоря, стриминговые задания работают более
эффективно при небольшом размере окна. Тем не менее практические условия
часто не идеальны. C’est la vie.
Возможно, некоторые читатели уже сталкивались с проблемами при использовании оконных операторов для реализации анализатора оконного расстояния
в задании обнаружения мошеннических действий:
zz В

этом анализаторе мы хотели отслеживать точки, находящиеся на удалении друг от друга, и сравнивать расстояние и время транзакций в этих
точках. А конкретно, если расстояние превышало 500 миль в час, умноженные на количество часов, прошедших между двумя транзакциями, оператор
пометит транзакцию как потенциально мошенническую. Насколько эффективно в такой ситуации многочасовое скользящее окно? В этом окне могут
содержаться сотни миллиардов транзакций, отслеживание и обработка
которых потребуют значительных затрат.
zzСитуация только усложняется, если принять во внимание требование
20-миллисекундной задержки. Со скользящим окном необходимо определить интервал смещения, который не должен быть слишком большим.
Если этот интервал слишком велик (например, одна секунда), то большинство транзакций (происходящих за первые 980 миллисекунд) нарушит 20-миллисекундное ограничение.
Похоже, для этой
задачи оконная реализация не подходит.
Верно. Базовые оконные
операторы работают в некоторых простых случаях, но в этом
задании придется действовать
более изобретательно.

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

190  Часть II. Движемся дальше

Другой взгляд
Теперь посмотрим, как наша команда решает проблему и предотвращает хищение бензина. Прежде всего необходимо понять, как именно обрабатываются
транзакции в анализаторе оконного расстояния.
Этот оператор должен отслеживать время и место каждой транзакции по
карте и проверять, что время и расстояние между двумя транзакциями не нарушают правило. Тем не менее формулировка «любые две транзакции в окне»
не является обязательной. Задачу можно упростить, если взглянуть на нее под
слегка другим углом: в любой момент при поступлении новой транзакции можно сравнить ее время и место с параметрами предыдущей транзакции по той же
карте и применить нашу формулу. Все предшествующие транзакции по карте,
а также все транзакции по другим картам не влияют на результат, и их можно
исключить из обработки.

В таком виде задача
заметно упрощается.

00:12

card no:

00:55

card no:

00:49

card no:

01:10

card no:

01:26

card no:

01:42

card no:

01:37
02:22

02:38

card no:
card no:

card no:

...1234
...6789

...1212

...6789

...2345

...1212

...1212

...7865

транзакКаждую
ходимо
цию необ
с предысравнить
анзакцидущей тр
.
же карте
ей по той

...4433

Так как формула уже готова, задача становится довольно простой; но как найти
предыдущую транзакцию по той же карте?
Как насчет использования скользящего окна? Хороший вопрос; давайте рассмотрим и этот вариант. Длина окружности Земли составляет около
25 000 миль, так что расстояние между любыми двумя точками не превышает
12 500 миль. Согласно нашему правилу максимальной скорости 500 миль в час,
на перемещение в любую другую точку Земли должно потребоваться не более
25 часов. Следовательно, транзакции со сроком более 25 часов обрабатывать
не нужно. Обновленная формулировка задачи выглядит так: как определить
предыдущую транзакцию по той же карте за последние 25 часов?

Глава 7. Оконные вычисления  191

Хранилища пар «ключ — значение»
Немного поразмыслив над вычислениями для анализатора оператора оконного
расстояния, наша команда решила реализовать их на базе системы хранилищ
«ключ — значение». Этот метод построения оконных операторов без применения стандартной поддержки оконных операторов в стриминговых фреймворках
чрезвычайно полезен, поэтому о нем стоит поговорить.
Хранилище пар «ключ — значение» (называемое также хранилищем K-V, от
key-value) представляет собой систему хранения данных, спроектированную
для сохранения и чтения объектов данных с ключами. За последнее десятилетие
эта парадигма стала чрезвычайно популярной. Если термин вам еще не знаком,
объясним: система работает как словарь, в котором каждая запись однозначно
идентифицируется конкретным ключом. В отличие от более традиционных
(и более известных) реляционных баз данных, записи в хранилищах «ключ —
значение» полностью независимы друг от друга.
Хранилище пар «ключ — значение»
Ключ 1

Значение 1

Ключ 2

Значение 2

Ключ 3

Значение 3

В хранилищах пар «ключ — значение»
каждая запись ассоциируется с уникальным ключом. Обычно для работы с хранилищем достаточно двух базовых функций:
get (ключ) и put (ключ, значение).

Зачем нужны системы хранения данных с ограниченной функциональностью?
Их главные преимущества — производительность и масштабируемость. Так как
хранилищам пар «ключ — значение» не нужно отслеживать отношения между
разными записями, строками и столбцами, внутренние вычисления значительно
упрощаются по сравнению с традиционными базами данных. В результате такие
операции, как чтение и запись, выполняются намного быстрее. А поскольку
­записи не зависят друг от друга, также становится намного проще распределять
данные по нескольким серверам и организовывать их совместную работу для
предоставления сервиса хранения пар «ключ — значение», способного обрабатывать огромные объемы данных. Эти два преимущества важны для системы
обнаружения мошеннических действий, а также многих других систем обработки данных.
Другая интересная возможность, поддерживаемая хранилищами пар
«ключ — значение», — ограничение срока действия. Для пары «ключ — значение», добавляемой в хранилище, можно указать срок действия. По истечении
этого срока пара будет автоматически удалена из системы и занимаемые ею
ресурсы будут освобождены. Эта возможность чрезвычайно удобна для оконных операторов в стриминговых системах (а конкретно в части «за последние
25 часов» нашей задачи).

192  Часть II. Движемся дальше

Реализация анализатора оконного расстояния
Благодаря паре «ключ — значение» стриминговому ядру не придется отслеживать и хранить в памяти все события окна. Это обязанность разработчиков
системы. Но есть и плохая новость: хранилище пар «ключ — значение» может
использоваться по-разному в зависимости от ситуации. Не существует простой и универсальной схемы реализации оконных стратегий с хранилищами
пар «ключ — значение». В качестве примера возьмем анализатор оконного
расстояния.
В анализаторе необходимо сравнить время и место каждой транзакции с временем и местом предыдущей транзакции по той же карте. Текущая транзакция
хранится во входном событии, а предыдущая транзакция для каждой карты
должна находиться в хранилище пар «ключ — значение». Ключом является
идентификатор карты, а значением — время и место (для простоты в приведенном ниже исходном коде событие целиком сохраняется как значение).
public class WindowedProximityAnalyzer implements Operator {
final
final
final
final

static double maxMilesPerHour = 500;
static double distanceInMiles = 2000;
static double hourBetweenSwipes = 2;
KVStore store;

public setupInstance(int instance) {
store = setupKVStore();
}

Вместо Operator
используется
WindowedOperator.

Настройка хранилища
пар «ключ — значение».

public void apply(Event event, EventCollector eventCollector) {
TransactionEvent transaction = (TransactionEvent) event;
TransactionEvent prevTransaction = kvStore.get(transaction.getCardId());

Предыдущая транзакция
загружается из
хранилища пар «ключ —
значение».

boolean result = false;
if (prevTransaction != null) {
double hourBetweenSwipe =
transaction.getEventTime() - prevTransaction.getEventTime();
double distanceInMiles = calculateDistance(transaction.getLocation(),
prevTransaction.getLocation());
if(distanceInMiles > hourBetweenSwipe * maxMilesPerHour) {
// Транзакция помечается как потенциально мошенническая.
result = true;
Обнаруживается мошенническая
}
транзакция.
}
eventCollector.emit(new AnazlyResult(event.getTransactionId(), result));
kvStore.put(transaction.getCardId(), transaction);

Текущая транзакция помещается в хранилище пар
«ключ — значение», при этом идентификатор
карты используется в качестве ключа.
Предыдущее значение при этом заменяется.

Глава 7. Оконные вычисления  193

Время события и другие вехи
Прежде чем завершить эту главу, осталось рассмотреть еще одну концепцию.
В коде анализатора оконного расстояния присутствует одна важная строка, на
которую стоит обратить особое внимание:
transaction.getEventTime();

Что же считать временем события (event time)? А есть ли другие ключевые
моменты времени? Речь идет о времени фактического возникновения события.
Многие операции обработки событий не совершаются немедленно. Вместо этого
возникшее событие обычно сохраняется в некоторой служебной подсистеме,
а реальная обработка происходит позднее. Все эти операции выполняются
в разное время, так что да, есть и другие вехи времени. Используем в качестве
примера нашу простую систему контроля дорожного движения и рассмотрим
важные вехи времени, относящиеся к событию.
чик
Время 1: дат обиль,
м
то
ав
ет
обнаружива
твующее
тс
ве
от
со
я
создаетс
тия.
время собы
событие. Это

Время 3: событие
принимается блоком чтения
данных с датчика. Это
время получения события
сервером.
Счетчик
автомобилей

Блок чтения данных
Счетчик
автомобилей

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

земпляром
е принимается эк
Время 4: событи
вается.
ты
ба
ра
билей и об
счетчика автомо
и.
тк
бо
Это время обра

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

194  Часть II. Движемся дальше

Водяной знак окна
Время события используется во многих оконных вычислениях, поэтому очень
важно понимать промежуток между временем события и временем обработки.
Из-за этого промежутка оконные стратегии, описанные в этой главе, не настолько просты, как может показаться.
Если взять в качестве примера систему контроля движения и настроить
оператор счетчика автомобилей с простыми фиксированными окнами для
подсчета автомобилей, обнаруженных за каждую минуту, каким должно быть
время открытия и закрытия для каждого окна? Обратите внимание на то, что
время появления каждого события в экземплярах операторов счетчиков автомобилей (время обработки) немного отстает от времени его создания в датчике
IoT (время события). Если окно закрывается точно в конечное время, события,
происходящие ближе к концу окна на датчиках IoT, будут отсутствовать, потому
что они еще не были получены экземплярами счетчиков. Заметим, что их нельзя
поместить в следующее окно, потому что на основании времени события они
принадлежат уже закрытому окну.
Автомобиль может быть
обнаружен здесь, в конце
минутного окна.

Если окно закрывается точно в конце временного
окна, некоторые автомобили, обнаруженные
датчиками IoT, могут отсутствовать, потому что они
еще не прибыли в эту точку.
Счетчик
автомобилей
Блок чтения данных
Счетчик
автомобилей

Чтобы решить проблему с пропуском событий, можно держать окно открытым
чуть дольше и ожидать получения событий. Дополнительное время ожидания
для окна обычно называется водяным знаком (windowing watermark).
е время
Дополнительно
вается
зы
на
ожидания
окна.
ом
ак
зн
м
водяны
Предыдущее
окно события
Новое окно события

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

Глава 7. Оконные вычисления  195
Если вернуться к реализации анализатора оконного расстояния, водяной знак
становится еще одной причиной, по которой стандартный оконный оператор
не идеален в рассматриваемой ситуации. Резервирование дополнительного
времени перед обработкой наборов событий создает дополнительную задержку и только усложняет соблюдение требования о 20-миллисекундной
задержке.

Поздние события
Водяной знак очень важен для предотвращения пропуска событий и генерирования полных наборов событий для обработки. Понять концепцию несложно,
но выбрать время ожидания несколько сложнее.
Например, в системе контроля дорожного движения датчики IoT работают очень хорошо. В результате обычно все события автомобилей собираются
успешно в пределах одной секунды. В этом случае может подойти 1-секундный
водяной знак (то есть задержка на 1 секунду).
Обычно все события должны прибывать в пределах 1 секунды
после их создания. 1-секундный водяной знак выглядит разумно.
Счетчик
автомобилей
Блок чтения данных
Счетчик
автомобилей

Тем не менее слово «обычно» звучит как тревожный сигнал. Ранее мы не раз
упоминали, что одной из серьезных проблем при построении любой распределенной системы становится преодоление сбоев. Выработайте в себе
полезную привычку спрашивать: что, если система не будет работать так, как
ожидалось? Даже в такой простой системе события могут задержаться более
чем на секунду, если что-то пошло не так — например, датчик или блок чтения данных могут временно замедлиться либо пропускная способность сети
может измениться из-за нестабильности соединения. События, полученные
после закрытия соответствующего окна, называются поздними событиями.
Что с ними делать?
Иногда потеря поздних событий допустима, но в других случаях важно обеспечить правильную обработку таких событий. Многие реальные стриминговые
фреймворки предоставляют механизмы обработки поздних событий, но мы не
будем описывать подробности, так как обработка привязана к конкретному

196  Часть II. Движемся дальше

фреймворку. А пока ключевой вывод: учитывайте возможность появления
поздних событий и не забывайте о них.
Здесь могут возникнуть проблемы,
поскольку события поступят после
предполагаемого времени и станут
поздними.

Счетчик
автомобилей

Блок чтения данных
Счетчик
автомобилей

Итоги
Оконные вычисления становятся важнейшим аспектом стриминговых систем,
потому что они предоставляют возможность объединения изолированных событий в наборы событий для обработки. В этой главе были рассмотрены три
стандартные оконные стратегии, поддерживаемые в большинстве стриминговых
фреймворков:
zzФиксированные окна.
zzСкользящие окна.
zzСеансовые окна.

Базовая поддержка окон в стриминговых фреймворках имеет свои ограничения
и может плохо работать во многих случаях. Поэтому кроме описания концепций
и подхода к обработке оконных операторов в стриминговых фреймворках вы
научились использовать хранилища пар «ключ — значение» для моделирования
оконных операторов и преодоления ограничений.
В конце главы также были рассмотрены три взаимосвязанные концепции,
которые играют важную роль при решении прикладных задач:
zzРазные

вехи времени, относящиеся к каждому событию, включая различия между временем события и временем обработки.
zzВодяные знаки окна.
zzПоздние события.

Глава 7. Оконные вычисления  197

Упражнение
В начале главы мы сказали, что существуют два способа предотвращения
мошеннических транзакций по кредитным картам:
yy Блокировать снятие средств с отдельных кредитных карт.
yy Блокировать обработку всех кредитных карт на бензоколонках.
Впоследствии мы сосредоточились на выявлении проблем с отдельными
кредитными картами, но не рассматривали второй вариант. Как бы вы стали
выявлять подозрительные бензоколонки, чтобы заблокировать обработку
кредитных карт на них?

8

Операции соединения

В этой главе
99Сопоставление различных типов событий в ре-

альном времени.

99Внутренние и внешние соединения.
99Оконные соединения.

«Запрос SQL заходит в бар, подходит к двум столикам
и спрашивает: “Можно к вам присоединиться?”»1
Автор неизвестен
Если вы работали с любой базой данных SQL, то, скорее всего, вы использовали
оператор join — или по крайней мере читали о нем. В стриминговых системах
операция соединения (join) не настолько важна, как в базах данных, но все
равно иногда очень полезна. В этой главе вы узнаете, как соединения работают
в контексте потоковой обработки. Сначала мы рассмотрим пример использования соединения в базах данных, чтобы вы поняли его суть, а потом перейдем
к соединениям в стриминговых системах. Если операция соединения вам уже
знакома, можете пропустить вводную часть.
1

An SQL query goes into a bar, walks up to two tables, and asks, can I join you? Шутка
основана на многозначности слов table — «столик» и «таблица» и join you — «присоединиться к вам» и «соединить вас». Можно перевести как «запрос подходит к двум
таблицам и спрашивает: можно вас соединить?». — Примеч. пер.

Глава 8. Операции соединения  199

Соединение данных выбросов
в реальном времени
Нашей команде повезло: ее привлекли к работе над отслеживанием выбросов
автомобилей в Кремниевой долине, штат Калифорния. Круто же?
Но каждая большая возможность несет в себе и сложности. Команде придется оперативно соединять события, генерируемые автомобилями в определенных
районах города, с расчетными показателями выбросов. Как это сделать? Давайте
разберемся.
ь в городе
втомобил
который
Каждый а
атчиком,
д
я
тс
ацию
е
а
щ
осна
т информ
о передае
тн
ждении.
у
о
н
х
и
а
н
м
е
то
еж
его мес
и
е
л
и
б
о
об автом

Интересно,
какие проблемы могут
возникнуть при соединении
данных в реальном
времени?

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

200  Часть II. Движемся дальше

Задание контроля выбросов, версия 1
Первая версия задания выбросов уже готова. Интересная часть задания — хранилище данных справа от преобразователя данных выбросов. Это статическая
таблица, используемая преобразователем для поиска данных о выбросах, производимых каждым автомобилем. Предполагается, что в этой системе выбросы
от автомобилей одного производителя, модели и года выпуска одинаковы.
ей
тий автомобил
Источник собы
илей.
об
м
тия от авто
получает собы
{

}

time: ......
make: ....,
model: ....,
year: ....,
location: .....

Источник данных пред
ставляет
собой статическую ссы
лку на
данные о выбросах от
разных
автомобилей.

Источник
событий
автомобилей
{

}

time: ....
make: ....,
model: ....,
year: ....,
zone: .....

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

{

}

Преобразователь
данных о выбросах

time: ....
zone: .....,
co2_emission: ....

Оконный
агрегатор

Следующий агрегатор
группирует все
события по зоне, после
чего объединяет общие
данные выбросов по
зоне.

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

Глава 8. Операции соединения  201

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

Источник
событий
автомобилей

Преобразователь
данных
о выбросах

Оконный
агрегатор

{

}

make: ....,
model: ....,
year: ....,
zone: .....

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

Модель

XXX

AA

Год
2020

3.3

YYY

CC

2021

4.2

...

...

...

...

class EmissionResolver extends Operator {
private final Table emissionTable = ......;
public void apply(Event event, EventCollector eventCollector) {
VehicleEvent vehicleEvent = (VehicleEvent) event.getData();
double emission = emissionTable.getEmission(
vehicleEvent.make, vehicleEvent.model, vehicleEvent.year
);
eventCollector.add(
new EmissionEvent(vehicleEvent.zone, emission)
);
}
}
{
zone: .....,
co2_emission: ....
}

Выброс

тся

осах включаю
Данные о выбр
событие.
в исходящее

Этот оператор можно рассматривать как очень простой оператор соединения,
который объединяет данные из разных источников на основании связывающих
их характеристик (производитель, модель, год). Тем не менее данные о выбросах
берутся из таблицы, а не из потока. Операторы соединения в стриминговых заданиях идут дальше, предоставляя данные в реальном времени.

202  Часть II. Движемся дальше

Точность становится проблемой
Задание в целом работает нормально и успешно генерирует данные о выбросах
в реальном времени. Тем не менее в вычислениях отсутствует один важный
фактор: температура (как вы знаете, выбросы CO2 зависят от температуры,
и в Калифорнии погода тоже меняется). В результате данные о выбросах по
зонам, возвращаемые системой, не обладают достаточной точностью. Добавлять
датчик температуры в устройства, установленные на каждом автомобиле, уже
поздно, поэтому проблему придется решать иначе.

У нас возникла проблема
с точностью. И похоже, из-за того,
что мы не учитываем текущую
температуру.

Глава 8. Операции соединения  203

Обновленное задание контроля выбросов
Чтобы повысить точность результатов, наша команда добавила еще один источник данных для введения в задание события текущей температуры. События
температуры соединяются с событиями автомобилей по идентификатору зоны.
Исходящие события выбросов передаются преобразователю данных о выбросах.
В каждую зону города был добавлен собственный
датчик температуры. Каждый датчик измеряет
температуру с интервалом 10 минут, после чего
направляет событие температуры источнику данных
о температуре.

Прежняя версия
Источник
событий
автомобилей

Новая версия

Преобразование
выбросов

Источник
событий
автомобилей

Оконный
агрегатор

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

Стоп, стоп! А как
два потока соединяются
в один?

Источник событий температуры получает данные о температуре со всего города.
Источник
событий
температуры

Соединитель
событий

Преобразование
выбросов

Оконный
агрегатор

{

}

zone: .....,
temperature: ....

Соединитель событий
соединяет данные потоков
от обоих источников
данных в реальном времени
и генерирует события
машин/температуры.

204  Часть II. Движемся дальше

Все внимание на соединение
Основные изменения в новой версии:
zzДополнительный

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

Если
все происходит
в реальном времени, то
как связать события,
происходящие в разное
время?

Источник
событий
температуры

Соединитель
событий

Преобразователь данных
о выбросах
Оконный
агрегатор

Источник событий
температуры получает
события от датчиков
с 10-минутным
интервалом.

Соединитель событий
х
соединяет события из дву
н
оди
в
в
входящих потоко
исходящий поток.

Возможно, события из
разных потоков необходимо
синхронизировать в операторе
соединения?

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

Глава 8. Операции соединения  205

Еще раз: что такое соединение?
Вероятно, при любом упоминании оператора соединения join естественно представлять себе SQL. В конце концов, сам термин «соединение» пришел из мира
реляционных баз данных.
Соединение — конструкция SQL, которая позволяет взять некоторое количество полей из одной таблицы и объединить их с набором полей из другой
таблицы или таблиц для получения консолидированных данных. На следующей
диаграмме представлен оператор соединения в контексте реляционных баз
данных; стриминговое соединение рассматривается ниже.
Таблица событий автомобилей
make

model

year

Таблица температуры
zone

zone

temperature

XXX

AA

2020

3

1

95.4

YYY

CC

2013

1

2

94.3

ZZZ

DD

2017

2

3

95.1

XXX

AA

2008

1

4

95.2

XXX

BB

2014

1

5

95.3

ZZZ

EE

2021

3

ZZZ

EE

2018

5

zone,
Две таблицы содержат общее поле
ми.
ица
табл
ду
меж
ь
которое образует связ
SELECT v.time, v.make, v.model, v.year, t.zone, t.temperature
FROM vehicle_events v
INNER JOIN temperature t on v.zone = t.zone;

Результат соединения этих таблиц
может выглядеть примерно так:

Соединеннаятаблица
make

model

year

zone

temperature

XXX

AA

2020

3

95.1

YYY

CC

2013

1

95.4

ZZZ

DD

2017

2

94.3

XXX

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.1

ZZZ

EE

2018

5

95.3

206  Часть II. Движемся дальше

Как работает стриминговое соединение
Как создать соединение для данных, которые непрерывно перемещаются и обновляются? Для этого следует преобразовать события температуры в таблицу.
Источник событий
автомобилей
получает данные
Источник
от автомобилей
событий
с минутным
автомобилей
интервалом.

Источник событий
температуры получает
события от датчиков
с 10-минутным
интервалом.

Источник
событий
температуры

События температуры
используются для
обновления строк
следующей таблицы
температур.

Cоединитель
событий
Подробный вид соединителя событий

Источник
событий
температуры

Источник
событий
автомобилей

Cоединитель событий
{

}

{

}

make: XXX
model: AA
year: 2020,
zone: 3

2. Событие автомобиля соединяется
с таблицей температур по полю zone.

{

}

zone

make: XXX,
model: AA,
year: 2020,
zone: 3,
temperature: 95.2

Преобразователь данных
о выбросах

zone: 3
temperature: 95.2

temperature
1

95.4

2

94.3

3

95.2

4

95.2

5

95.2

3. Данные температур
добавляются в события
автомобилей.

1. Поступающие события
температур либо
обновляют существующие строки, либо
добавляются как новые
строки таблицы температур в реальном
времени. Иначе говоря,
таблица представляет
непрерывно изменяющийся набор данных,
находящийся в памяти.
Данные постоянно
обновляются потоком
событий температуры.

тся
являе ет
а
ц
и
л
! Таб существу .
АНИЕ
ой
ть
ВНИМ ной (то ес изменяем
н
и
е
)
м
и
вре
мят
о в па
тольк

Глава 8. Операции соединения  207

Cоединение (join) потоков — разновидность
объединения (fan-in)
В главе 4 рассматривался сценарий обнаружения мошеннических действий,
в котором оценки риска от предшествующих анализаторов использовались
для определения того, является ли транзакция мошеннической. Относится ли
агрегатор оценок к той же категории операторов?
Правильный ответ — нет, не является. В агрегаторе оценок все входящие потоки имеют один тип событий. Оператор не знает, из какого потока поступило
каждое событие, и просто применяет одну и ту же логику. В соединителе событий события двух входящих потоков отличаются друг от друга и по-разному
обрабатываются в операторе. Агрегатор оценок является оператором слияния,
а соединитель событий является оператором соединения. Оба они являются
операторами объединения (fan-in).
Анализатор
среднего
чека

Анализатор
среднего
чека

Анализатор
среднего
чека

результаАгрегатор оценок собирал
щих
вую
ест
ты трех разных предш
выдают
ры
ато
лиз
ана
но
анализаторов,
ратор
опе
ой
Так
а.
тип
ого
события одн
я.
яни
называется оператором сли

Более абстрактное представление
слияния: потоки типа данных A
сводятся воедино оператором слияния.

Данные A

Слияние

Источник
событий
температуры

Соединитель
событий

Агрегатор
оценок

Данные A

Источник
событий
автомобилей

Данные A

В соединителе со
бытий
соединяются два
разных
типа данных. Тако
й
оператор называ
ется
оператором соед
инения.

Более абстрактное представление
соединения: потоки разных типов данных
сводятся воедино оператором соединения.

Данные A

Данные B

Соединение

208  Часть II. Движемся дальше

События автомобилей и события температуры
Следует заметить, что в операторе соединения события тем- { make: XXX
zone
temperature
model: AA
пературы преобразуются во
year: 2020,
1
95.4
временную таблицу температур,
zone: 3
2
94.3
но события автомобилей обра- }
3
95.2
батываются как поток. Почему
{
преобразуются события темпе4
95.2
make: XXX,
model: AA,
ратуры, а не события автомоби5
95.3
year: 2020,
лей? Почему не преобразовать
zone: 3,
temperature: 95.2
оба потока в таблицы? Важно
}
продумать ответы на эти вопросы при построении систем.
Во-первых, предполагается, что для каждого входящего события автомобиля
будет выдаваться одно исходящее событие. Следовательно, будет логично поддерживать течение событий автомобилей через оператор как поток. Во-вторых,
управлять событиями автомобилей в форме таблицы поиска будет сложнее.
Автомобилей намного больше, чем зон в системе, поэтому хранение событий
автомобилей во временной таблице в памяти требует значительно больших
затрат ресурсов. Более того, в каждой зоне для нас важна только последняя
температура, а при управлении счетчиком автомобилей (добавлении и удалении) необходимо действовать осторожнее, потому что важно каждое событие.
Попробуем поместить события автомобилей в таблицу, а затем соединить их
с потоком событий температуры. Для каждой зоны в таблице будет храниться
несколько записей, а результатами станут пакеты событий вместо отдельных
событий.

{

}

1. Каждое новое
событие
автомобиля
добавляется
к таблице.

make: ZZZ
model: EE
year: 2021,
zone: 3

make

model

year

}

zone: 3,
temperature: 95.2

{

zone

XXX

AA

2020

3

YYY

CC

2013

1

XXX

BB

2014

1

ZZZ

EE

2021

3

3. Для зоны в таблице автомобилей теперь имеются две
записи. В дальнейшем они
должны быть удалены.

{

},
{

make: XXX,
model: AA,
year: 2020,
zone: 3,
temperature: 95.2

}

make: ZZZ,
model: EE,
year: 2021,
zone: 3,
temperature: 95.2

2. Одно событие
температуры
поступает от
каждой зоны
раз в 10 минут
и добавляется
к таблице
автомобилей.

4. Для результата
оператора соединения генерируются два события.
На практике их
может быть
намного больше.

Глава 8. Операции соединения  209

Таблица как материализованное
представление стриминга
Порассуждаем на чуть более абстрактном уровне: как связаны между собой
события температуры и таблица температур? Понимание отношений между
ними поможет понять особенности событий температуры и принимать более
эффективные решения при построении новых стриминговых систем.
Одна важная особенность данных температуры заключается в том, что в любой момент необходимо хранить только последнюю температуру для каждой
зоны. Дело в том, что нас интересует именно последняя температура, а не история ее изменений. На следующей диаграмме показаны изменения в таблице
температур до и после получения и обработки двух событий температуры.
Два события температуры принимаются и используются для обновления таблицы температур.
{

zone: 3
temperature: 95.0

}

{

}

zone

temperature

zone: 1
temperature: 95.4

zone

temperature

Две строк
и в таблиц
е
обновляю
тся после
обработки
двух собы
тий.

zone

temperature

1

95.4

1

95.4

1

95.4

2

94.3

2

94.3

2

94.3

3

95.1

3

95.0

3

95.0

4

95.2

4

95.2

4

95.2

5

95.3

5

95.3

5

95.3

Время

Каждое событие температуры используется для обновления таблицы последними данными. Таким образом, каждое событие может рассматриваться как
изменение данных в таблице, а поток событий — как журнал изменений.
С другой стороны, при выполнении соединения ведется поиск по таблице
температур. В любой момент таблица температур является результатом применения всех событий до конкретного момента времени. Таким образом, таблица
может рассматриваться как материализованное представление событий температур. Интересный эффект материализации заключается в том, что интервал
между событиями становится несущественным. В нашем примере интервал
между событиями температур для каждой зоны составляет 10 минут, но система
будет работать точно так же с интервалом в 1 секунду или 1 час.

210  Часть II. Движемся дальше

Материализация событий автомобилей
менее эффективна
С другой стороны, по сравнению с событиями температуры материализация событий автомобилей менее эффективна. Автомобили постоянно перемещаются
по городу, и в соединение должно включаться каждое отдельное событие для
одного автомобиля, а не только последнее. В результате таблица событий автомобилей по сути содержит список событий, ожидающих обработки. Кроме того,
количество автомобилей обычно больше количества зон. В итоге по сравнению
с событиями температуры события автомобилей сложнее, а их материализация
менее эффективна.
События автомобилей принимаются
и добавляются к таблице событий
автомобилей.
{

}

make

model

year

После обр
аботки со
бытия
машины в
таблице
появляетс
я новая стр
ока.

make: ZZZ
model: EE
year: 2021,
zone: 3

zone

make

model

year

zone

XXX

AA

2020

3

XXX

AA

2020

3

YYY

CC

2013

1

YYY

CC

2013

1

XXX

BB

2014

1

XXX

BB

2014

1

ZZZ

EE

2021

3

Время

На этой диаграмме показано, что события машин добавляются к таблице (вместо обновления данных в строках). Хотя существуют некоторые приемы для
повышения эффективности, например добавление столбца счетчика и объединение строк с одним производителем, моделью, годом и зоной вместо простого
добавления данных к таблице, достаточно ясно, что материализовать события
температуры намного удобнее, чем события автомобилей. В реальных задачах
это свойство может стать важным фактором при выборе способа обработки потоков, если используется оператор соединения.

Глава 8. Операции соединения  211

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

ая
н
ст рия
е
в то
з
и и
Не ерр
т

Система постоянно
сообщает об ошибках.

Автомобили выезжают
за установленные границы.
И что нам делать в такой
ситуации?

Почему возникает проблема и как ее решить? Необходимо рассмотреть различные типы операторов соединения.

212  Часть II. Движемся дальше

Проблемы с оператором соединения
Ключевая функция оператора соединения — получение температуры для заданной зоны. Рассмотрим приведенное ниже табличное представление оператора.
На диаграмме каждое событие автомобиля представляется строкой в таблице,
но следует помнить, что события автомобилей обрабатываются одно за другим,
как поток. Также следует учитывать, что таблица температур изменяется динамически, а значения температур могут изменяться при поступлении новых
событий температуры.
Поток событий автомобилей
make

model

year

Таблица температур

zone

XXX

AA

2020

3

YYY

CC

2013

1

ZZZ

DD

2017

2

XXX

AA

2008

1

XXX

BB

2014

1

ZZZ

EE

2021

3

ZZZ

EE

2018

5

YYY

CC

2015

X

...

...

...

...

И что делать,
если в полученном
событии автомобиля
указана неизвестная
зона?

zone

Зоны этого
события автомобиля не существует в таблице
температур.

temperature
1

95.4

2

94.3

3

95.0

4

95.2

5

95.3

Выходной поток
make

model

year

zone

temperature

XXX

AA

2020

3

95.0

YYY

CC

2013

1

95.4

ZZZ

DD

2017

2

94.3

XXX

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.0

ZZZ

EE

2018

5

95.3

YYY

CC

2015

X

...

...

...

???
...

Проблема целостности данных возникла из-за того, что зона 7 из последнего
события автомобиля отсутствует в таблице температур. Что делать? Чтобы ответить на этот вопрос, необходимо обсудить две новые концепции: внутреннее
соединение (inner join) и внешнее соединение (outer join).

Глава 8. Операции соединения  213

Внутреннее соединение
Внутреннее соединение обрабатывает только события автомобилей, для которых имеется соответствующая зона в таблице температур.
Поток событий автомобилей
make

model

year

zone

XXX

AA

2020

3

YYY

CC

2013

1

ZZZ

DD

2017

2

XXX

AA

2008

1

XXX

BB

2014

Таблица температур
zone

temperature

1

1

95.4
94.3

ZZZ

EE

2021

3

2

ZZZ

EE

2018

5

3

95.0
95.2
95.3

YYY

CC

2015

X

4

...

...

...

...

5

Выходной поток
make

Результат! Об
ратите
внимание: в та
блице
результата не
т строки для
последнего со
бытия
автомобиля, по
тому что
зоны X не сущ
ествует
в таблице тем
ператур.

model

year

zone

temperature

XXX

AA

2020

3

95.0

YYY

CC

2013

1

95.4

ZZZ

DD

2017

2

94.3

XXX

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.0

ZZZ

EE

2018

5

95.3

...

...

...

...

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

214  Часть II. Движемся дальше

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

model

year

zone

XXX

AA

2020

3

YYY

CC

2013

1

ZZZ

DD

2017

2

XXX

AA

2008

1

XXX

BB

2014

1

ZZZ

EE

2021

3

ZZZ

EE

2018

5

YYY

CC

2015

X

...

...

...

...

Таблица температур
zone

temperature
1

95.4

2

94.3

3

95.0

4

95.2

5

95.3

Исходящий поток
make

model

year

zone

temperature

XXX

AA

2020

3

95.0

YYY

CC

2013

1

95.4

ZZZ

DD

2017

2

94.3

XXX

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.0

ZZZ

EE

2018

5

95.3

YYY

CC

2015

X

null

...

...

...

Внешне соединение оставляет возможность обработать
особые случаи позже.

...

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

Наша команда решает применить внешнее соединение, чтобы сохранить неполные строки и обработать их позже.

Глава 8. Операции соединения  215

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

model

year

zone

Таблица температур

XXX

AA

2020

3

YYY

CC

2013

1

ZZZ

DD

2017

2

1

95.4

XXX

AA

2008

1

2

94.3

XXX

BB

2014

1

3

95.0

ZZZ

EE

2021

3

4

95.2

ZZZ

EE

2018

5

5

95.3

YYY

CC

2015

X

...

...

...

...

zone

model

year

zone

Внеш

е
не
ен ие
р
ен
ут
Вн дин
е
со

Исходящий поток
make

temperature

temperature

2020

3

95.0

2013

1

95.4
94.3

DD

2017

2

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.0

ZZZ

EE

2018

5

95.3

...

...

...

...
make

model

ение

ZZZ
XXX

оедин

AA
CC

нее с

XXX
YYY

Исходящий поток
year

zone

temperature

XXX

AA

2020

3

YYY

CC

2013

1

95.0
95.4

ZZZ

DD

2017

2

94.3

XXX

AA

2008

1

95.4

XXX

BB

2014

1

95.4

ZZZ

EE

2021

3

95.0

ZZZ

EE

2018

5

95.3

YYY

CC

2015

X

null

...

...

...

...

216  Часть II. Движемся дальше

Разные типы соединений
Если вам известна конструкция join в базах данных, то вы наверняка помните,
что существуют разные типы внешних соединений: полные внешние соединения
(или просто полные соединения), левые внешние соединения (левые соединения)
и правые внешние соединения (правые соединения). Все операторы соединения
представлены на следующих диаграммах, чтобы продемонстрировать различия
между ними в контексте баз данных SQL.
Внутренние соединения возвращают
только те результаты, для которых
существуют соответствующие
значения в обеих таблицах.

События
автомобилей

Температура

Левые внешние соединения
возвращают все результаты из
таблицы событий автомобилей
и только соответствующие строки
из таблицы температур.

События
автомобилей

Температура

Полные внешние соединения
возвращают все результаты
из обеих таблиц.

События
автомобилей

Температура

Правые внешние соединения
возвращают все результаты из
таблицы температур и только
соответствующие строки из таблицы
событий автомобилей.

События
автомобилей

Температура

Глава 8. Операции соединения  217

Внешние соединения в стриминговых системах
Теперь вы знаете, как работают внутренние и внешние соединения в базах
данных SQL. В сфере потоковой обработки все организовано примерно так же.
Важное отличие заключается в том, что во многих случаях (например, в задании
контроля выбросов CO2) события одного из входящих потоков обрабатываются одно за другим, тогда как другие потоки материализуются в таблицах для
соединения. Обычно специальный поток рассматривается как левый, а материализуемые потоки как правые. Таким образом, соединение, использованное
в соединителе событий, является левым внешним соединением.
С левым внешним соединением наша команда может идентифицировать автомобили, передвигающиеся за пределами запланированной области, и решить
проблемы целостности данных, подставляя среднюю температуру в генерируемые события «автомобиль — температура» (вместо того, чтобы удалять их).
Такая подстановка повышает точность результатов.
Источник
событий
автомобилей

{

}

{

}

Специальны
й
поток обычн
о
рассматрива
ется
как левый.

make: YYY
model: CC
year: 2015,
zone: X

make: YYY,
model: CC,
year: 2015,
zone: X,
temperature: null -> 95.0

Преобразователь данных
о выбросах

Источник
событий
температуры

zone

temperature
1

95.4

2

94.3

3

95.0

4

95.2

5

95.3

Материализуемый
поток обычно
рассматривается
как правый.

температура
Отсутствующая
ся
бытии заменяет
в выходном со
ны.
зо
я
дл
ой
ур
ат
средней темпер

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

218  Часть II. Движемся дальше

Новая проблема: ненадежное соединение
После решения проблемы целостности данных команда через несколько недель
заметила другую проблему: некоторые значения в таблице температур выглядят
странно. После анализа ситуации выяснилась первопричина: в одном датчике
возникли проблемы с подключением, и иногда он сообщает температуру с периодичностью в несколько часов вместо 10 минут. Проблему можно решить
ремонтом устройства, но нельзя ли повысить устойчивость системы к проблемам с подключением?

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

zone

temperature
1

95.4

2

94.3

3

91.2

4

95.2

5

95.3

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

Оконные соединения
Новая концепция оконных соединений (windowed joins) поможет стриминговому
заданию решить проблемы с ненадежностью канала связи. Смысл понятен из
названия: оконное соединение представляет собой оператор, сочетающий окна
с соединениями. В предыдущей главе оконные вычисления рассматривались
подробно. Но подробности здесь не важны, так что не беспокойтесь, если вы
решили начать чтение с этой главы.
С оконными соединениями задание работает почти так же, как исходная
версия: события автомобилей обрабатываются одно за другим, а события температуры материализуются в таблицу поиска. Тем не менее материализация
событий температуры основана на фиксированном временном окне вместо непрерывных событий. А если говорить конкретнее, события температуры сначала
собираются в буфер и материализуются в пустой таблице в пакетном режиме
каждые 30 минут. Если все датчики успешно передают данные в границах окна,

Глава 8. Операции соединения  219
вычисления работают нормально. Но в случае, если в окне от датчика не было
получено событие температуры, соответствующая строка таблицы поиска будет
пустой, а соединитель события сможет оценить текущее значение по соседним
зонам. На следующей диаграмме температура в зоне 3 используется для оценки
температур в зонах 2 и 4. Использование оконного соединения позволяет гарантировать актуальность всех температурных данных в таблице.

{

}

{

}

make: XXX
model: AA
year: 2020,
zone: 3

make: XXX,
model: AA,
year: 2020,
zone: 3,
temperature: 94.8

zone

temperature
1

95.4

2

94.3

3

94.8

4

95.2

5

95.3

Все входящие события
температуры сначала
помещаются в буфер
и материализуются
в новую таблицу
каждые 30 минут.

Если событие температуры не будет получено
из зоны 3 в течение
30-минутного окна,
то используется оценка,
вычисленная на осно­
вании температур
из зон 2 и 4.

Переключаясь с непрерывной материализации на оконную, мы немного теряем
в задержке температуры (значения температур обновляются каждые 30 минут
вместо 10), но взамен получаем более защищенную систему, которая способна
обнаруживать и автоматически обрабатывать некоторые непредвиденные проблемы.

Соединение двух таблиц вместо соединения
потока с таблицей
Прежде чем завершить эту главу, рассмотрим для примера вариант, в котором
оба потока сначала преобразуются в таблицы, а затем соединяются системой
контроля выбросов CO2. При таком решении процесс в компоненте состоит
из двух шагов: материализации и соединения. Сначала два входящих события
материализуются в две таблицы. Затем логика соединения применяется к таблицам, а результаты передаются последующим компонентам. Обычно на шаге
материализации используются окна, а операция соединения очень похожа на

220  Часть II. Движемся дальше

конструкцию join в базах данных SQL. Учтите, что к разным входящим потокам
могут применяться разные оконные стратегии.
Источник
событий
автомобилей

make

1. События автомобилей
в окне добавляются
в виде строк в таблицу
автомобилей.

model

year

zone

zone

XXX

AA

2020

3

YYY

CC

2013

1

XXX

BB

2014

1

ZZZ

EE

2021

3

Источник
событий
температуры

2. События температуры материализуются
в таблицу температур.

temperature
1

95.4

2

94.3

3

95.0

4

95.2

5

95.3

Соединение

3. Когда обе таблицы станут
доступными, активизируется
Преобразова- операция соединения, после чего
тель данных результаты направляются
последующему компоненту.
о выбросах

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

Глава 8. Операции соединения  221

Снова о материализации представлений
Ранее говорилось о том, что события
{
температуры материализуются более
zone: 3
temperature: 95.0
эффективно, чем события автомоби}
лей. Также упоминалось, что обычно
события в одном специальном потоzone
temperature
ке обрабатываются одно за другим,
а другие потоки материализуются во
1
95.4
временные таблицы, но можно мате2
94.3
риализовать все потоки и соединить
3
95.0
таблицы. Любознательный читатель
4
95.2
наверняка спросит: можно ли соеди5
95.3
нять необработанные события температуры вместо материализованного
представления?
Попробуем сохранить все события температуры в списке и обойтись без временной таблицы. Чтобы избежать нехватки памяти, мы будем удалять события
температуры с возрастом более 30 минут. Для каждого события автомобиля
необходимо найти последнюю температуру зоны в списке температур, сравнивая идентификатор зоны в событии автомобиля с идентификатором зоны для
каждой температуры в списке. Конечный результат будет тем же, но с таблицей
поиска (которая может быть представлена хеш-картой, деревом бинарного
поиска или простым массивом с индексом — идентификатором зоны) поиск
будет выполняться намного эффективнее. Из этого сравнения можно сделать
вывод, что материализованное представление может рассматриваться как оптимизация. Собственно, материализованное представление стало популярным
паттерном оптимизации во многих приложениях обработки данных.
Материализация представления —
популярный паттерн оптимизации
в приложениях обработки данных.

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

222  Часть II. Движемся дальше

Итоги
В этой главе была рассмотрена другая разновидность оператора объединения:
соединение. Операторы соединения, как и операторы слияния, имеют несколько входящих потоков. Но вместо применения одной логики ко всем событиям
разных потоков, в операторах соединения события разных потоков обрабатываются по-разному.
Как и в случае с конструкцией join в базах данных SQL, существуют разные типы соединений. Понимание соединений важно для решения проблемы
целостности данных:
zzВнутренние соединения возвращают только те результаты, которые имеют

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

ствуют соответствующие данные в обеих таблицах или нет.
Существуют три разновидности внешних соединений: полные внешние соединения (или полные соединения), левые внешние соединения (или левые
соединения) и правые внешние соединения (или правые соединения).
В системе контроля выбросов CO2 события автомобилей обрабатываются
как поток, а события температуры используются как таблица поиска. Таблица
является материализованным представлением потока. В конце главы вы также
узнали, что соединения могут использоваться в сочетании с окнами, и познакомились с другими вариантами построения операторов соединения: материализацией всех входных потоков в таблицы и их соединением.

Обратное давление

9

В этой главе
99Общие сведения об обратном давлении.
99Когда активизируется обратное давление.
99Как обратное давление работает в локальных

и распределенных системах.

«Никогда не доверяйте компьютеру, который
не сможете выкинуть из окна».
Стив Возняк

«Приготовьтесь к неожиданностям» — важнейшее правило при построении
любых распределенных систем, и стриминговые системы не исключение. В этой
главе рассматривается популярный механизм обработки ошибок в стриминговых системах: обратное давление (backpressure). Оно помогает защитить
систему от сбоев в некоторых нестандартных ситуациях.

224  Часть II. Движемся дальше

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

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

Глава 9. Обратное давление  225

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

ии
лучает транзакц
1. Шлюз API по
ру
то
та
ен
ез
осы пр
и передает запр
еж
ру
на
об
ю
дани
транзакций и за
.
ий
тв
йс
де
их
ск
ния мошенниче

2. Источник транзакций передает
одну транзакцию
нескольким последующим компонентам-анализаторам.

5. Презентатор объединяет
транзакцию из шлюза API
и оценку риска из базы данных
и предоставляет результат
банку-плательщику.

Презентатор
транзакций

Шлюз
API

Источник
транзакций

Анализатор
среднего
чека

3. Кажды
й анализа
тор
выполняе
т свою ло
гику
и создае
т оценку.

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок
выводит общую
4. Агрегатор оценок
ичества для
нн
оценку риска моше
записывает
и
ии
кц
каждой транза
ее в базу данных.

226  Часть II. Движемся дальше

Усовершенствование стриминговых заданий
Причина, по которой стриминговые системы находят все более широкое применение, — возможность получать данные по мере необходимости, но такую
необходимость бывает сложно предсказать. Компоненты стриминговой системы
или зависимой внешней системы (например, база данных оценок на диаграмме)
могут не справляться с трафиком, и время от времени у них также могут возникать собственные проблемы. Рассмотрим несколько проблем, которые могут
возникнуть в системе обнаружения мошеннических действий.
Количество тран
закций может
внезапно возрас
ти настолько, чт
о
источник транза
кций с ним не
справится.

дых может заме
Передача данн
м
ло
на
ка
с
м
обле
литься из-за пр
связи.

Источник
транзакций

Анализатор
среднего
чека

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

Анализатор
оконного
расстояния

Агрегатор
оценок

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

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

Повод для размышлений
Что случится, если экземпляры
начнут запаздывать или на них про­
изойдет сбой?

Глава 9. Обратное давление  227

Новые концепции: мощность, использование
и резерв мощности
Следующие взаимосвязанные концепции пригодятся при обсуждении обратного
давления:
(capacity) — максимальное количество событий, которое
может быть обработано экземпляром. На практике мощность измерить
не так просто, поэтому для ее оценки часто применяются показатели использования процессора и памяти. Следует учитывать, что в потоковой
системе количество событий, которые могут обрабатываться разными
экземплярами, может сильно различаться.
zzИспользование мощности (capacity utilization) — отношение (выраженное
в процентах) фактического количества обрабатываемых событий к мощности. В общем случае более высокое использование мощности означает
более высокую эффективность.
zzРезерв мощности (capacity headroom) — показатель, обратный использованию. Он представляет собой дополнительные события, которые могут
обрабатываться экземпляром сверх текущего трафика. В большинстве
случаев экземпляр с большим резервом мощности может быть более
устойчивым к неожиданным данным или проблемам, однако его эффективность ниже, потому что больше ресурсов выделяется, но не используется в полной мере.
zzМощность

Допустим, максимальное количество событий, которые могут быть обработаны
этим экземпляром, составляет 10 000 в секунду. Таким образом, мощность этого
экземпляра составляет 10 000 событий в секунду (EPS, Events Per Second). Если
предположить, что в настоящее время экземпляр обрабатывает 7500 событий
в секунду, его использование составляет 75 %, а резерв мощности — 25 %.
Исполнитель экземпляра

Агрегатор
оценок

Исполнитель экземпляра

Анализатор
среднего
чека

Исполнитель экземпляра

Анализатор
среднего
чека

Исполнитель экземпляра

Диспетчер
событий

Агрегатор
оценок
Исполнитель экземпляра

Агрегатор
оценок

228  Часть II. Движемся дальше

Подробнее об использовании
и резерве мощности
В реальных системах время от времени происходит нечто неожиданное, что
приводит к пиковым значениям использования. Примеры:
zzКоличество

входящих событий может резко возрастать в отдельные моменты времени.
zzМогут происходить сбои оборудования — например, перезапуск компьютера из-за сбоя питания или снижение пропускной способности, если
канал связи занят другими процессами.
Важно учитывать эти потенциальные проблемы при построении распределенных
систем. Грамотно написанное задание должно преодолевать эти временные трудности самостоятельно. В стриминговых системах при достаточном резерве мощности
задание должно нормально выполняться без вмешательства со стороны пользователя. Тем не менее резерв мощности не может быть бесконечным (вдобавок он не
бесплатен). Когда использование достигает 100 %, экземпляр становится полностью загруженным и следующей защитной мерой становится обратное давление.

Использование экземпляра

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

Резерв мощности
экземпляра

Уровень текущег
о
использования.
Время

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

Глава 9. Обратное давление  229

Новая концепция: обратное давление
Когда использование достигает 100 %,
ситуация меняется. Рассмотрим происходящее на примере задания обнаружения
мошеннических действий.
1. Экземпляры
анализатора генери
я.
ти
руют собы

2. События в очереди ожидают
обработки.

Анализатор
среднего
чека

Исполнитель
экземпляра

Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

Диспетчер перемещает события
между очередями.
4. Проходит некоторое время…

Исполнитель
экземпляра

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

Исполнитель
экземпляра

Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

Агрегатор
оценок

Исполнитель
экземпляра

5. Так как последующий
экземпляр запаздывает,
промежуточная очередь
заполняется событиями,
ожидающими обработки.
Здесь должен вступить
в силу механизм обратного давления.

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

230  Часть II. Движемся дальше

события со скоростью входящего трафика, или, иначе говоря, когда использование достигает 100 %. Целью обратного давления является замедление входящего
трафика, если трафик превышает возможности системы по его обработке.

Измерение использования мощности
Обратное давление должно срабатывать, когда использование достигает 100 %.
Но мощность и ее использование не так просто измерить или оценить. Существует множество факторов, ограничивающих количество событий, которые могут
быть обработаны экземпляром: ресурсы, оборудование, данные и т. д. Уровни
использования процессора и памяти полезны, но это ненадежная характеристика
мощности. Требуется более эффективное решение; к счастью, оно существует.
Вы узнали, что работающая стриминговая система состоит из процессов
и очередей событий, которые их связывают. Очереди событий отвечают за
передачу событий между экземплярами (по аналогии с конвейерными лентами,
связывающими рабочих на сборочной линии). Когда использование экземпляра
достигает 100 %, скорость обработки начинает отставать от входящего трафика.
В результате события во входящей очереди экземпляра начинают накапливаться. Таким образом, по длине входящей очереди экземпляра можно определить,
достигло ли использование экземпляра своего максимума.
Обычно длина очереди должна возрастать и убывать в пределах относительно
устойчивого диапазона. Если она возрастает непрерывно, то, скорее всего, экземпляр слишком занят и не справляется с обработкой трафика.
Далее мы сначала рассмотрим обратное давление на примере локального ядра
Streamwork, чтобы вы поняли некоторые основные принципы, а затем перейдем
к более общим распределенным фреймворкам.
Обратное давление особенно полезно для решения внешних проблем: перезапуска экземпляров, сопровождения зависимых систем, внезапных выбросов событий от источников. Стриминговая система устраняет
Исполнитель
экземпляра
их, временно замедляя раИсполнитель
боту и затем возобновляя ее
экземпляра
без вмешательства пользоваИсполнитель
теля. Следовательно, очень
экземпляра
важно понимать, что может
и что не может сделать обИсполнитель
экземпляра
ратное давление, чтобы при
Исполнитель
возникновении проблем не
экземпляра
впадать в панику и держать
ситуацию под контролем.
После накопления в очереди слишком большого количества событий должно произойти
событие обратного давления, замедляющее поступление событий от предшествующих
компонентов.

Глава 9. Обратное давление  231

Обратное давление в ядре Streamwork
Начнем с нашего ядра Streamwork, так как оно устроено достаточно просто.
Ядро Streamwork как локальная система не имеет сложной логики обратного
давления. Тем не менее приведенная информация поможет понять, как работает
обратное давление в реальных фреймворках.
В ядре Streamwork блокирующие очереди (очереди, которые могут приостанавливать потоки, пытающиеся присоединять новые события при заполненной
очереди, или получать элементы при пустой очереди) используются для соединения процессов. Длины очередей ограничены — у каждой очереди существует
максимальная емкость, которая играет ключевую роль в механизме обратного
давления. Когда экземпляр не успевает достаточно быстро обрабатывать события, скорость уменьшения очереди перед ним будет ниже скорости вставки.
Очередь начинает расти и со временем заполняется. В дальнейшем операция
вставки блокируется до того, как событие перейдет в последующий экземпляр.
В результате скорость вставки снижается до скорости обработки событий последующим экземпляром.

В Streamwork для соединения процессов используются
блокирующие очереди
с заданной емкостью.
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

Исполнитель
экземпляра

едний
1. Посл р комполя
экземп успевает
е
н
нента
тывать ик.
а
б
обра
ф
ий тра
входящ

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

232  Часть II. Движемся дальше

Обратное давление в ядре Streamwork:
распространение
Замедление диспетчера событий не решает всех проблем. После того как диспетчер событий замедлится, то же самое произойдет с очередью, расположенной
между ним и предшествующими экземплярами. Когда эта очередь будет заполнена, это повлияет на все экземпляры предшествующего компонента. Чтобы
увидеть перед диспетчером событий блокирующую очередь, общую для всех
предшествующих компонентов, рассмотрим работу обратного давления в ядре
Streamwork более подробно на следующей диаграмме.
1. Когда эта очередь заполняется, она блоки
их
вующ
шест
рует входящие события от пред
экземпляров.

2. В результате
скорость
обработки
предшествующих экземпляров снижается.

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Если перед этим компонентом происходит объединение (то есть несколько
непосредственно предшествующих компонентов для одного последующего
компонента), это повлияет на все такие компоненты, потому что события блокируются в одной блокирующей очереди.
При наличии
нескольких
предшествующих компонентов
замедлится
работа всех этих
экземпляров,
потому что все
они будут
направлять
события в одну
блокирующую
очередь.

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Глава 9. Обратное давление  233

Стриминговое задание с обратным давлением
Посмотрим, как обратное давление с ядром Streamwork влияет на работу задания обнаружения мошеннических действий, когда экземпляр агрегатора
оценок не справляется с входящим трафиком. Сначала только агрегатор оценок
работает на пониженной скорости. Затем предшествующие анализаторы замедляются из-за обратного давления. Со временем обратное давление замедляет
весь процесс выполнения задания, и его производительность понизится, пока
проблема не будет решена.
Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций
Че

ре
з
се нес
ку ко
нд льк
о

1. Задание начи
нает
работать нормал
ьно,
но агрегатор оц
енок
работает медл
еннее
других компон
ентов.

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

2. Так как агрегатор оценок
запаздывает, между ним и непосредственно ему предшествующими компонентами возникает
обратное давление и предшествующие компоненты в какой-то момент
замедляются.

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

о

ьк
ол
ск д
е
н н
ез ку
ер се

Ч

Анализатор
оконного числа
транзакций

Агрегатор
оценок

е линии
Волнисты
вляют
предста
давлеобратное двумя
у
д
ние меж ми.
а
т
н
е
н
компо

3. Со временем обратное давление выйдет за пределы компонентов анализатора и достигнет
источника. Поступление входных
данных замедлится.

234  Часть II. Движемся дальше

Обратное давление в распределенных системах
В целом обнаружение и реализация обратного давления в локальных системах
на базе блокирующих очередей достаточно просты. Однако в распределенных
системах ситуация усложняется. Рассмотрим возможные сложности поэтапно:
1. Обнаружение занятых экземпляров.
2. Состояние обратного давления.

Обнаружение занятых экземпляров
На первом шаге важно обнаружить занятые экземпляры, чтобы система могла
реагировать с опережением. В главе 2 уже упоминалось, что такая структура
данных, как очередь событий, широко применяется в стриминговых системах
для соединения процессов. Хотя обычно используются неограниченные очереди, отслеживание размера очередей позволяет определить, успевает ли экземпляр обрабатывать входящий трафик. Если говорить конкретнее, есть минимум
две разные метрики, которые могут использоваться для определения порога:
zzКоличество событий в очереди.
zzРазмер памяти, занимаемой событиями в очереди.

Когда количество событий или размер памяти достигают порогового значения,
это означает, что у присоединенного экземпляра возникли проблемы. Ядро объявляет состояние обратного давления.

Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

1. Если максим
альная емкость
очереди — 6 эл
ементов, то, по
скольку
оно уже равно
6, задание вход
ит в состояние обратн
ого давления.

Исполнитель
экземпляра

емы —
рог памяти сист
2. Или, если по
ют
ма
элементов зани
1 килобайт и 6
е
ни
да
за
ти и более,
ения.
1 килобайт памя
вл
да
о
ог
тн
ние обра
входит в состоя

Глава 9. Обратное давление  235

Состояние обратного давления
После объявления состояния обратного давления, как и в случае с ядром
Streamwork, входящие события должны замедлиться. Тем не менее в распределенных системах эта задача часто оказывается намного более сложной, чем
в локальных, потому что экземпляры могут работать на разных компьютерах
и даже в разных регионах. По этой причине стриминговые фреймворки обычно
останавливают обработку входящих событий вместо ее замедления, чтобы занятый экземпляр хотя бы временно получил ресурс мощности:
zzостанавливаются экземпляры предшествующих компонентов

или
zzостанавливаются экземпляры источников.

Далее в этой главе будет рассмотрен еще один вариант, хотя и намного менее
популярный: отбрасывание событий. Этот вариант может показаться нежелательным, но и он может быть полезен, если задержка между конечными точками
более критична, а потеря событий приемлема. По сути, выбор между этими
двумя вариантами означает компромисс между точностью и задержкой.
Два варианта поясняются на следующих диаграммах. Мы добавили экземпляр источника, чтобы упростить объяснения, и для краткости опустили подробности промежуточных очередей и диспетчеров очередей.
Можно остановить экземпляры источника или
предшествующих компонентов, пока накопленные
события в заполненной очереди не будут обработаны.

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Источник
транзакции

Анализатор
среднего
чека

Агрегатор
оценок

Замедление трафика
блокирующей очередью удобно
в локальных системах, но в распре­
деленных системах задача
­усложняется.

Также, если эт
от вариант
приемлем, возм
ожно отбрасыв
ание событий пр
и попытке их
занесения в оч
ередь, которая
достигает свое
го порога.

236  Часть II. Движемся дальше

Обратное давление: остановка источников
Вероятно, остановка компонента-источника — самый простой способ снизить
обратное давление в распределенных системах. Она позволяет приостановить
поток входящих событий медленного экземпляра, а также всех остальных экземпляров стримингового задания; это может быть уместно в том случае, если
существует вероятность появления нескольких занятых экземпляров.
Источник
транзакций

Анализатор
среднего
чека

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

1. Здесь возник
ает обратное да
вление,
его необходимо
снизить.

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

Анализатор
оконного
расстояния

Анализатор
оконного числа
транзакций

Агрегатор
оценок

2. Источник можно
временно остановить,
прекращая передачу
дополнительных транзакций
заданию; для этого всем
экземплярам компонентаисточника отправляется
специальное сообщение.
Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

Глава 9. Обратное давление  237

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

Анализатор
среднего
чека

Исполнитель
экземпляра
Исполнитель
экземпляра

Исполнитель
экземпляра

Анализатор
оконного числа
транзакций

Агрегатор
оценок

Исполнитель
экземпляра

Исполнитель
экземпляра

Источник
транзакций

1. Здесь возник
ает обратное да
вление,
его необходимо
снизить.

2. На уровне компонентов мы бы
остановили все три компонентаанализатора, чтобы они перестали
получать новые события от
источника и направлять результаты
компоненту — агрегатору оценок.

Анализатор
оконного
расстояния

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

238  Часть II. Движемся дальше

Снятие обратного давления
После того как задание пробудет в состоянии обратного давления в течение
некоторого времени и занятый экземпляр восстановится (хочется надеяться),
возникает следующий вопрос: что считать завершением состояния обратного
давления, чтобы можно было восстановить нормальный трафик?
Решение вас вряд ли удивит, так как оно похоже на первый этап — обнаружение: нужно отслеживать размер очередей. Но, в отличие от этапа обнаружения,
на котором мы проверяли, не переполнилась ли очередь, на этот раз мы проверяем, достаточно ли она пуста, то есть опустилось ли количество событий
ниже минимального порога и появилось ли в очереди достаточно места для
новых событий.
Обратите внимание: снятие обратного давления не означает, что медленный
экземпляр восстановил работоспособность. Оно означает лишь то, что в очереди
появилось место для новых событий.
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

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

Исполнитель
экземпляра

лема может
Учтите, что проб
ого
и снятии обратн
пр
сохраниться и
давления.

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

Глава 9. Обратное давление  239

Новая концепция: водяные знаки
обратного давления
Размеры промежуточных очередей проверяются и сравниваются с пороговыми
значениями для включения и снятия состояния обратного давления. Рассмотрим эти два порога в рамках нового понятия: водяных знаков обратного давления. Обычно они являются параметрами конфигурации, предоставляемыми
стриминговыми фреймворками:
zzВодяные знаки обратного давления представляют собой верхний и ниж-

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

давления, система должна объявить состояние обратного давления (если
оно не было объявлено ранее).
zzЕсли обратное давление активно, а размер данных в очереди, вызвавшей
обратное давление, меньше нижнего водяного знака, обратное давление
можно снять. Учтите, что нижний водяной знак обратного давления нежелательно задавать равным нулю, поскольку это означает, что предыдущий занятый экземпляр будет простаивать в промежуток времени между
снятием обратного давления и поступлением новых событий в очередь.
Размеры данных в очередях увеличиваются и уменьшаются при обработке событий заданием. В идеале они всегда находятся в промежутке между нижним
и верхним водяными знаками обратного давления, чтобы события обрабатывались на полной скорости.

Если размер данных достигает
верхнего водяного знака
обратного давления — например, 80 Мбайт (80 % полной
емкости) и выше, запускается
обратное давление, так как
очередь «почти заполнена».

Если размер данных достигает
нижнего водяного знака обратного
давления — например, 20 Мбайт
(20 % полной емкости), обратное
давление можно снять, так как
очередь «почти пуста».

Вертикальная шкала
представляет данные в промежуточной очереди. Допустим,
в качестве верхней границы
выбрано значение 100 Мбайт.
Как правило, верхняя граница
является мягким пределом; это
означает, что размер данных
может превысить его в крайних случаях, хотя такая
ситуация и нежелательна.
Обратите внимание: пустая
очередь тоже нежелательна, так как она с большой
вероятностью указывает на
то, что последующий
экземпляр тратит много
времени на ожидание
обработки процессов.

240  Часть II. Движемся дальше

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

Игнорирует
события?
Кошмар! Кому
такое в голову
придет?

Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

Исполнитель
экземпляра

Если очередь уж
е содержит слиш
ком много событи
новые события бу
й,
дут просто отбрас
ываться.

Этот вариант выглядит пугающе, потому что результаты будут неточными.
Да, это, безусловно, так. Если вы помните семантику доставки, о которой говорилось в главе 5, то заметите, что этот вариант должен применяться только
в ситуациях «не более одного».
Тем не менее все не так страшно, как кажется. Результаты оказываются неточными, только если экземпляр не справляется с трафиком, что при правильной конфигурации системы происходит редко. Иначе говоря, результаты должны быть точными почти всегда. Мы уже несколько раз отмечали, что обратное
давление представляет собой механизм самозащиты в экстремальных ситуациях, предотвращающий аварийный останов системы. Для стриминговых заданий
состояние обратного давления не оптимально. Если оно возникает слишком
часто, проверьте систему и попытайтесь найти и устранить первопричину.

Глава 9. Обратное давление  241

Когда отбрасывание событий допустимо?
Разве кто-нибудь согласится, чтобы система теряла события? Не только вы
задаете такой вопрос. В процессе разработки заданий спросите себя: готовы ли
вы поступиться точностью ради снижения задержки в случае, если какой-то
экземпляр не будет справляться с рабочей нагрузкой?
Возьмем для примера социальные сети и задачу отслеживания количества
взаимодействий с пользователями (например, лайков) в реальном времени. Во
втором варианте счетчик всегда будет содержать самые новые данные, хотя
и не на 100 % точные. Если проблема затрагивает 1 экземпляр из 100, можно
ожидать, что ошибка составит менее 1 %. Если применять обратное давление
для остановки событий, счетчик будет точным, но вы не получите самые последние данные, пока система находится в состоянии обратного давления, потому
что работа системы замедлится. После снятия этого состояния системе также
понадобится время, чтобы наверстать отставание и вернуться к новейшим событиям. Вы не получите последнее значение, пока проблема не будет решена,
что в случае длительного сбоя может быть хуже погрешности < 1 %. По сути,
при отбрасывании событий вы получаете систему, приближенную к реальному
времени, при предположительно достаточной точности результатов.
Вернемся к заданию обнаружения мошеннических действий — соблюдение
сроков в нем критично. Приостановка обработки данных на несколько минут и нарушение требований к задержке до того, как проблема будет решена
обратным давлением, в данном случае
неприемлемы. Если сравнивать два
варианта, то предпочтительнее
Отбрасывание событий —
обеспечить работу без задержек,
стандартное решение, если
даже при небольшой потере точнеобходим баланс между
точностью и общей
ности. Безусловно, о происхозадержкой.
дящем следует сообщить техническим специалистам, чтобы они
проанализировали проблему и устранили ее как можно скорее. Отслеживание количества отброшенных событий чрезвычайно важно для понимания текущего состояния и уровня точности результатов.

242  Часть II. Движемся дальше

Обратное давление как возможный симптом
Обратное давление — важный механизм решения временных проблем в стриминговых системах (например, сбоев экземпляров и внезапных пиков входного
трафика), чтобы предотвратить более серьезные сбои. Стриминговые системы
могут возвращаться в нормальное состояние автоматически после устранения
причины без вмешательства со стороны пользователя. Иначе говоря, с обратным давлением стриминговые системы более устойчивы к непредвиденным
проблемам, что обычно желательно для распределенных систем. Конечно, было
бы идеально, если бы в системе никогда не возникало обратное давление, но,
к сожалению, реальные условия от идеальных далеки. Обратное давление — необходимая мера предосторожности.
Хотя мы надеемся, что проблема временная и обратное давление справится с ней за нас, все зависит от ситуации. Вполне возможно, что экземпляр не
восстановится сам по себе и для устранения причины потребуется внешнее
вмешательство. В таких случаях постоянное обратное давление становится
симптомом. Обычно существуют два варианта развития событий, и каждый
требует особых действий.
zzЭкземпляр

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

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

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

Глава 9. Обратное давление  243

Останов и возобновление работы могут
привести к пробуксовке
А теперь рассмотрим эффект, называемый пробуксовкой (thrashing). Если проблема имеет долгосрочный характер, то при объявлении заданием состояния
обратного давления события в очередях будут приниматься всеми экземплярами; но как только состояние обратного давления будет снято, новые события
данных снова переполняют экземпляр и состояние вскоре объявляется заново.
Пробуксовка — циклическое объявление и снятие обратного давления.
Нормальное состояние
Цикл проб

уксовки

Количество событий в очереди

Состояние обратного давления

Количество событий во
входящей очереди колеблется
между верхним и нижним
водяным знаком.

Верхний и нижний
водяные знаки
обратного давления

Время

Пробуксовка ожидаемо возникает в том случае, если ситуация не изменяется.
Если один экземпляр по-прежнему не справляется с обработкой трафика, размер данных в очереди увеличится снова, пока не достигнет верхнего предела,
что приведет к повторной активизации обратного давления. А после следующего
снятия обратного давления это с большой вероятностью произойдет снова.
Количество событий во входящей очереди экземпляра изменяется так, как показано на диаграмме выше. Чтобы выйти из пробуксовки, необходимо найти
первопричину и устранить ее.

244  Часть II. Движемся дальше

Решение проблемы пробуксовки
Если в системе наблюдается пробуксовка, выясните, почему экземпляр не
обрабатывает события с нужной скоростью. Может быть, в нем возникла
внутренняя проблема, замедляющая работу, или пришло время масштабировать систему? Как правило, у подобных проблем два источника — трафик
и компоненты.
zzВозможно, поток событий от источника повысился до постоянного уров-

ня, с которым задание не справляется. В этом случае задание необходимо
масштабировать, чтобы обрабатывать новый трафик. Если говорить
конкретнее, возможно, стоит начать с повышения параллелизма (количества экземпляров компонента — подробности см. в главе 3) медленных
компонентов задания.
zzСкорость обработки некоторых компонентов может по какой-то причине
снизиться. Проверьте компоненты, попробуйте их оптимизировать или
перенастроить. Учитывайте зависимости, используемые компонентами.
Нередко зависимости могут работать медленнее при изменении модели
трафика.

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

Обратное давление играет
важную роль в обеспечении
устойчивости систем, но еще
важнее понимать его
первопричины.

Глава 9. Обратное давление  245

Итоги
В этой главе рассматривался часто возникающий механизм обратного давления.
Вы узнали:
zzКогда и почему возникает обратное давление.
zzКак

стриминговые фреймворки обнаруживают проблемы и устраняют
их, используя обратное давление.
zzОстановка входящего трафика или отбрасывание событий — принципы
работы, плюсы и минусы.
zzЧто можно сделать, если первопричина продолжает существовать.
Обратное давление — важный механизм. Надеемся, что знание принципов его
работы пригодится вам при обслуживании и усовершенствовании стриминговых систем.

10

Вычисления с состоянием

В этой главе
99Что такое компоненты с состоянием и без состояния.
99Как работают компоненты с состоянием.
99Сопутствующие методы.

«А вы пробовали выключить, а потом снова включить?»
Сериал «Компьютерщики»

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

Глава 10. Вычисления с состоянием  247

Миграция стриминговых заданий
Обслуживание — часть повседневной работы с распределенными системами.
Несколько примеров: выпуск новой сборки с исправлением ошибок и новым
функционалом, обновление программ или оборудования для повышения безопасности или эффективности систем, обработка отказов оборудования для
обеспечения беспрерывной работы системы.
ЭйДжей и Сид решили перенести стриминговые задания на новое, более
эффективное оборудование, чтобы сократить затраты и повысить надежность.
Это довольно масштабная задача обслуживания, и очень важно действовать
осторожно.

С каким риском
сопряжена миграция
стриминговых заданий на новые
машины? Повлияет ли она на
результаты работы в процессе
миграции?

Миграция не должна
влиять на результаты. Компоненты реализуются как компоненты
с состоянием там, где это необходимо.
Когда экземпляры перестают существовать
на старых машинах и перезапускаются на
новых, они автоматически продолжают
работать в своих предыдущих
состояниях.

248  Часть II. Движемся дальше

Компоненты с состоянием в задании контроля
за использованием системы
Компоненты с состоянием (stateful) очень полезны для построения компонентов с внутренними данными. Мы кратко говорили о них в главе 5 в контексте
задания контроля за использованием системы. Пришло время поближе рассмотреть их и понять, как они работают.
Компоненты с состоянием кратко рассматривались в предыдущих главах.
В нашем стриминговом задании есть несколько точек, в которых они будут
уместны.

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

транзакций должен отслеживать
смещения обработки. Смещение (offset) обозначает место чтения компонентом — источником транзакций в источнике данных
(журнале событий).

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

Шлюз
API

Источник
транзакций

Анализатор
использования
системы
Блок записи
данных об
использовании

Источни
к
данных
(журнал
событий
)

Глава 10. Вычисления с состоянием  249
zzСчетчики транзакций критичны для работы анализатора использования

системы, их тоже необходимо сохранить.
zzУ блока записи данных об использовании нет данных, которые нужно
сохранять или восстанавливать.
Следовательно, первые два компонента должны быть реализованы как компоненты с состоянием, а последний — как компонент без состояния (stateless).

Снова о состоянии
Прежде чем двигаться дальше, вернемся к одной из фундаментальных концепций: что такое состояние? Как объяснялось в главе 5, состояние представляет
собой внутренние данные каждого экземпляра, изменяемые по мере обработки
событий. Например, состояние компонента — источника транзакций определяет
место во внешнем источнике, откуда экземпляр загружает данные (смещение).
Оно перемещается вперед после загрузки новых событий. Посмотрим, как состояние экземпляра источника транзакций изменяется до и после обработки
двух экземпляров.

Исходное состояние до обработки
транзакций 1 и 2.

Время
{
}

смещение = 100

Транзакция 1
загружается из
источника данных.
{
}
{
}

няется после
Состояние изме
закции 1.
обработки тран

смещение = 101

е

Состояние снова изменяется посл
обработки транзакции 2.
смещение = 102

Транзакция 2 загружается из источника
данных.

250  Часть II. Движемся дальше

Состояния в разных компонентах
Все становится интереснее, когда мы рассматриваем совокупность состояний разных компонентов. В главе 7, посвященной оконным вычислениям,
говорилось, что время обработки события должно быть разным для разных
экземпляров, потому что событие передается от одного экземпляра к другому.
Аналогичным образом для одного события в разных экземплярах изменения
состояния происходят в разное время. Рассмотрим изменения состояния экземпляра источника транзакций и экземпляра анализатора использования системы
до и после обработки двух транзакций.
Экземпляр источника
транзакций

Экземпляр анализатора
использования системы

Время
{
}

смещение = 100

{
}

Исходные состояния
до обработки
транзакций 1 и 2.

счетчик = 1000

Транзакция 1 передается от экземпляра
источника данных экземпляру анализатора
использования.
{
}

{
{
}

Состояние изменяется пос
ле
получения и учета транзакции 1.

смещение = 101

смещение = 102

}

{
}

счетчик = 1001

счетчик = 1002

Что можно
сделать, чтобы все
экземпляры сохраняли
состояния в нужное
время?

ется
е изменя
Состояни
ния
ч
у
ле пол е
снова пос закции 2.
ран
и учета т

Глава 10. Вычисления с состоянием  251

Данные состояния и временные данные
До сих пор состояние определялось просто: внутренние данные экземпляра,
которые изменяются при обработке событий. Да, определение правильное,
но некоторые данные состояния могут быть временными, и их не нужно восстанавливать при восстановлении экземпляра. Обычно временные данные не
включаются в состояние экземпляра.
Например, кэширование — популярный метод улучшения быстродействия
и/или эффективности. В процессе кэширования компонент, находящийся перед
затратными или медленными вычислениями (например, сложной функцией или
запросом к удаленной системе), сохраняет результаты запроса, чтобы вычисления
не приходилось выполнять повторно. Обычно содержимое кэша не считается
данными состояния экземпляра, хотя оно и может изменяться при обработке
событий. В конце концов, экземпляр должен правильно работать с совершенно
новым кэшем после перезапуска. Подключение к базе данных в каждом экземпляре блока записи данных об использовании также относится к временным данным,
так как подключение придется создавать заново при перезапуске экземпляра.
Другой пример — компонент — источник транзакции в задании обнаружения
мошеннических действий. Внутри каждый экземпляр хранит смещение события
последней транзакции, загруженного из источника данных. Но, как упоминалось в главе 5, так как задержка критична для этого задания, при перезапуске
экземпляра предпочтительно сразу перейти к последней транзакции, вместо
того чтобы восстанавливать предыдущее смещение. В этом задании смещение
относится к временным данным, и его не следует относить к данным состояния.
А значит, этот компонент является компонентом без состояния.
Задание обнаружения
мошеннических действий
Экземпляр источника
транзакций
{
}

смещение = 102

Смещение
должно
сохранятьс
я и восста
навливаться п
ри перезап
уске
экземпляр
а. Следова
тельно, оно отн
осится к с
остоянию экзем
пляра.

Задание контроля за
использованием системы
Экземпляр источника
транзакций
{
}

смещение = 102

ым
м данн
менны раняете
р
в
к
не сох
осится
ние отн
мяти) и а.
Смеще я только в па
р
я
тс
кземпл
(храни резапуске э
пе
ся при

Итак, состояние экземпляра включает только ключевые данные, чтобы экземп­
ляр можно было развернуть в предыдущем состоянии и он мог продолжить
нормально работать. Временные данные в стриминговых системах обычно не
считаются данными состояния.

252  Часть II. Движемся дальше

Компоненты с состоянием и без состояния: код
Компонент — источник транзакции есть как в задании контроля за использованием системы, так и в задании обнаружения мошеннических действий и работает в обоих случаях примерно одинаково. Единственное различие заключается
в том, что в задании контроля он обладает состоянием, а в задании обнаружения
мошенничества — нет. Ниже мы приводим коды обоих заданий, чтобы вы могли
проследить за изменениями в компоненте с состоянием.
zzФункция setupInstance() получает дополнительный параметр state.
zzВводится новая функция getState().

Версия

с состоянием
class TransactionSource extends StatefulSource {
в задании конт
роля.
EventLog transactions = new EventLog();
int offset = 0;
......
public void setupInstance(int instance, State state) {
SourceState mstate = (SourceState)state;
Данные в объекте состояния
if (mstate != null) {
используются для настройки
offset = mstate.offset;
экземпляра.
transactions.seek(offset);
}
}
public void getEvents(Event event, EventCollector eventCollector) {
Transaction transaction = transactions.pull();
eventCollector.add(new TransactionEvent(transaction));
offset++;
system.out.println("Reading from offset %d", offset);
}
public State getState() {
SourceState state = new SourceState();
State.offset = offset;
return new state;
Объект состояния экземпляра содержит
}
текущее смещение в журнале событий.
}
сlass TransactionSource extends Source {
EventLog transactions = new EventLog();
int offset = 0;
......
public void setupInstance(int instance) {
offset = transactions.seek(LATEST);
}

}

Версия бе
з состояни
я в задани
обнаруже
и
ния мошенн
ических
действий.

public void getEvents(Event event, EventCollector eventCollector) {
Transaction transaction = transactions.pull();
eventCollector.add(new TransactionEvent(transaction));
offset++;
system.out.println("Reading from offset %d", offset);
}

Глава 10. Вычисления с состоянием  253

Источник с состоянием и оператор в задании
контроля за использованием системы
В главе 5 был представлен код классов TransactionSource и SystemUsa­
geAna­
lyzer. Теперь объединим их и сравним. В целом процессы с состоянием в источниках с состоянием и операторах протекают примерно одинаково.
class TransactionSource extends StatefulSource {
MessageQueue queue;
int offset = 0;
......
public void setupInstance(int instance, State state) {
SourceState mstate = (SourceState)state;
Данные в объекте состояния
if (mstate != null) {
используются для настройки
offset = mstate.offset;
экземпляра.
log.seek(offset);
}
}
public void getEvents(Event event, EventCollector eventCollector) {
Transaction transaction = log.pull();
eventCollector.add(new TransactionEvent(transaction));
offset++;
Значение offset изменяется при
}

извлечении нового события из журнала

событий и передаче его последующим
public State getState() {
SourceState state = new SourceState(); компонентам.
State.offset = offset;
return new state;
Объект состояния экземпляра содержит
}
текущее смещение в журнале событий.
}
class SystemUsageAnalyzer extends StatefulOperator {
int transactionCount;

public void setupInstance(int instance, State state) {
AnalyzerState mstate = (AnalyzerState)state;
При формировании экземпляра
transactionCount = state.count;
объект состояния используется
}

для его инициализации.

public void apply(Event event, EventCollector eventCollector) {
transactionCount++;
}

}

eventCollector.add(transactionCount);

Переменная-счетчик изменяется
при обработке событий.

public State getState() {
AnalyzerState state = new AnalyzerState();
State.count = transactionCount;
Новый объект состояния
return state;
периодически создается для
}

хранения данных экземпляра.

254  Часть II. Движемся дальше

Состояния и контрольные точки
По сравнению с компонентами без состояния, рассмотренными выше, в компоненты с состоянием добавляются две функции, которые необходимо реализовать разработчикам:
zzФункция getState()

преобразует данные экземпляра в объект состо-

яния.

zzФункция setupInstance() использует объект состояния для реконструк-

ции экземпляра.

А теперь посмотрим, что происходит на самом деле. Понимание этого механизма
поможет вам создавать эффективные и надежные задания и находить причины
возникновения проблем.
В главе 5 контрольная точка была определена как «блок данных, который
может использоваться экземпляром для восстановления предыдущего состояния». Стриминговое ядро — а конкретнее исполнитель экземпляра и диспетчер
контрольных точек (помните принцип единой ответственности?) — отвечает за
вызов двух функций в следующих двух случаях соответственно:
zzФункция getState() периодически вызывается исполнителем экземпля-

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

м1. Исполнитель экзе
ояст
со
пляра получает
.
ра
ля
мп
ние от экзе
getState()

saveState()

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Диспетчер
контрольных
точек

Контрольная точка

Глава 10. Вычисления с состоянием  255
zzФункция setupInstance() вызывается исполнителем экземпляра после

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

1. Исполнитель экземпляра
получает состояние от диспетчера
контрольных точек, который ищет
и загружает данные контрольной
точки из хранилища.

2. Исполнитель экземпляра использует
объект состояния для настройки
экземпляра.

loadState()

Диспетчер
контрольных
точек

Контрольная
точка

setupInstance()

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

Создание контрольных точек: сложность
выбора момента времени
В обязанности исполнителей экземпляров входит вызов функции getState()
экземпляров для получения текущих состояний и их передачи диспетчеру
контрольных точек для сохранения в контрольной точке. Остается понять, как
исполнители экземпляров определяют правильный момент для запуска этого
процесса.
Первая естественная мысль — создавать контрольные точки по часам. Все исполнители экземпляров запускают функцию одновременно. Создается полный
«моментальный снимок» всей системы, как при переводе компьютера в спящий
режим, когда все содержимое памяти сохраняется на диске и данные снова загружаются в память при пробуждении компьютера.
Тем не менее в стриминговых системах такой подход не работает. При запуске создания контрольной точки некоторые события могут быть обработаны
частью компонентов, но при этом остаются необработанными последующими
компонентами. Если контрольная точка будет создана подобным способом

256  Часть II. Движемся дальше

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

Анализатор
использования
системы

Проблема
возникает из-за того,
что одно событие обрабатывается разными компонентами
в разное время. Как обеспечить синхронизацию всех
экземпляров?

Блок записи
данных об
использовании

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

Событийный отсчет времени
При создании контрольных точек в стриминговых системах время измеряется
не по часам, а по идентификатору события. Например, в задании контроля за
использованием системы источник транзакции будет находиться во времени
транзакции 1001, когда транзакция 1001 была обработана и выдана им. Анализатор использования системы в тот же момент физического времени будет
находиться во времени после транзакции 1000 и достигнет времени транзакции
1001 после получения, обработки и выдачи транзакции 1001. На следующей
диаграмме физическое время и событийное время изображены параллельно.
Для простоты будем предполагать, что каждый компонент существует только

Глава 10. Вычисления с состоянием  257
в одном экземпляре. Случай множественных экземпляров будет рассмотрен
позже, когда мы будем говорить о реализации.
Физическое
время

Экземпляр источника
транзакций
{
}

}

{

смещение = 100

Транзакция 1000
загружается в задание.

{

Экземпляр анализатора контроля
за использованием системы

}

Транзакция 1000 обработана экземпляром
источника транзакций.

смещение = 101
{

{
}

смещение = 102

Транзакция 1001
загружается в задание.

счетчик = 1000

}

{
}

Транзакция 1000 обработанаэкземпляром
анализатора использования системы.

счетчик = 1001

счетчик = 1002

Событийное
время: транзакция 1000
Событийное
время: транзакция 1001

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

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

258  Часть II. Движемся дальше

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

Журнал
событий

тия
Собы
х
данны

Источник
транзакций

Анализатор
использования
системы
Блок записи
данных об
использовании

Со
контр бытие
ольно
й точ
ки

1

1. Диспетчер
контрольных точе
к
отвечает за пе
риодическое создан
ие
событий конт
рольных
точек.

Диспетчер
контрольной
точки

рольной точки
2. Событие конт
й
в поток событи
будет включено
в
ро
ля
экземп
исполнителями
очника.
ст

та
ен
компон

Учтите, что экземпляры компонента-источника, содержащие пользовательскую
логику, не знают о существовании события контрольной точки. Им известно
лишь то, что функция getState() вызывается исполнителем экземпляра для
извлечения текущих состояний.

Глава 10. Вычисления с состоянием  259

Событие контрольной точки обрабатывается
исполнителями экземпляров
Все исполнители экземпляров повторяют одни и те же действия:
zzВызов функции getState() и отправка состояния диспетчеру контроль-

ных точек.
zzВключение события контрольной точки в ее выходной поток.

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

Источник
транзакций

1

Диспетчер
контрольной
точки

Анализатор
использования
системы
Блок записи
данных об
использовании
Экземпляр компонента — источника транзакций отправляет состояния экземпляров диспетчеру
контрольных точек и включает
событие контрольной точки в свой
выходной поток.

Объект
со
с идент стояния
ификат
ором
контрол
ьной то
чки
Журнал
событий

Источник
транзакций
1
Анализатор
использования
системы
Блок записи
данных об
использовании

Cостояние: 1

Диспетчер
контрольной
точки

260  Часть II. Движемся дальше

Путь события контрольной точки через задание
После того как событие контрольной точки будет добавлено в поток событий
исполнителями экземпляров источника, оно пройдет через задание и всех исполнителей экземпляров операторов в нем. На двух диаграммах, приведенных
ниже, показано, что событие контрольной точки с идентификатором 1 последовательно обрабатывается компонентами источника транзакций и анализатора
использования системы.
Последний компонент, блок записи данных об использовании, не имеет состояния, поэтому он оповещает диспетчер контрольных точек о том, что событие
обработано без объекта состояния. Диспетчер контрольных точек узнает, что
событие контрольной точки посетило все компоненты в задании, контрольная
точка завершена и ее можно зафиксировать в хранилище.
Журнал
событий

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

Диспетчер
контрольной
точки

Источник
транзакций

1

Компонент анализ
атора использовани
я
системы отправля
ет
состояния экземп
ляров
диспетчеру конт
рольных точек и доба
вляет
событие контроль
ной
точки в свой выхо
дной
поток.

е:

Блок записи
данных об
использовании

Журнал
событий

оя
ни

Анализатор
использования
системы

Диспетчер
контрольной
точки

ст

1

Состояние: 1

Со

Источник
транзакций

Анализатор
использования
системы
1

Блок записи
данных об
использовании

В целом событие контрольной точки проходит по заданию примерно так же,
как обычное событие, но отличия все же имеются. Рассмотрим происходящее
более подробно.

Глава 10. Вычисления с состоянием  261

Создание контрольных точек
с использованием событий контрольных
точек на уровне экземпляров
Событие контрольной точки переходит от компонента к компоненту. Объекты
состояния отправляются диспетчеру контрольных точек по очереди исполнителями экземпляров при получении события контрольной точки. В результате
все состояния создаются между двумя конкретными событиями (200 и 201) для
каждого компонента в приведенном примере.
Журнал
событий

ст

оя

ни

е:

1

Источник
транзакций
Со

201
Анализатор
использования
системы
200

1

Блок записи
данных об
использовании

Но ведь каждый
компонент может
существовать в нескольких
экземплярах?
Он все равно будет
работать правильно?

Диспетчер
контрольной
точки

262  Часть II. Движемся дальше

Не стоит забывать, что каждый компонент может существовать в нескольких
экземплярах. В главе 4 вы узнали, что каждое событие передается конкретному экземпляру в зависимости от стратегии группировки. Маршрутизация
событий контрольных точек осуществляется иначе; рассмотрим ее более подробно. (Некоторым читателям материал на этой и следующей странице может
показаться слишком подробным. Вы можете пропустить его и перейти к теме
загрузки контрольных точек.)
Простой ответ выглядит так: все экземпляры должны получить событие
контрольной точки, чтобы вызовы getState() были правильно инициированы.
В нашем фреймворке Streamwork диспетчер событий отвечает за синхронизацию
и диспетчеризацию события контрольной точки. Начнем с диспетчеризации (так
как эта тема проще), а синхронизацию рассмотрим ниже.
р
н экземпля
Только оди
данных.
е
ти
ы
б
о
ос
получает эт
Исполнитель
экземпляров

1
1

Исполнитель
экземпляров

1

Каждый экземпляр получает
копию этого события
контрольной точки.

Исполнитель
экземпляров

Исполнитель
экземпляров

Исполнитель
экземпляров

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

Глава 10. Вычисления с состоянием  263

Синхронизация событий контрольных точек
Механизм диспетчеризации событий контрольных точек достаточно прост, но
с синхронизацией дело обстоит сложнее. Синхронизация событий контрольных
точек — процесс получения диспетчером событий входящих событий контрольных точек. Каждый диспетчер событий получает события от нескольких
экземпляров (собственно, он также может получать события от экземпляров нескольких компонентов), то есть ожидается одно событие контрольной точки от
каждого предшествующего исполнителя экземпляра. Эти события контрольных
точек редко прибывают одновременно, как в примере на приведенной диаграмме. Что же делать в подобных случаях?
События контрольн
ых точек поступаю
т в разное
время, и их необхо
димо синхронизиро
вать.

Исполнитель
экземпляров

Исполнитель
экземпляров

1

Cобыт
ие дан
ных:
200

Исполнитель
экземпляров

Исполнитель
экземпляров

1

ых:

е данн
Cобыти
200

Исполнитель
экземпляров

Если взглянуть на приведенную выше диаграмму и принять во внимание
событийный отсчет времени, время, представляемое событием контрольной
точки 1, лежит между событиями данных 200 и 201. Событие контрольной
точки будет получено всеми исполнителями экземпляров, поэтому возможно,
что событие контрольной точки будет обработано одним экземпляром раньше
других, как на диаграмме. В этом случае после получения первого события
контрольной точки диспетчер событий блокирует поток событий, из которого поступило событие контрольной точки, до того момента, когда событие
контрольной точки будет получено из всех остальных входящих соединений.
Иначе говоря, событие контрольной точки работает как барьер (или блокировщик). В приведенном примере событие контрольной точки сначала поступает
из нижнего соединения. Диспетчер событий блокирует обработку события

264  Часть II. Движемся дальше

данных 201 и продолжает обрабатывать события (событие данных 200 и то,
которое ему предшествует) от входящего соединения, пока не будет получено
событие контрольной точки.
нных:

ие да

Cобыт
200

1

Исполнитель
экземпляров

1
Исполнитель
экземпляров

Исполнитель
экземпляров

Исполнитель
экземпляров

1
Cобытие данных:
201

Исполнитель
экземпляров

После того как событие контрольной точки 1 будет получено из обоих соединений, поскольку другие входные соединения, которые следовало бы ожидать,
отсутствуют, диспетчер событий передает событие контрольной точки всем последующим исполнителям экземпляров и начинает принимать события данных.
В результате событие данных 200 передается диспетчером событий до события
контрольной точки 1 и события данных 201.

Загрузка контрольных точек
и обратная совместимость
Итак, мы рассмотрели процесс создания контрольных точек. Теперь разберемся,
как контрольные точки загружаются и используются. В отличие от повторяющегося процесса создания, загрузка контрольных точек осуществляется только
один раз в каждом жизненном цикле стримингового задания — при запуске.
Когда стриминговое задание запускается (например, в случае сбоя — когда
в экземпляре произошел аварийный останов и задание перезапускается на тех
же машинах; экземпляры задания переместились на другие машины, как при миграции, над которой работают ЭйДжей и Сид), каждый исполнитель экземпляра
запрашивает данные состояния соответствующего экземпляра от диспетчера

Глава 10. Вычисления с состоянием  265
контрольных точек. В свою очередь, диспетчер контрольных точек обращается
к хранилищу контрольных точек, ищет последнюю контрольную точку и возвращает данные исполнителям экземпляров. Каждый исполнитель экземпляра
затем использует полученные данные состояния для настройки экземпляра.
После того как все экземпляры будут успешно созданы, стриминговое задание
начинает обрабатывать события.
loadState()

Диспетчер
контрольной
точки

Контрольная
точка

setupInstance()

Исполнитель
экземпляров

Экземпляр

Исполнитель
экземпляров

Экземпляр

Исполнитель
экземпляров

Экземпляр

Весь процесс достаточно прост, но в нем есть одна сложность — обратная совместимость. Контрольная точка была создана при предыдущем запуске задания, и данные состояния в контрольной точке используются для создания
новых экземпляров. Если задание просто перезапускается (вручную или автоматически), проблем быть не должно, так как логика экземпляров остается
прежней. Но если логика существующих компонентов с состоянием изменится,
разработчики должны позаботиться о том, чтобы новая реализация работала
с прежними контрольными точками для правильного восстановления состояний
экземпляров. Если это требование не выполняется, задание может запуститься
в некорректном состоянии или вообще перестать работать.
Некоторые стриминговые фреймворки создают особую разновидность
контрольных точек: точки сохранения. Они похожи на обычные контрольные
точки, но активируются вручную, и разработчик имеет больше возможностей
для управления ими. Это фактор, который разработчики должны учитывать
при выборе стримингового фреймворка для систем.

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

266  Часть II. Движемся дальше
Время
Диспетчер
контрольной
точки

1

2

3

4

5

6

7

чки
Контрольные то
одичери
пе
я
тс
создаю
одолпр
ски, и процесс
.
но
еч
он
жается беск

При перезапуске экземпляров тольloadState()
ко самая последняя контрольная
Исполнитель
Диспетчер
точка используется для их иниэкземпляра
контрольной
циализации. Теоретически можно
точки
хранить только одну контрольную
Исполнитель
точку для стримингового задания
экземпляра
и обновлять ее «на месте» при создании новой точки.
Исполнитель
Тем не менее на практике все
экземпляра
сложнее. Например, создание контрольной точки может завершиться
неудачей, если какие-либо экземпляры будут потеряны и контрольная точка
не будет завершена, или данные контрольной точки могут быть повреждены
из-за отказа диска и невозможности тем самым загрузить их. Для повышения
надежности стриминговых систем в хранилище обычно оставляют N последних
контрольных точек, а более старые точки удаляют (как правило, значение N
можно настраивать). Если самая последняя контрольная точка не настраивается, диспетчер контрольных точек выполняет откат к предыдущей контрольной
точке и пытается восстановить стриминговое задание. При необходимости
откат может выполняться повторно, пока контрольная точка не будет успешно
загружена.

Время
Диспетчер
контрольной
точки

1. Самая последняя контрольная точка может
использоваться для восстановления стримингового
задания при перезапуске экземпляров.

N=5
1

2

3

4

5

2. Поддерживаются послед
ние N (N = 5)
контрольных точек, более
старые точки
можно удалить.

6

7

льной
едней контро
3. Если с посл
ы,
м
нут пробле
точкой возник
рольных точек
нт
ко
р
диспетче
ей
т к предыдущ
выполнит отка
е.
чк
контрольной то

Глава 10. Вычисления с состоянием  267

Компоненты с состоянием и компоненты
без состояния
Вы достаточно узнали о компонентах с состоянием и без состояния. Пора
прерваться, взглянуть на общую картину и оценить достоинства и недостатки
компонентов с состоянием. В конце концов, у компонентов с состоянием есть
и обратная сторона. На самом деле вопрос звучит так: стоит ли использовать
компоненты с состоянием?
Окончательный ответ на него можете дать только вы — разработчик. В разных системах действуют разные требования. Хотя функциональность систем
может быть примерно одинаковой, они могут работать абсолютно по-разному,
поскольку входящий трафик событий обладает разными характеристиками:
пропускной способностью, размером данных, количеством экземпляров и т. д.
Надеемся, приведенное ниже краткое сравнение поможет вам принимать лучшие решения и строить лучшие системы. В оставшейся части этой главы мы поговорим о двух практических методах поддержки полезной функциональности
компонентов с состоянием в компонентах без состояния.
Компонент с состоянием

Компонент без состояния

Точность

Вычисления с состоянием важны
для семантики «ровно один», которая гарантирует точность (фактически)

Гарантии точности отсутствуют, потому что фреймворк
не управляет состоянием
экземпляров

Задержка
(при возникновении
ошибок)

При возникновении ошибок экземпляры выполняют откат к предыдущему состоянию

При возникновении ошибок
экземпляры продолжают работать над новыми событиями

ПотреблеУправление состоянием экземпляУправление состоянием экние ресурсов ров требует больших затрат ресурсов земпляров не требует ресурсов
Трудоемкость сопровождения

Приходится поддерживать больше
процессов (например, диспетчер
контрольных точек или хранилище
контрольных точек), обратная совместимость критична

Дополнительные затраты на
сопровождение отсутствуют

Пропускная
способность

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

Код

Необходимо реализовать управление состоянием экземпляров

Дополнительная логика отсутствует

Зависимости

Необходимо хранилище контрольных точек

Внешние зависимости отсутствуют

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

268  Часть II. Движемся дальше

Ручное управление
состоянием экземпляров
Из сравнения становится видно, что преимуществом компонентов с состоянием
является точность. Когда в системе что-то происходит и некоторые экземпляры приходится перезапускать, стриминговые ядра
помогают в управлении и выполнении
отката состояний экземпляров.
Кроме дополнительных затрат,
управляемые ядром состояния
Существуют ли другие
имеют и другие ограничения.
механизмы управления
состоянием?
Одно очевидное ограничение
заключается в том, что контрольные точки не должны создаваться слишком часто, потому что
в этом случае затраты на обслуживание повышаются и система
становится менее эффективной. Может оказаться, что для некоторых компонентов желательно использовать разные интервалы, а это невозможно с управлением на уровне ядра. Таким образом, иногда стоит рассмотреть
возможность ручного управления состоянием. Рассмотрим его на примере
задания контроля за использованием системы.

Журнал
событий

Источник
транзакций

Анализатор
использования
системы

Блок записи
данных об
использовании

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

Глава 10. Вычисления с состоянием  269
На следующей диаграмме показано задание контроля за использованием системы с подключенным хранилищем состояния. Разные экземпляры сохраняют
свои состояния в хранилище независимо. Как обсуждалось ранее, абсолютное
время не работает, потому что разные экземпляры работают с разными событиями. И поскольку мы управляем состояниями вручную, теперь у нас нет событий
контрольных точек, обеспечивающих событийный отсчет времени. Что можно
сделать для синхронизации разных экземпляров?
Ключ к решению проблемы — создание чего-то общего, что могут использовать все компоненты и экземпляры для взаимной синхронизации. Одно из
возможных решений — идентификатор транзакции. Например, экземпляры
источников транзакций хранят смещения, а экземпляры анализаторов использования системы каждую минуту сохраняют идентификаторы транзакции
и текущие счетчики в хранилище. При перезапуске задания экземпляры источников транзакций загружают смещение из хранилища и затем возвращаются
на несколько событий (или даже несколько минут) назад и перезапускаются
с этой точки. Экземпляры анализатора использования системы загружают последние идентификаторы транзакций и счетчики из хранилища. Впоследствии
экземпляры анализаторов могут пропускать входящие события, пока не обнаружат идентификаторы транзакций в состояниях, после чего можно возобновить
обычный подсчет. При таком решении источники транзакций и анализаторы
использования системы могут управлять состояниями своих экземпляров
по-разному, потому что два компонента уже не связаны идентификаторами
контрольных точек. В результате затраты ресурсов снижаются, а также обес­
печивается большая гибкость, что может быть важно в некоторых реальных
сценариях использования.

Лямбда-архитектура
Другое популярное и интересное решение называется лямбда-архитектурой.
Название кажется сложным, но не беспокойтесь — это не так.
Чтобы понять суть этого метода, вспомните главу 1, где сравнивались системы пакетной и стриминговой обработки. При стриминговой обработке системы могут генерировать результаты в реальном времени, а системы пакетной
обработки обычно обладают большей отказоустойчивостью, потому что при
возникновении проблем можно легко отбросить все временные данные и заново обработать пакет событий с самого начала. Как следствие, окончательные
результаты точны, потому что каждое событие обрабатывается ровно один раз
для получения окончательного результата. Кроме того, поскольку системы
пакетной обработки могут быть более эффективными при обработке очень
большого количества событий, в некоторых случаях можно применить более
сложные вычисления, которые трудно выполнять в реальном времени.
Идея лямбда-архитектуры весьма проста: стриминговое задание и пакетное
задание выполняются параллельно с одними и теми же данными событий. В такой архитектуре стриминговое задание отвечает за генерирование результатов

270  Часть II. Движемся дальше

в реальном времени, которые в основном точны, но при возникновении проблем
точность не гарантирована; с другой стороны, пакетное задание отвечает за генерирование точных результатов с более высокой задержкой.
Стриминговое задание отвечает за генерирование
результатов в реальном времени без гарантий точности.
Задание
стриминговой
обработки
Результат
Задание
пакетной
обработки

Пакетное задание отвечает за генерирование
точных результатов с высокой задержкой.

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

Итоги
В этой главе мы вернулись к теме состояния экземпляров и рассмотрели ее
более подробно. Затем так же подробно рассмотрели управление состояниями
экземпляров и контрольными точками в стриминговых заданиях, включая:
zzСоздание

контрольных точек с использованием событий контрольных
точек.
zzЗагрузку контрольных точек и проблему обратной совместимости.
zzХранение контрольных точек.
После краткого сравнения компонентов с состоянием и без состояния мы узнали два популярных приема, которые можно использовать, чтобы сохранить
некоторые преимущества компонентов с состоянием:
zzРучное управление состоянием экземпляров.
zzЛямбда-архитектура.

Глава 10. Вычисления с состоянием  271

Упражнения
1. Если преобразовать задание контроля за использованием системы в задание без состояния, каковы будут достоинства и недостатки такого
решения? Можно ли улучшить его, вручную управляя состоянием экземпляров? И что произойдет в случае аппаратного сбоя, когда экземпляры
будут перезапущены на других машинах?
2. Задание обнаружения мошеннических действий оптимизировано для
обработки в реальном времени из-за требований к задержке. Каковы его
достоинства и недостатки и как его можно усовершенствовать с применением лямбда-архитектуры?

11

Продвинутые концепции
в стриминговых системах

В этой главе
99Более сложные аспекты стриминговых систем.
99Что дальше?

«Важно не то, сбили ли тебя с ног, — важно то,
поднялся ли ты снова».
Винс Ломбарди

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

Глава 11. Продвинутые концепции в стриминговых системах  273

Это действительно все?
Мы, авторы, считаем, что подошла к концу книга, но не обучение и эксперименты, которые вам предстоят. И когда мы пишем эту главу, мы вспоминаем свой
долгий путь учения. Сколько интересного мы узнали на этом пути! Хочется
верить, что после прочтения этой книги вы почувствуете пользу новых знаний —
как почувствовали мы.

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

274  Часть II. Движемся дальше

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

До сих пор каждый элемент обрабатывался по отдельности.

Время

В главе 7 вы научились обрабатывать события группами,
которые определяются границами окон.
Окно 1

Окно 2

Окно 3

Время

Размер окна мо
жет определять
ся
периодом врем
ени или количе
ством
элементов — по
выбору
разработчика.

Глава 11. Продвинутые концепции в стриминговых системах  275

Основные виды окон
За создание или определение окна отвечает исключительно разработчик. Мы
представили три основных вида окон на примере задания обнаружения мошеннических действий. На диаграммах ниже используются временные окна.

Фиксированные окна
00:12

card no:

00:55

card no:

...1234

00:49

card no:

...6789

01:10

card no:

...6789

01:26

card no:

01:42

card no:

01:37
02:22

02:38

card no:
card no:

card no:

...1212

...2345

...1212

...1212

...7865

...4433

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

Первое минутное окно:
00:00-00:59
Второе минутное окно:
01:00-01:59
Третье минутное окно:
02:00-02:59

1-е окно
2-е окно

card no: ....1212

00:55

card no: ....6789
00:49
01:10

01:37

00:12

card no:

...1234

00:55

card no:

...1212

00:49

card no:

01:10

card no:

01:26

card no:

01:42

card no:

3-е окно

01:37

4-е окно

02:22

5-е окно

Сеансовые окна

Хотя все временные интервалы окон
одинаковы, разные
окна содержат
разное количество
событий.

02:38

card no:
card no:

card no:

...6789

...6789

...2345

...1212

...1212

...7865

...4433

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

01:42

15:21

15:33

Сеансовые окна определяются максимальной продолжительностью тайм-аута. Если интервал между
транзакциями превышает длину тайм-аута, то существующее окно закрывается и открывается новое окно.

276  Часть II. Движемся дальше

Соединение данных в реальном времени
В главе 8 рассматривалось соединение данных в реальном времени. В предложенном сценарии в одной географической зоне генерировались события двух
видов. Нужно было решить, как соединять события двух разных типов, поступающие с разными интервалами.
Рассматривая одну часть
города, мы видим, что на
каждом участке карты
могут происходить
разные события. Объединение разных событий
в одной зоне может
создать специфические
проблемы в системах
реального времени.

Источник событий
автомобилей получает данные от автомобилей с минутными
интервалами.

Источник
событий
автомобилей

Источник
событий
температуры

Cоединитель
событий
Соединитель событий
объединяет события двух
входящих потоков в один
исходящий поток.

Преобразователь данных
о выбросах

Оконный
агрегатор

Источник событий
температуры получает
события от датчиков
с 10-минутными
интервалами.

Глава 11. Продвинутые концепции в стриминговых системах  277

SQL и стриминговые соединения
Многим из нас известна (более или менее) конструкция join в SQL. В стриминговых системах соединение работает похожим образом, но есть и различия. В одном из типичных решений один входящий поток работает как поток, а другой
поток (или потоки) преобразуется во временную таблицу в памяти. Эта таблица
может рассматриваться как материализованное представление потока. Нужно
помнить два ключевых факта:
1. Потоковое соединение является разновидностью объединения.
2. Поток может материализоваться в таблицу непрерывно или с использованием окна.
Источник
событий
автомобилей

Источник
событий
температуры

Соединитель
событий

Источник
событий
автомобилей

Источник
событий
температуры

Cоединитель событий
{

}

make: XXX
model: AA
year: 2020,
zone: 3

2. Событие автомобиля соединяется
с таблицей температур по полю zone.

}

zone: 3
temperature: 95.2

zone

{

}

{

make: XXX,
model: AA,
year: 2020,
zone: 3,
temperature: 95.2

Преобразователь данных
о выбросах

temperature
1

95.4

2

94.3

3

95.2

4

95.2

5

95.2

3. Данные температуры добавляются
в событие машины.

1. Поступающие
события температуры
либо обновляют
существующие строки,
либо добавляются как
новые строки в таблицу
температур в реальном
времени. Иначе говоря,
эта таблица становится
непрерывно изменяющимся набором данных
в памяти. Ее содержимое постоянно обновляется потоком событий
температуры.

тся
являе о
а
ц
и
л
Таб
тольк
вует
НИЕ!
т
А
с
е
М
ВНИ ной (сущ емой.
ня
ен
врем и) и изме
т
я
в пам

278  Часть II. Движемся дальше

Внутренние и внешние соединения
Как и у конструкции join в SQL, в стриминговых системах существуют четыре
типа соединений. Выбирайте наиболее подходящий для каждого случая.

Внутренние соединения возвращают
только результаты, у которых
имеются соответствующие значения
в обеих таблицах.

События
автомобилей

Температура

Левые внешние соединения
возвращают все результаты
из таблицы событий автомобилей
и только соответствующие строки
из таблицы температур.

События
автомобилей

Температура

Полные внешние соединения
возвращают все результаты
из обеих таблиц.

События
автомобилей

Температура

Правые внешние соединения
возвращают все результаты
из таблицы температур и только
соответствующие строки из таблицы
событий автомобилей.

События
автомобилей

Температура

Глава 11. Продвинутые концепции в стриминговых системах  279

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

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

Исполнитель
экземпляра

Исполнитель
экземпляра

Исполнитель
экземпляра

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

Исполнитель
экземпляра

Проходит некоторое
время…

Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра
Исполнитель
экземпляра

Исполнитель
экземпляра

Так как последующий экземпляр запаздывает, промежуточная очередь
заполняется событиями, ожидающими обработки. Со временем, когда
очередь заполнится, система может стать нестабильной.

280  Часть II. Движемся дальше

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

Остановка источников
1. Чтобы временно остановить
отправку дополнительных
транзакций заданию источником,
можно отправить специальное
сообщение всем экземплярам
компонента-источника.

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

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного числа
транзакций

Агрегатор
оценок

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

Анализатор
оконного
расстояния

Источник
транзакций

Анализатор
среднего
чека

Анализатор
оконного
расстояния

Агрегатор
оценок

Анализатор
оконного числа
транзакций

Глава 11. Продвинутые концепции в стриминговых системах  281

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

Исполнитель
экземпляра

Если очередь за
полнена, то новы
е
события, направ
ляемые в очеред
ь,
просто отбрасыв
аются.

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

282  Часть II. Движемся дальше

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

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

Экземпляр не справляется, обратное давление возникает снова:
пробуксовка
Если в системе наблюдается пробуксовка, выясните, почему экземпляр не обрабатывает события с нужной скоростью. Как правило, у подобных проблем два
источника — трафик и компоненты. Если трафик увеличился или изменился
его паттерн, возможно, система нуждается в оптимизации или настройке. Если
экземпляр стал работать медленнее, необходимо найти первопричину. Также
необходимо учитывать зависимости, используемые компонентами. В конечном
итоге вы, владелец системы, должны понимать данные и логику системы и разобраться, из-за чего возникает обратное давление.

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

Глава 11. Продвинутые концепции в стриминговых системах  283
zzФункция getState() периодически вызывается исполнителем экземпля-

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

мпляра получает
1. Исполнитель экзе
ляра.
состояние от экземп
getState()

saveState()

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Диспетчер
контрольных
точек

Контрольная точка

zzФункция setupInstance() вызывается исполнителем экземпляра после

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

1. Исполнитель экземпляра получает
состояние от диспетчера контрольных точек, который ищет и загружает данные контрольной точки
из хранилища.

2. Исполнитель экземпляра использует
объект состояния для настройки
экземпляра.

loadState()

Диспетчер
контрольных
точек

Контрольная
точка

setupInstance()

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

Исполнитель
экземпляра

Экземпляр

284  Часть II. Движемся дальше

Событийный отсчет времени

Со
да быт
нн ие
ых

Все экземпляры в стриминговом
Журнал
задании должны зафиксировать
Со
событий
контр бытие
свое состояние одновременно,
ольно
й точ
чтобы при необходимости задание
ки
можно было восстановить в со1
стоянии на предыдущий момент
Диспетчер
Источник
времени. Однако в этом случае исконтрольных
транзакций
пользуется не абсолютное, а событочек
тийное время.
Периодически диспетчер контрольных точек в задании выдаАнализатор
ет событие контрольной точки
использования
системы
с уникальным идентификатором
всем экземплярам источника. Событие проходит через все задание
и оповещает каждый экземпляр,
Блок записи
что пришло время отправить свое
данных об
внутреннее состояние диспетчеру
использовании
контрольных точек. В отличие от
обычных событий данных, которые
передаются одному экземпляру последующего компонента, событие контрольной точки передается всем экземплярам последующего компонента.
На уровне экземпляров каждый диспетчер событий соединен с несколькими
предшествующими экземплярами и несколькими последующими. Входные события контрольных точек диспетчера событий могут прибыть не одновременно,
и их необходимо синхронизировать, прежде чем отправлять последующим
экземплярам.

Два события контрольных точек должны быть
синхронизированы перед
их отправкой последующим экземплярам.

Исполнитель
экземпляра

Исполнитель
экземпляра

Событ
ие дан
ных:
200

Исполнитель
экземпляра

1

Исполнитель
экземпляра

1

ых:

е данн
Событи
201

Исполнитель
экземпляра

Глава 11. Продвинутые концепции в стриминговых системах  285

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

Компонент без состояния

Точность

Вычисления с состоянием важны
для семантики «ровно один»,
которая гарантирует точность
(фактически)

Гарантии точности отсутствуют, потому что фреймворк не управляет состоянием экземпляров

Задержка (при
возникновении
ошибок)

При возникновении ошибок
экземпляры выполняют откат
к предыдущему состоянию

При возникновении ошибок
экземпляры продолжают
работать над новыми событиями

Потребление
ресурсов

Управление состоянием экземпляров требует больших затрат
ресурсов

Управление состоянием
экземпляров не требует
ресурсов

Трудоемкость со- Приходится сопровождать
Дополнительные затраты на
провождения
больше процессов (например,
сопровождение отсутствуют
диспетчер контрольных точек или
хранилище контрольных точек),
обратная совместимость критична
Пропускная
способность

Если управление контрольными
точками не оптимизировано,
пропускная способность может
снизиться

Высокая пропускная способность не требует дополнительных затрат

Код

Необходимо реализовать управление состоянием экземпляров

Дополнительная логика не
нужна

Зависимости

Необходимо хранилище контрольных точек

Внешние зависимости отсутствуют

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

У вас все получилось!

Вы заслужили похвалу: материал был довольно объемным. Вы преодолели
около 300 страниц описания работы стриминговых систем! Что же дальше?
Например, можно начать углублять знания и опыт по этой теме. У вас нет диплома? Не беспокойтесь, он вам не понадобится. С упорством и самоотдачей вы,
безусловно, сможете освоить стриминговые системы (и выйти на новый уровень
карьеры). Вот некоторые идеи, чем заняться дальше (конечно, не обязательно
делать это в указанном порядке).

Выберите проект с открытым исходным кодом
Попробуйте воспроизвести задачи, над которыми вы работали в этой книге,
в реальном стриминговом фреймворке с открытым исходным кодом. Узнаете
ли вы части нашего ядра Streamwork в реальных стриминговых фреймворках?
Как называются экземпляры, исполнители экземпляров и диспетчеры событий
в выбранном вами фреймворке?

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

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

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

Никогда не опускайте руки… никогда
Чтобы достичь выдающихся результатов, вам неизбежно придется терпеть неудачи — одну за другой. Примите их. Они делают вас лучше.

Ключевые концепции,
рассмотренные в книге

DAG (направленный ациклический граф)

глава 4

внешнее соединение
внутреннее соединение
водяной знак обратного давления
водяной знак окна
время обработки

глава 8
глава 8
глава 9
глава 7
глава 7

группировка полей
группировка событий

глава 3
глава 7

диспетчер контрольных точек
диспетчер событий

глава 9
глава 3

задание

глава 2

идемпотентная операция
исполнитель экземпляра
использование мощности
источник

глава 5
глава 2
глава 9
глава 2

компонент
компонент без состояния
компонент с состоянием
контрольная точка
кортеж (см. событие)

глава 2
глава 10
глава 10
глава 5

логическое планирование
лямбда-архитектура

глава 2
глава 10

288  Часть II. Движемся дальше

мощность

глава 9

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

глава 5
глава 5

обратное давление
объединение
окно
оконное соединение
оконная стратегия
оператор

глава 9
глава 4
глава 7
глава 7
глава 7
глава 2

параллелизм
параллелизм данных
параллелизм задач
позднее событие
последующий компонент
поток
предшествующий компонент
пробуксовка

глава 3
глава 3
глава 3
глава 7
глава 2
глава 2
глава 2
глава 9

разветвление
резерв мощности
ровно один, семантика доставки

глава 4
глава 9
глава 5

сеансовое окно
семантика доставки
скользящее окно
случайная группировка
событие
событие контрольной точки
событийное время
событийный отсчет времени
соединение
сообщение (см. событие)
состояние
стратегия группировки

глава 7
глава 5
глава 7
глава 3
глава 2
глава 5
глава 7
глава 9
глава 8
глава 5
глава 3

топология (см. задание)
фактически один, семантика доставки
фиксированное окно

глава 5
глава 7

экземпляр

глава 2