Реактивные микросервисы с Apache Kafka / Денис Иванов (2ГИС)

Preview:

Citation preview

Реактивные микросервисыс Apache Kafka

Денис Ивановdenis@ivanovdenis.ru@denisivanov

1

Обо мне

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

Спасибо! Вопросы?

Денис Иванов@denisivanovdenis@ivanovdenis.ruhttps://github.com/denisivan0v

90

Recommended