Реактивные микросервисы с apache kafka / Денис Иванов (2ГИС)
TRANSCRIPT
Обо мне
2
Код и презентация
https://github.com/denisivan0v/highload-2017
3
План
4
5
Терминология
6
Терминология
7
Откуда берутся микросервисы?
8
Откуда берутся микросервисы?
Advertising Management System в
9
10
Storage API
S3 Storage
High-level API
Frontend App
Откуда берутся микросервисы?
11
Storage API
S3 Storage
High-level API
Frontend App
Откуда берутся микросервисы?
Challenges
•Синхронное/асинхронное взаимодействиекомпонентов
12
Challenges
•Синхронное/асинхронное взаимодействиекомпонентов•Гарантированная доставка данных с минимальными задержками
13
Challenges
•Синхронное/асинхронное взаимодействиекомпонентов•Гарантированная доставка данных с минимальными задержками•Распределенные изменения и согласованность
14
Challenges
•Синхронное/асинхронное взаимодействиекомпонентов•Гарантированная доставка данных с минимальными задержками•Распределенные изменения и согласованность
15
Распределенные изменения
Распределенные изменения
•Блокирующие•Неблокирующие (отложенные)
17
Распределенные изменения
18
Storage API
High-level API
S3 Storage
Распределенные изменения
19
Storage API
High-level API
S3 Storage
Распределенные изменения
20
Storage API
High-level API
S3 Storage
Распределенные изменения
21
Storage API
High-level API
S3 Storage
Распределенные изменения
22
Storage API
High-level API
S3 Storage
Распределенные изменения
23
Storage API
High-level API
S3 Storage
24
/
Background workers
Frontend App
REST API
Storage
Фоновые процессы
/
25
26Kafka: The Definitive Guide by Neha Narkhede, Gwen Shapira, and Todd Palino (O’Reilly).
Распределенный лог
Kafka .NET Client
•Обертка над C-библиотекой librdkafka•Реализовано подмножество API (open source)•Использует managed и unmanaged память и потоки•xplat, .NET Core
27
Batched Kafka Consumer Wrapper
28
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
29
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
var messages = new List<Message<Null, string>>();var isEof = false;
30
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
var messages = new List<Message<Null, string>>();var isEof = false;
void OnMessage(object sender, Message<Null, string> message) => messages.Add(message);
void OnEof(object sender, TopicPartitionOffset offset) => isEof = true;
31
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
var messages = new List<Message<Null, string>>();var isEof = false;
void OnMessage(object sender, Message<Null, string> message) => messages.Add(message);
void OnEof(object sender, TopicPartitionOffset offset) => isEof = true;consumer.OnMessage += OnMessage;consumer.OnPartitionEOF += OnEof;consumer.Subscribe(topics);
32
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
var messages = new List<Message<Null, string>>();var isEof = false;
void OnMessage(object sender, Message<Null, string> message) => messages.Add(message);
void OnEof(object sender, TopicPartitionOffset offset) => isEof = true;consumer.OnMessage += OnMessage;consumer.OnPartitionEOF += OnEof;consumer.Subscribe(topics);
while (messages.Count < batchSize && !isEof &&!cancellationToken.IsCancellationRequested)
{consumer.Poll(TimeSpan.FromMilliseconds(100));
}
33
var consumer = new Consumer<Null, string>(config, new NullDeserializer(), new StringDeserializer(Encoding.UTF8));
var messages = new List<Message<Null, string>>();var isEof = false;
void OnMessage(object sender, Message<Null, string> message) => messages.Add(message);
void OnEof(object sender, TopicPartitionOffset offset) => isEof = true;consumer.OnMessage += OnMessage;consumer.OnPartitionEOF += OnEof;consumer.Subscribe(topics);
while (messages.Count < batchSize && !isEof &&!cancellationToken.IsCancellationRequested)
{consumer.Poll(TimeSpan.FromMilliseconds(100));
}consumer.Unsubscribe(topics);
34
var consumerWrapper = new ConsumerWrapper(brokerEndpoints, groupId);
35
var consumerWrapper = new ConsumerWrapper(brokerEndpoints, groupId);while (!cancellationToken.IsCancellationRequested){
var messages = consumerWrapper.Consume(topics, batchSize, cancellationToken);
}
36
var consumerWrapper = new ConsumerWrapper(brokerEndpoints, groupId);while (!cancellationToken.IsCancellationRequested){
var messages = consumerWrapper.Consume(topics, batchSize, cancellationToken);
}
37
var consumerWrapper = new ConsumerWrapper(brokerEndpoints, groupId);while (!cancellationToken.IsCancellationRequested){
var messages = consumerWrapper.Consume(topics, batchSize, cancellationToken);
foreach (var message in messages){
Console.WriteLine($"{message.Topic}/{message.Partition} @" +$"{message.Offset}: '{message.Value}'");
}}
38
var consumerWrapper = new ConsumerWrapper(brokerEndpoints, groupId);while (!cancellationToken.IsCancellationRequested){
var messages = consumerWrapper.Consume(topics, batchSize, cancellationToken);
foreach (var message in messages){
Console.WriteLine($"{message.Topic}/{message.Partition} @" + $"{message.Offset}: '{message.Value}'");
await consumerWrapper.CommitAsync(message); }
}
DEMO:Kafka + Docker + .NET Core
40
More challenges
•Легко написать неправильно
41
More challenges
•Легко написать неправильно•Частые перебалансировки consumer group
42
More challenges
•Легко написать неправильно•Частые перебалансировки consumer group•Управление распределенными consumer-ами
43
More challenges
•Легко написать неправильно•Частые перебалансировки consumer group•Управление распределенными consumer-ами•Задержки, синхронизация, утилизация ресурсов
44
45
Реактивные приложения
46
https://www.reactivemanifesto.org/http://reactivex.io/
Реактивные приложения
•Событийно-ориентированные
47
https://www.reactivemanifesto.org/http://reactivex.io/
Реактивные приложения
•Событийно-ориентированные•Масштабируемые
48
https://www.reactivemanifesto.org/http://reactivex.io/
Реактивные приложения
•Событийно-ориентированные•Масштабируемые•Отказоустойчивые
49
https://www.reactivemanifesto.org/http://reactivex.io/
Реактивные приложения
•Событийно-ориентированные•Масштабируемые•Отказоустойчивые•Отзывчивые
50
https://www.reactivemanifesto.org/http://reactivex.io/
Pull-модель
51
public interface IEnumerable{
IEnumerator GetEnumerator();}
public interface IEnumerator{
bool MoveNext();
object Current { get; }
void Reset();}
Push-модель
52
public interface IObservable<out T>{
IDisposable Subscribe(IObserver<T> observer);}
public interface IObserver<in T>{
void OnCompleted();
void OnError(Exception error);
void OnNext(T value);}
…
void OnMessage(object sender, Message<Null, string> message) => messages.Add(message);
void OnEof(object sender, TopicPartitionOffset offset) => isEof = true;
consumer.OnMessage += OnMessage;consumer.OnPartitionEOF += OnEof;
…
53
IEnumerable<string> topics;var observable =
Observable.FromEventPattern<Message<Null, string>>(x =>
{consumer.OnMessage += x;consumer.Subscribe(topics);
}, x => {
consumer.Unsubscribe();consumer.OnMessage -= x;
}).Select(x => x.EventArgs);
54
IEnumerable<string> topics;var observable =
Observable.FromEventPattern<Message<Null, string>>(x =>
{consumer.OnMessage += x;consumer.Subscribe(topics);
}, x => {
consumer.Unsubscribe();consumer.OnMessage -= x;
}).Select(x => x.EventArgs);
55
IEnumerable<string> topics;var observable =
Observable.FromEventPattern<Message<Null, string>>(x =>
{consumer.OnMessage += x;consumer.Subscribe(topics);
}, x => {
consumer.Unsubscribe();consumer.OnMessage -= x;
}).Select(x => x.EventArgs);
56
Rx Kafka Consumer Wrapper
57
var observable = consumerWrapper.Consume(token);
58
var observable = consumerWrapper.Consume(token);
59
var observable = consumerWrapper.Consume(token);
var subscription = observable.Buffer(batchSize).Subscribe(
messages =>{
foreach (var message in messages){
Console.WriteLine(message.Value);consumerWrapper.CommitAsync(message)
.GetAwaiter().GetResult();}
});
60
var observable = consumerWrapper.Consume(token);
var subscription = observable.Buffer(batchSize).Subscribe(
messages =>{
foreach (var message in messages){
Console.WriteLine(message.Value);consumerWrapper.CommitAsync(message)
.GetAwaiter().GetResult();}
});
61
var observable = consumerWrapper.Consume(token);
var subscription = observable.Buffer(batchSize).Subscribe(
messages =>{
foreach (var message in messages){
Console.WriteLine(message.Value);consumerWrapper.CommitAsync(message)
.GetAwaiter().GetResult();}
});
62
DEMO: Kafka + Rx
63
64
Распределенные изменения
65
Распределенные изменения
66
Storage API
High-level API
S3 Storage
Распределенные изменения
67
Storage API
High-level API
S3 Storage
Распределенные изменения
68
Storage API
High-level API
S3 Storage
Распределенные изменения
69
Storage API
High-level API
S3 Storage
Распределенные изменения
70
Storage API
High-level API
S3 Storage
Распределенные измененияНеизменяемость (immutability)
71
Распределенные измененияНеизменяемость (immutability)
•Версионирование данных
72
Распределенные измененияНеизменяемость (immutability)
•Версионирование данных•Данные + события
73
Распределенные измененияНеизменяемость (immutability)
•Версионирование данных•Данные + события•Данные = события (event sourcing)
74
75
Storage API
High-level API
S3 Storage
76
Storage API
High-level API
S3 Storage
77
Storage API
High-level API
S3 Storage
Неблокирующие изменения
78
Неблокирующие изменения
79
Storage API
High-level API
S3 Storage
Неблокирующие изменения
80
Storage API
High-level API
StorageQueue
Неблокирующие изменения
81
Worker
StorageQueue
/
Неблокирующие изменения
82
StorageQueue
Worker /
Неблокирующие изменения
83
StorageQueue
Worker /
Краткие итоги
84
Краткие итоги•Микросервисы не появляются сами по себе
85
Краткие итоги•Микросервисы не появляются сами по себе•Управление изменениями в приложении значительно усложняется
86
Краткие итоги•Микросервисы не появляются сами по себе•Управление изменениями в приложении значительно усложняется•Apache Kafka упрощает асинхронные взаимодействия
87
Краткие итоги•Микросервисы не появляются сами по себе•Управление изменениями в приложении значительно усложняется•Apache Kafka упрощает асинхронные взаимодействия•Реактивный подход убирает границы между компонентами приложения
88
Полезные ссылки
https://github.com/denisivan0v/highload-2017https://github.com/2gis/nuclear-vstore
89