-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
693 lines (504 loc) · 20.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>reveal.js - The HTML Presentation Framework</title>
<meta name="description" content="A framework for easily creating beautiful presentations using HTML">
<meta name="author" content="Hakim El Hattab">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">
<link rel="stylesheet" href="css/reveal.css">
<link rel="stylesheet" href="css/theme/moon.css" id="theme">
<!-- Code syntax highlighting -->
<link rel="stylesheet" href="lib/css/monokai_sublime.css">
<style type="text/css">
.reveal section img { border: 0px; }
</style>
<!-- Printing and PDF exports -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
<!--[if lt IE 9]>
<script src="lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section data-markdown>
# ScalaLab
## Akka hacking Session
</section>
<section data-background="#360515">
<section data-markdown>
## Some words on Akka
</section>
<section data-markdown>
###Actors
- **Akka** is a toolkit that promises scalability via _Actors_
+ each actor can have internal state<br />
which can be changed only via messages<br/>
which are executed in order
+ supervision strategies, event buses, remote actors, cluster...
- Vertical and horizontal scalability using single paradigm
- Cons:
+ paradigm switch, takes time to learn
+ a general concurrency pattern, but doesn't fit **every** usage case
</section>
<section data-markdown>
###Actors - Documentation
- The official Akka site: [akka.io](http://akka.io/)
- The reference manual: [html](http://doc.akka.io/docs/akka/2.3.11/scala.html), [PDF](http://doc.akka.io/docs/akka/2.3.11/AkkaScala.pdf?_ga=1.72362139.9282561.1410281289)
+ If you never read the *Introduction* and *General* chapters of this manual, please do so in preparation for the session
- Akka Scaladoc for Akka 2.3.11: [Akka 2.3.11 API](http://doc.akka.io/api/akka/2.3.11/#package)
</section>
</section>
<section data-markdown>
## Let's hack!
</section>
<section>
<section data-markdown>
###Akka-Network-Ping Base Actor Hierarchy
![Actor Hierarchy](images/01-Base-Actor-Hierarchy.png)
</section>
<section data-markdown>
###Akka-Network-Ping External communication path
![External Communication Path](images/02-External-communication-path.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
</section>
<section data-background="#0C5E75">
<section data-markdown>
###Exercise 1 - Basic, pinging a network
![External Communication Path](images/03-Exercise-1.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
### Creating a top-level Actor
<pre><code class="scala">
val networkPingCoordinator =
system.actorOf(
Props(new PingResponseCoordinator),
"networkPingCoordinator"
)
</code></pre>
A good practice is to defined it on actor's companion object
<pre><code class="scala">
object PingResponseCoordinator {
def props(): Props = Props(new PingResponseCoordinator)
}
val networkPingCoordinator =
system.actorOf(
PingResponseCoordinator.props(),
"networkPingCoordinator"
)
</code></pre>
</section>
<section data-markdown>
### Creating a child Actor
We create a child actor by using an actor's context instead.
The ***PingServer*** and ***PingMaster*** actors are children of ***PingResponseCoordinator***
<pre><code class="scala">
class PingResponseCoordinator extends Actor with ActorLogging {
val pingServer =
context.actorOf(PingServer.props(), "pingServer")
val pingMaster =
context.actorOf(PingMaster.props(pingServer), "pingMaster")
}
</code></pre>
</section>
<section data-markdown>
### Sending a message
A message is sent using the ! operator,
better known as the `tell` operator.
<pre><code class="scala">
// NetworkPingApp.createPinger method
import PingResponseCoordinator._
def createPinger(pingerCount:Int, pingCount:Int, pingInterval:Int) = {
for ( _ <- 1 to pingerCount)
networkPingCoordinator ! CreatePinger(pingCount, pingInterval)
}
</code></pre>
Basically, we tell the ping coordinator to create Pinger actors and we don't wait for a response (fire-and-forget).
</section>
<section data-markdown>
### Sending to `self`
Each actor has a reference to himself that can be accessed via the `self` field*.
<pre><code class="scala">
/** The 'self' field holds the ActorRef for this actor. */
implicit final val self: ActorRef
def receive = {
// send to actor's own mailbox for later processing
case "Hello" => self ! "Hi!"
}
</code></pre>
\* Note that this is different then 'this', 'this' refers to the object instance while `self` points to the ***ActorRef*** of the current Actor.
</section>
<section data-markdown>
### Sending a message back to `sender`
The `sender()` method gives you the ActorRef of the Actor that sent the current message.
<pre><code class="scala">
/** The reference sender Actor of the last received message. */
final def sender(): ActorRef
def receive = {
// send a message back to the sender of 'Ola' message
case "Ola" => sender() ! "Hello!"
}
</code></pre>
Think about the consequences of the following fragment.
<pre><code class="scala">
// this will run on a separated thread
def doSomething = Future("Hello!")
def receive = {
case "Ola" => doSomething.map { greetings => sender() ! greetings }
}
</code></pre>
</section>
<section data-markdown>
### Receiving a message
The actor's receive message is the where messages are handled.
The impl must define which messages the actor is supposed to receive and act upon.
<pre><code class="scala">
import PingResponseCoordinator._
class PingMaster(pingServer: ActorRef) extends Actor {
override def receive = {
case CreatePinger(pingCount, pingInterval) =>
context.actorOf(Pinger.props(pingServer, pingCount, pingInterval))
}
}
</code></pre>
***PingMaster*** receives a ***CreatePinger*** message and create Pinger actors.
</section>
</section>
<section data-background="#360515">
<section data-markdown>
###Exercise 2 - Ping at regular time interval
![Ping at regular time interval](images/04-Exercise-2.png) <!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
### Scheduling a message
It's possible to add some delay when sending a message to an actor.
<pre><code class="scala">
import scala.concurrent.duration._
context.system.scheduler.scheduleOnce(
2 seconds, // delaying 2 seconds
actorRef, // the actorRef to send the message
Message("hey") // the message
)
</code></pre>
</section>
<section data-markdown>
### Where does an actor live?
An actor is created and lives inside the ***Actor System***. We can't access it directly. We have an ***ActorRef*** instead.
If you don't stop an actor, it'll live as long as your ***Actor System*** is running.
<pre><code class="scala">
// stopping a child
context.stop(childActorRef)
// stopping ifself
context.stop(self)
// usually for top-level actors
actorSystem.stop(myTopLevelActorRef)
</code></pre>
Stopping an actor is done asynchronously. All its children will be stopped as well.
</section>
</section>
<section>
<section data-markdown>
###Exercise 3 - Response delay
![Response delay](images/05-Exercise-3.png) <!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
###Simulating latency
We want to pretend that PingServer is too
busy and can't respond immediatly.
For that purpose we'll switch between
`receive` implementations.
</section>
<section data-markdown>
### Becoming something different
The `context.become` method allows
us to change an actor's behavior.
<pre><code class="scala">
class MoodyActor extends Actor {
def goodMood = {
case Rain => context.become(badMood)
case _ => sender() ! "I'm doing fine!"
}
def badMood = {
case Sun => context.become(goodMood)
case _ => sender() ! "I'm not in a good mood!"
}
def receive = goodMood
}
</code></pre>
</section>
<section data-markdown>
### Delaying message processing
We can put messages aside using the Stash trait.
<pre><code class="scala">
class MoodyActor extends Actor with Stash {
def goodMood = {
case Rain => context.become(badMood)
case _ => sender() ! "I'm doing fine!"
}
def badMood = {
// sun is back, ready to talk again
case Sun =>
unstashAll() // bring messages back
context.become(goodMood)
// put messages aside while in bad mood
case _ => stash()
}
def receive = goodMood
}
</code></pre>
</section>
</section>
<section>
<section data-markdown>
###Exercise 4 - Scaling your Actors
![Response delay](images/05-Exercise-3.png) <!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
###Scaling Actors with Akka Routing
- In exercise 3, we introduced an artificial bottleneck
- The effect is that our *PingServer* doesn't scale:
it limits the throughput of the application
- We now scale-up the *PingServer* by turning it onto a router
</section>
<section data-markdown>
### Akka Routing
- We implement a Actor as usual, but scale-up by using either a *group-router*
or a *pooled-router* having a number of *Routees* underneath
+ Group-router: Explicitly create a set of Actors, then create a group router that has
these Actors underneath.
+ Pooled-router: Let the system create the *Routees* for you
</section>
<section data-markdown>
### Akka Routing Strategies
- From a programming point of view, we send messages to a single entry point, which is the router
- A routing strategy is chosen from one of the following:
+ Round-Robin
+ Random
+ Smallest-mailbox
+ Broadcast
+ Scatter-gather First-completed
+ Tail-chopping
+ Consistent Hashing
</section>
<section data-markdown>
### Akka Routing - Implementation aspects
- Implement Akka Router either
+ Programmatically
+ Through configuration
- We will change our *PingServer* into a *pooled-router* using a *round-robin* routing strategy and do this purely via configuration
</section>
<section data-markdown>
### Pooled-Router via configuration (1)
- Suppose that our base Actor lives at ***/somePoint/OtherPoint/ActorToBeScaled***
- We add the router's configuration info in ***resources/application.conf***
<pre><code class="scala">
akka {
actor {
deployment {
/somePoint/OtherPoint/ActorToBeScaled {
router = round-robin-pool
nr-of-instances = 100
}
}
}
}
</code></pre>
</section>
<section data-markdown>
### Pooled-Router via configuration (2)
If our non-router Actor was created as follows:
<pre><code class="scala">
context.actorOf(
ActorToBeScaled.props(...), "ActorToBeScaled"
)
</code></pre>
We utilize *FromConfig.props* to instruct the system to get the router set-up from configuration
<pre><code class="scala">
context.actorOf(
FromConfig.props(
ActorToBeScaled.props(...)
), "ActorNowScaled"
)
</code></pre>
</section>
<section data-markdown>
###Actor Hierarchy with ***PingServer*** as a Router
![Actor Hierarchy Pooled-Router](images/10-Actor-Hierarchy-PooledRouter.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
</section>
<section>
<section data-markdown>
###Exercise 5 - Introducing 'unreliable' Actors
![Unreliable PingServer Actor](images/15-Exercise-5.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
### Message Delivery in Akka - Reliability
- Different message delivery guarantees are possible:
+ At most once
+ At least once
+ Exactly once
- Akka implements *At most once*
+ Implications when working with *Akka remoting* (or *Akka Clustering*)
+ Messages may get *lost*
+ Application *must* deal with this
</section>
</section>
<section>
<section data-markdown>
###Exercise 6 - Dealing with Failure/Delegating work to one-off Actors
![Delegating work to one-off Actors](images/20-Actor-Hierarchy-One-off-Actors.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
### Dealing with Failure (1)
- Failure is a fact of life
+ Deal with failure in the right place
+ It should never be the responsiblity of the client to recover from failure
- What is the definition of an Actor failure in Akka ?
+ An exception is thrown during:
- the processing of a message (behaviour)
- Actor initialisation
- execution of one of the so-called life-cycle hooks
</section>
<section data-markdown>
### Dealing with Failure (2)
- What does Akka do in case of an Actor failure ?
+ The exception that caused the failure doesn't crash the system
+ Determined by a *Directive* in the Actor's *Supervision* strategy:
- Resume
- Restart
- Stop
- Escalate
+ There's a default behavior - Let's find out what it is in this exercise
</section>
</section>
<section>
<section data-markdown>
###Exercise 7 - Tuning an actor's supervision strategy
![Tuning Actor Supervisory strategy](images/25-Exercise-6-7.png)
<!-- .element: style="background:none; border:2px solid #FDF6E4; box-shadow:none; background-color:#FDF6E4;" -->
</section>
<section data-markdown>
### Actor Supervision strategy
- Akka comes with two configurable supervision strategies
+ One-For-One-Strategy
- Strategy is applied to failing Actor only
+ All-For-One-Strategy
- Strategy is applied to *ALL* supervised children
</section>
<section data-markdown>
### Actor Supervision strategy - implementation
- Akka's default Supervision strategy can be overriden by overriding a ```supervisionStrategy``` variable
<pre><code class="scala">
override val supervisorStrategy: SupervisorStrategy = {
val myDecider: Decider = {
case TheExceptionThatWasThrown =>
DoSomethingSmart()
SupervisorStrategy.Restart
}
OneForOneStrategy()(
decider =
myDecider orElse
super.supervisorStrategy.decider
)
}
</code></pre>
</section>
</section>
<section>
<section data-markdown>
###Exercise 8 - The *Ask* pattern
- Sometimes you want to interact with Actors from a non-Actor based application
+ Sending a message is not a problem as long as you have an ActorRef ...
+ ... but how to you the message from the reponse in your application ?
</section>
<section data-markdown>
### Applying the *Ask* pattern
- Instead of using the *tell* (*!*) operator, use the ask operator: *?*
+ an implicit *ExecutionContext* needs to be in scope
+ an implicit *Timeout* needs to be defined
+ *akka.pattern.ask* should be imported
- ask return a *Future* that is completed either with
+ a *Success* holding the reponse
or
+ a *Failure* holding an *AskTimeoutException* in case no response is received within the given *Timeout*
</section>
<section data-markdown>
### *Ask* coding example
Suppose we send *someActor* a *SomeQuestion* message and the response is a *SomeAnswer* message
<pre><code class="scala">
import myActorSystem.dispatcher
import akka.pattern.ask
implicit val myTimeout: Timeout = Timeout(1 second)
val reponse: Future[Any] = someActor ? SomeQuestion
response.mapTo[SomeAnswer] onComplete {
case Success(someAnswer) =>
doSomethingFancyWithAnswer(someAnswer)
case Failure(failure) =>
dealWithFailure(failure)
}
</code></pre>
</section>
<section data-markdown>
###The *Ask* pattern
- Note the use of *mapTo*: this is needed because of *Type erasure* in the JVM
- The *ask* pattern can also be used in the communication between two Actors
+ in that case, don't install a handler to get the reponse
+ instead, use *pipeTo* (*import akka.pattern.pipe*)
</section>
<section data-markdown>
### *Ask/pipeTo* coding example
Suppose we send *someActor* a *SomeQuestion* message and the response is a *SomeAnswer* message
<pre><code class="scala">
import myActorSystem.dispatcher
import akka.pattern.{ask, pipe}
implicit val myTimeout: Timeout = Timeout(1 second)
someActor ? SomeQuestion pipeTo self
override def receive: Receive = {
case answer: SomeAnswer =>
doSomethingFancyWithAnswer(answer)
case Status.Failure(failure) =>
dealWithFailure(failure)
}
</code></pre>
</section>
</div>
</div>
<script src="lib/js/head.min.js"></script>
<script src="js/reveal.js"></script>
<script>
// Full list of configuration options available at:
// https://github.com/hakimel/reveal.js#configuration
Reveal.initialize({
controls: true,
progress: true,
history: true,
center: true,
transition: 'slide', // none/fade/slide/convex/concave/zoom
// Optional reveal.js plugins
dependencies: [
{ src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/highlight/highlight.js', async: true, condition: function() { return !!document.querySelector( 'pre code' ); }, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'plugin/zoom-js/zoom.js', async: true },
{ src: 'plugin/notes/notes.js', async: true }
]
});
</script>
</body>
</html>