Upload
others
View
6
Download
0
Embed Size (px)
Citation preview
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://[email protected]: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