1 Akka - JUG Ru Grouppublic.jugru.org/dotnext/2015/piter/tsukanov.pdfAkka.Routing По...

Post on 14-Sep-2020

5 views 0 download

Transcript of 1 Akka - JUG Ru Grouppublic.jugru.org/dotnext/2015/piter/tsukanov.pdfAkka.Routing По...

1

Akka.NET

2

1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 20160

500

1000

1500

2000

2500

3000

3500

4000

0

10

20

30

40

50

60

70

1 1 1 1 1 1 1 1 1 2 24 4

8 8

16 16

32 32

64

200300

400500

1000

1800

2530

3200

3600

2200

29303000

32003330 3330

3150 3200 3150 3150 3150

Тактовая частота и количество ядер по годам

Частота (МГц) Ядра

3

4

Многопоточность

Ожидания

Реальность

5

6

Вертикальное масштабирование

7

Вертикальное масштабирование

Горизонтальное масштабирование

8

Вертикальное масштабирование

Горизонтальное масштабирование Эластичность

9

Вертикальное масштабирование

Горизонтальное масштабирование Эластичность

Parallel LinqTPL – async/await

Потоки

10

Вертикальное масштабирование

Горизонтальное масштабирование Эластичность

Parallel LinqTPL – async/await

Потоки

WCFWeb API

ServiceStackMSMQ

11

Вертикальное масштабирование

Горизонтальное масштабирование Эластичность

Parallel LinqTPL – async/await

Потоки

WCFWeb API

ServiceStackMSMQ

12

Что такое Актор?

13

Это — единица организации программного кода

Что такое Актор?

14

Это — единица организации программного кода

Что такое Актор?

ООП

ПоведениеСостояние

Синхронные* вызовы

Акторы

ПоведениеСостояние

Асинхронные сообщения

15

Это — единица организации программного кода

Что такое Актор?

ООП

ПоведениеСостояние

Синхронные* вызовы

Акторы

ПоведениеСостояние

Асинхронные сообщения

Не нужно думать о:

- разделяемом состоянии- видимости состояния

- потоках, блокировках, конкурентных коллекциях, и т. п.

16

Каковы накладные расходы

● 2.5 миллиона акторов на гигабайт памяти● 50 миллионов сообщений в секунду на локальной машине● Дешевле, чем потоки

17

Th

read

Pool Actor

1Actor

1Actor

1Actor

1Actor

2Actor

2

Actor2Actor2 Actor3

Actor3

Actor4

Actor4 Actor4Actor4

Actor1Actor1

Actor3Actor3 Actor4Actor4

Actor2Actor2

Actor1Actor1

Actor3Actor3

Actor2Actor2

Time

● Акторы работают в пуле потоков, нет расходов на переключения контекста.

● Есть возможность управлять тем, где и как будет выполняться актор.2.5 миллиона акторов на гигабайт памяти

18

Актор можно использовать как...

● поток● экземпляр объекта/компонента● колбек/подписчик● синглетон или сервис (например, слой работы с бд)● маршрутизатор, балансировщик, пул● сервис вне текущего процесса● конечный автомат

19

Модель акторов используют:

● Erlang● Facebook WhatsApp (Erlang)● RabbitMQ (Erlang)● CouchDB (Erlang)● LinkedIn.com (JVM Akka)● Walmart.com (JVM Akka)● Blizzard (JVM Akka)

20

Анатомия актора

ActorRefActorRefActorRefActorRef

АкторАктор

СостояниеСостояние

НадзорНадзор

Дочерние акторы

Дочерние акторы

Почтовый ящик

ПоведениеПоведение

Транспорт

21

Действия с акторами

1) Define

2) Create

3) Send

4) Become

5) Supervise

22

public class GreetingActor : ReceiveActor

{

public class Greet

{

public string Who { get; private set; }

public Greet(string who)

{

Who = who;

}

}

public GreetingActor()

{

Receive<Greet>(greet => Console.WriteLine(greet.Who));

}

}

23

var system = ActorSystem.Create("my-system");var actorRef = system.ActorOf<GreetingActor>("my-actor");actorRef.Tell(new GreetingActor.Greet("World"));

24

var system = ActorSystem.Create("my-system");var actorRef = system.ActorOf(Props.Create<GreetingActor>() .WithRouter(new RoundRobinPool(10)), "my-actor");actorRef.Tell(new GreetingActor.Greet("World"));

25

var system = ActorSystem.Create("my-system");var actorRef = system.ActorOf(Props.Create<GreetingActor>() .WithDeploy(new Deploy(new RemoteScope( Address.Parse("akka.tcp://my-system@192.168.1.15:6001")))), "my-actor");

actorRef.Tell(new GreetingActor.Greet("World"));

26

user

/

sys

27

a1

user

/

sys

28a2 a3

a1

user

/

sys

29

a4 a5

a2 a3

a1

user

/

sys

30

a4 a5

a2 a3

a1

user

/

sys

31

a4 a5

a2 a3

a1

user

/

sys

32

a4 a5

a2 a3

a1

user

/

sys

/user/a1/a2/a5

33

Обработка исключений в C, C#, Java...

34

35

36

Error Kernel

37

/

38

/

39

/

40

/

41

/

42

/

43

/

44

/

45

/

46

/

47

/

48

/

49

/

50

/

51

Другой компьютер/процесс

52

protected override SupervisorStrategy SupervisorStrategy()

{

return new OneForOneStrategy(5, new TimeSpan(0, 1, 0), e =>

e is ArithmeticException ? Directive.Resume

: e is IOException ? Directive.Restart

: Directive.Escalate);

}

53

class CallCentreOperator{ public enum States { Offline, Idle, InCall, Busy } private States _state; public void DoSomethingUseful() { if(_state==States.Offline) ... else if(_state == States.Idle) ... ... }}

54

public class CallCentreOperator : ReceiveActor{ private readonly ActorSelection _callInitiator; private readonly ActorSelection _callRouter; private string OperatorContactNumber => string.Format("client:{0}", Self.Path.Name);

public CallCentreOperator() { _callInitiator = Context.ActorSelection("/user/twiliocallinitiator"); _callRouter = Context.ActorSelection("/user/callrouter"); Become(Offline); }

public void Offline() { Receive<AgentConnected>(m => { Become(Idle); }); }...

}

55

public class CallCentreOperator : ReceiveActor{ private readonly ActorSelection _callInitiator; private readonly ActorSelection _callRouter; private string OperatorContactNumber => string.Format("client:{0}", Self.Path.Name);

public CallCentreOperator() { _callInitiator = Context.ActorSelection("/user/twiliocallinitiator"); _callRouter = Context.ActorSelection("/user/callrouter"); Become(Offline); }

public void Offline() { Receive<AgentConnected>(m => { Become(Idle); }); }...

}

56

public class CallCentreOperator : ReceiveActor{ private readonly ActorSelection _callInitiator; private readonly ActorSelection _callRouter; private string OperatorContactNumber => string.Format("client:{0}", Self.Path.Name);

public CallCentreOperator() { _callInitiator = Context.ActorSelection("/user/twiliocallinitiator"); _callRouter = Context.ActorSelection("/user/callrouter"); Become(Offline); }

public void Offline() { Receive<AgentConnected>(m => { Become(Idle); }); }...

}

57

public void Idle() { Receive<AgentDisconnected>(m => { Become(Offline); }); Receive<IncomingCallMessage>(m => { string url = String.Format("/twilio/client_connected?conference={0}", m.ConferenceId); _callInitiator.Tell(new CreateCall(OperatorContactNumber, url)); _callRouter.Tell(new OperatorUnavailable()); Become(InCall); }); } public void InCall() { Receive<AgentDisconnected>(m => { Become(Offline); }); Receive<CallCompleteMessage>(m => { Become(Wrapup); Context.System.Scheduler.ScheduleTellOnce(TimeSpan.FromSeconds(10.0), Self, new OperatorReadyUp(), Sender); }); }

58

Akka.Routing

По контролю времени жизни:- Pool- Group

По логике маршрутизации● RandomRouter● SmallestMailboxRouter● BroadcastRouter● RoundRobinRouter● ConsistentHashRouter● ScatterGatherFirstCompletedRouter● TailChoppingRouter

59

RoundRobinRouter

12

1

2

3

34

4

Router

Routee1

Routee2

Routee3

60

BroadcastRouter

12

12

12

12

Router Routee2

Routee3

Routee1Шлёт всемШлёт всем

61

ConsistentHashRouter

ZY

Z

Y

X

AX

A

Router

Routee1

Routee2

Routee3

Одинаковый «индекс хэша»Одинаковый «индекс хэша»

«индекс хэша» привязан к актору«индекс хэша» привязан к актору

62

Routee1

Routee2

Routee3

?

?

?

?

Sender !

Шлём всем, получаем первый ответ, отправляем запрашивающему(Task.WaitAny)

Шлём всем, получаем первый ответ, отправляем запрашивающему(Task.WaitAny)

ScatterGatherFirstCompletedRouter

Router

63

Routee1

Routee2

Routee3

Sender

TailChoppingRouter

Router

64

Routee1

Routee2

Routee3

?Sender

TailChoppingRouter

Router

65

Routee1

Routee2

Routee3

?

?

Sender

TailChoppingRouter

Router

66

Routee1

Routee2

Routee3

?

?

Sender

TailChoppingRouter

Router

Routee1

67

Routee1

Routee2

Routee3

?

?

Sender

TailChoppingRouter

Router ?

Routee1

68

Routee1

Routee2

Routee3

?

?

Sender

TailChoppingRouter

Router ?

Routee1

Routee2

69

Routee1

Routee2

Routee3

?

?

Sender

TailChoppingRouter

Router ?

Routee1

Routee2 !

70

Routee1

Routee2

Routee3

?

?

Sender !

TailChoppingRouter

Router ?

Routee1

Routee2 !

71

Routee1

Routee2

Routee3

?

?

Sender !

Шлёт случайному, если не получает ответа, шлёт следующему

Шлёт случайному, если не получает ответа, шлёт следующему

TailChoppingRouter

Router ?

Routee1

Routee2 !

72

Akka.Cluster

Seed Node

73

Akka.Cluster

Node

Seed Node

74

Akka.Cluster

Node

Seed Node

75

Akka.Cluster

Node

Seed Node

Node

76

Akka.Cluster

Node

Seed Node

Node

77

Akka.Cluster

Node

Seed Node

Node

78

Akka.Cluster

Node

Seed Node

Node

Node

79

Akka.Cluster

Node

Seed Node

Node

Node

80

Akka.Cluster

Node

Seed Node

Node

Node

81

Akka.Cluster

Node

Seed Node

Node

Node

82

Akka.Cluster

Node

Seed Node

Node

Node

83

Akka.Cluster

Node

Seed Node

Node

Node

Node

84

Akka.Persistence

85

События

Запросы

Команды

CQRS(Command/Query Request Separation)

Потребитель(UI, слой API, etc)

Доменная модельМодель для чтения

База для чтенияБаза для записи

и работы доменной модели

86

public class Bus{

public List<Passenger> Passengers { get; set; }public Bus(){

Passengers = new List<Passenger>();}public void Update(PassengerAddedEvent ev){

Passengers.Add(ev.Passenger);}

}

87

public class PassengerAddedEvent{

public PassengerAddedEvent(Passenger passenger){

Passenger = passenger;}public Passenger Passenger { get; private set; }

}

88

public class AddPassengerCommand{

public AddPassengerCommand(Passenger passenger){

Passenger = passenger;}public Passenger Passenger { get; private set; }

}

public class AddPassengerCommandResult{

public bool Added { get; set; }public AddPassengerCommandResult(bool added){

Added = added;}

}

89

public class BusActor : PersistentActor

{

Bus _state = new Bus();

protected override bool ReceiveRecover(object message) { ... }

protected override bool ReceiveCommand(object message) { ... }

public override string PersistenceId { get };

public BusActor(int id)

{

PersistenceId = id;

}

public static Props Props(int id) => Props.Create<BusActor>(id);

}

90

protected override bool ReceiveCommand(object message){

var addPassenger = message as AddPassengerCommand;if (addPassenger != null){

if (_state.Passengers.Count > 10 || addPassenger.Passenger.Drunk)Sender.Tell(new AddPassengerCommandResult(false));

else{

Persist(new PassengerAddedEvent(addPassenger.Passenger), e =>{

_state.Update(e));Sender.Tell(new AddPassengerCommandResult(true));

}}

}else if (message as string == "print")

Console.WriteLine(string.Join(", ", _state.Passengers.Select(p => p.Name)));else if (message as string == "snap")

SaveSnapshot(_state);else return false;return true;

}

91

protected override bool ReceiveCommand(object message){

var addPassenger = message as AddPassengerCommand;if (addPassenger != null){

if (_state.Passengers.Count > 10 || addPassenger.Passenger.Drunk)Sender.Tell(new AddPassengerCommandResult(false));

else{

Persist(new PassengerAddedEvent(addPassenger.Passenger), e =>{

_state.Update(e));Sender.Tell(new AddPassengerCommandResult(true));

}}

}else if (message as string == "print")

Console.WriteLine(string.Join(", ", _state.Passengers.Select(p => p.Name)));else if (message as string == "snap")

SaveSnapshot(_state);else return false;return true;

}

92

protected override bool ReceiveCommand(object message){

var addPassenger = message as AddPassengerCommand;if (addPassenger != null){

if (_state.Passengers.Count > 10 || addPassenger.Passenger.Drunk)Sender.Tell(new AddPassengerCommandResult(false));

else{

Persist(new PassengerAddedEvent(addPassenger.Passenger), e =>{

_state.Update(e));Sender.Tell(new AddPassengerCommandResult(true));

}}

}else if (message as string == "print")

Console.WriteLine(string.Join(", ", _state.Passengers.Select(p => p.Name)));else if (message as string == "snap")

SaveSnapshot(_state);else return false;return true;

}

93

protected override bool ReceiveCommand(object message){

var addPassenger = message as AddPassengerCommand;if (addPassenger != null){

if (_state.Passengers.Count > 10 || addPassenger.Passenger.Drunk)Sender.Tell(new AddPassengerCommandResult(false));

else{

Persist(new PassengerAddedEvent(addPassenger.Passenger), e =>{

_state.Update(e));Sender.Tell(new AddPassengerCommandResult(true));

}}

}else if (message as string == "print")

Console.WriteLine(string.Join(", ", _state.Passengers.Select(p => p.Name)));else if (message as string == "snap")

SaveSnapshot(_state);else return false;return true;

}

94

protected override bool ReceiveRecover(object message){

Bus offeredState;

if (message is SnapshotOffer && (offeredState = ((SnapshotOffer)message).Snapshot as Bus) != null)

_state = offeredState;

else if (message is PassengerAddedEvent)

_state.Update(message as PassengerAddedEvent);

else return false;

return true;

}

95

protected override bool ReceiveRecover(object message){

Bus offeredState;

if (message is SnapshotOffer && (offeredState = ((SnapshotOffer)message).Snapshot as Bus) != null)

_state = offeredState;

else if (message is PassengerAddedEvent)

_state.Update(message as PassengerAddedEvent);

else return false;

return true;

}

96

protected override bool ReceiveRecover(object message){

Bus offeredState;

if (message is SnapshotOffer && (offeredState = ((SnapshotOffer)message).Snapshot as Bus) != null)

_state = offeredState;

else if (message is PassengerAddedEvent)

_state.Update(message as PassengerAddedEvent);

else return false;

return true;

}

97

public class BusViewActor : PersistentView

{

private readonly string _name;

private int _cnt;

public BusViewActor(string persistenceId, string name)

{

PersistenceId = persistenceId;

_name = name;

}

...

}

98

protected override bool Receive(object message){ var passengerAdded = message as PassengerAddedEvent; var query = message as GetCountQuery; if (passengerAdded != null) { if (passengerAdded.Passenger.Name == _name) _cnt++; } else if (query != null) Sender.Tell(new GetCountQueryResult(_cnt)); ...}

99

protected override bool Receive(object message){ var passengerAdded = message as PassengerAddedEvent; var query = message as GetCountQuery; if (passengerAdded != null) { if (passengerAdded.Passenger.Name == _name) _cnt++; } else if (query != null) Sender.Tell(new GetCountQueryResult(_cnt)); ...}

100

protected override bool Receive(object message){ var passengerAdded = message as PassengerAddedEvent; var query = message as GetCountQuery; if (passengerAdded != null) { if (passengerAdded.Passenger.Name == _name) _cnt++; } else if (query != null) Sender.Tell(new GetCountQueryResult(_cnt)); ...}

101

Cluster Event Sourcing Demo

https://github.com/kekekeks/dotnext-akka-demo/

102

Akka.NET

http://getakka.net/

https://github.com/akkadotnet/akka.net/

https://gitter.im/akkadotnet/akka.net