Slide 1

Slide 1 text

PHPͰΞΫλʔϞσϧΛཧղɾମݧ͠Α͏ Understand and experience the actor model in PHP phpcon okinawa 2024 yuuki takezawa a.k.a ytake

Slide 2

Slide 2 text

Pro fi le • ஛ᖒ ༗و a.k.a ytake • ઍגࣜձࣾ CTO / ΄͔ٕज़ސ໰ʢωοτϓϩςΫγϣϯζͳͲʣ • Go / Scala / Kotlin / PHP • ΞΫλʔϞσϧେ޷͖

Slide 3

Slide 3 text

A toolkit for fl exible actor models in PHP

Slide 4

Slide 4 text

Phluxor

Slide 5

Slide 5 text

Phluxor • Phluxor / Actor System • Phluxor Persistence / Event Sourcing etc.. • Phluxor Remote / Network (Experimental / WIP) • Phluxor Cluster / Virtual Actors (WIP)

Slide 6

Slide 6 text

What is The Actor Model? • 1973೥ʹൃද͞Εͨฒߦܭࢉͷ਺ֶతϞσϧͷҰछ A type of mathematical model for concurrent computation introduced in 1973. • Erlang΍ScalaͳͲͰ͓ͳ͡Έ Well-known in languages like Erlang and Scala.

Slide 7

Slide 7 text

What is The Actor Model? • ϝοηʔδΛ༻͍ͯ΍ΓऔΓΛߦ͏ Communication is performed using messages. • ΞΫλʔ Ϟσϧ͸ɺ͢΂͕ͯΞΫλʔ The actor model adopts the philosophy that everything is an actor.

Slide 8

Slide 8 text

Actor Responsibilities • ΞΫλʔΛੜ੒͢Δ Create Another Actor • ϝοηʔδΛૹ৴͢Δ Send a Message • ϝοηʔδʹద༻͢Δಈ࡞Λߦ͏ Handle Message

Slide 9

Slide 9 text

Internal State • No Share Memory • State Change -> Event, and Persistence • Immutable Message

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Actor Reference • ͢΂ͯͷΞΫλʔ͸ΞυϨεΛ࣋ͭ All actors have an address. • ͜ͷΞυϨε͸ϢχʔΫͰࣝผ͞ΕΔ This address is unique and identi fi able. • ૹ৴ݩ΍ૹ৴ઌΛܾΊΔ͜ͱ͕Ͱ͖Δ You can specify the sender and recipient.

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Actor Lifecycle • ͢΂ͯͷΞΫλʔ͸ϥΠϑαΠΫϧΛ࣋ͭ All actors have a lifecycle • ΞΫλʔͷఀࢭɺ࠶ىಈͳͲ including stopping, restarting, etc. • Կ΋ཁٻ͞Εͳ͍৔߹͸Կ΋͠ͳ͍ If nothing is requested of them, they do nothing.

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Hierarchy • ΞΫλʔ͸ΞΫλʔΛੜ੒Ͱ͖Δ Actors can create actors. • ਌ࢠؔ܎͕ܗ੒͞ΕΔ Parent-child relationships are formed. • ࢠΞΫλʔʹ࢓ࣄ͕ґཔͰ͖Δ You can assign tasks to child actors.

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Fault Tolerance • ਌ΞΫλʔ͕ࢠΞΫλʔΛ؂ಜɾ؅ཧ͢Δ੹຿Λ࣋ͭ Parent actors have the responsibility to supervise and manage child actors. • ਌ࢠؔ܎ʹͳ͍ΞΫλʔ΋؂ࢹ͢Δ͜ͱ͕Ͱ͖Δ Actors that are not in a parent-child relationship can also be monitored.

Slide 20

Slide 20 text

Fault Tolerance • ࣦഊ࣌ʹͲͷΑ͏ʹ෮چͤ͞Δ͔ΛࢦࣔͰ͖Δ You can instruct how to recover in case of failure. • ΞΫλʔͷো֐͸ଞʹ͸఻೻͠ͳ͍ Actor failures do not propagate to others.

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

message(); switch (true) { case $message instanceof Hello: $ref = $context->spawn(Props::fromProducer(fn() => new ChildActor())); $context->send($ref, $message); break; case $message instanceof Terminated: $context->logger()->info('terminated', [ 'who' => $message->getWho()->getId(), 'why' => $message->getWhy(), ]); break; } } }

Slide 23

Slide 23 text

message(); switch (true) { case $message instanceof Hello: $ref = $context->spawn(Props::fromProducer(fn() => new ChildActor())); $context->send($ref, $message); break; case $message instanceof Terminated: $context->logger()->info('terminated', [ 'who' => $message->getWho()->getId(), 'why' => $message->getWhy(), ]); break; } } } 1BSFOU"DUPS

Slide 24

Slide 24 text

message(); switch (true) { case $message instanceof Hello: $ref = $context->spawn(Props::fromProducer(fn() => new ChildActor())); $context->send($ref, $message); break; case $message instanceof Terminated: $context->logger()->info('terminated', [ 'who' => $message->getWho()->getId(), 'why' => $message->getWhy(), ]); break; } } } $SFBUF$IJME "DUPS

Slide 25

Slide 25 text

message(); switch (true) { case $message instanceof Restarting: $context->logger()->info('restarting...'); break; case $message instanceof Hello: $context->logger()->info('Hello ' . $message->name); $context->stop($context->self()); break; } } } $IJME"DUPS

Slide 26

Slide 26 text

message(); switch (true) { case $message instanceof Restarting: $context->logger()->info('restarting...'); break; case $message instanceof Hello: $context->logger()->info('Hello ' . $message->name); $context->stop($context->self()); break; } } } 4UPQ4FMG

Slide 27

Slide 27 text

message(); switch (true) { case $message instanceof Hello: $ref = $context->spawn(Props::fromProducer(fn() => new ChildActor())); $context->send($ref, $message); break; case $message instanceof Terminated: $context->logger()->info('terminated', [ 'who' => $message->getWho()->getId(), 'why' => $message->getWhy(), ]); break; } } } /PUJ fi DBUJPO GSPN$IJME

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

Supervision

Slide 30

Slide 30 text

message(); switch (true) { case $message instanceof Restarting: $context->logger()->info('restarting...'); break; case $message instanceof Hello: $context->logger()->info('Hello ' . $message->name); throw new \Exception('hi, I am an exception'); break; } } } UISPX&YDFQUJPO -FUJUDSBTI

Slide 31

Slide 31 text

Auto Recovering / Restarting

Slide 32

Slide 32 text

Supervision • OneForOneStrategy • AllForOneStrategy • ExponentialBackoffStrategy • RestartStrategy

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Phluxor

Slide 39

Slide 39 text

Incarnate Actor Spawn

Slide 40

Slide 40 text

ActorInterface • No Return / Only sending and receiving messages. • All actors have a receive method that receives a context. • All actors are asynchronous and independent.

Slide 41

Slide 41 text

Slide 42

Slide 42 text

Spawning Actors • spawn method is used to create a child actor or root actor. • spawnNamed method is used to create a named actor. • spawnPre fi x method is used to create an actor with a pre fi x. • When an actor is spawned, Phluxor\ActorSystem\Ref is returned.

Slide 43

Slide 43 text

use Phluxor\ActorSystem; use Phluxor\ActorSystem\Props; $system = ActorSystem::create(); $spawned = $system->root()->spawn( Props::fromProducer( fn() => new YourActor() ) ); 8IFODSFBUJOHDIJMEBDUPST UIFZBSFHFOFSBUFEGSPNUIFBDUPSDPOUFYU 5IFJOUFSGBDFJTUIFTBNFBTUIFSPPU

Slide 44

Slide 44 text

$context->spawnNamed( Props::fromProducer( fn() => new YourChildActor() ), 'unique-name' ); $context->spawnPrefix( Props::fromProducer( fn() => new YourChildActor() ), 'prefix-' ); "TTJHOBOZOBNFUPUIFBDUPS *GZPVBSFVTJOHQFSTJTUFODF BTTJHOBVOJRVFTQFDJ fi DOBNFSBUIFSUIBOBSBOEPNPOF DSFBUFBOBDUPSXJUIBQSF fi Y

Slide 45

Slide 45 text

Props • The Props are a con fi guration object used to create an actor. • Phluxor\ActorSystem\Props is a class that creates a Props object. • and more!!!!

Slide 46

Slide 46 text

Props::fromProducer(fn() => new YourActor()) Props::fromFunction( new ActorSystem\Message\ReceiveFunction( fn( ActorSystem\Context\ContextInterface $context ) => $context->message() ) ); :PVDBOTQFDJGZBDMBTTUIBUJNQMFNFOUTUIF"DUPS*OUFSGBDF PSVTFBGVODUJPOTUZMF

Slide 47

Slide 47 text

Props::fromProducer( fn() => new VoidActor(), ActorSystem\Props::withSupervisor( new ActorSystem\Strategy\OneForOneStrategy( 10, new \DateInterval('PT10S'), fn() => ActorSystem\Directive::Restart ) ) ); Props::fromProducer( fn() => new YourActor(), Props::withMailboxProducer(new ActorSystem\Mailbox\Unbounded()) ); :PVDBONPEJGZUIF.BJMCPY 5IFSFBSFPQUJPOTGPS#BUDI #PVOEFE BOE6OCPVOEFENBJMCPYFT :PVDBONPEJGZUIFTVQFSWJTPSTUSBUFHZ TJNJMBSUP"LLB 1FLLP PS1SPUP"DUPS

Slide 48

Slide 48 text

Send Message • send is a non-blocking, fi re-and-forget • request is very similar to send, Only use it when request/reply communication is required between two actors. • Futures and promises

Slide 49

Slide 49 text

$context->send($ref, new PrepareTest(['subject' => $msg->getSubject()]) ); $context->request($ref, new PrepareTest(['subject' => $msg->getSubject()]) ); $future = $context->requestFuture( $ref, new PrepareTest(['subject' => $msg->getSubject()]), 1 ); 5IFSFTVMUJTFYQFDUFEUPCFPCUBJOFEXJUIJOUIFTQFDJ fi FEUJNF *GOPU BOFSSPSXJMMCFSFUVSOFEBTUIFSFTVMU

Slide 50

Slide 50 text

Try!

Slide 51

Slide 51 text

https://github.com/ytake/phpcon- okinawa-example

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

ֶߍͷςετྫ • ઌੜ͕ੜెʹରͯ͠ ͞Μ͢͏ͷςετΛ࣮ࢪ • ੜె͸ͦΕͧΕݸͱͯ͠໰୊Λղ͘ • ઌੜ͸ςετதʹݟकΔͳͲͷߦಈ • શͯ౴͑ͨΒςετ༻ࢴΛఏग़͢Δ

Slide 54

Slide 54 text

example (the answer sheet • Class time begins • The teacher comes to the classroom • Test begins • Write your answers on the answer sheet. • Submit test answer sheets

Slide 55

Slide 55 text

4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

Notice! Actors Always Run Asyncronously It doesn't look like php? :)

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Classroom

Slide 60

Slide 60 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } }

Slide 61

Slide 61 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } } 'SPNSPPU

Slide 62

Slide 62 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } } 4QBXO5FBDIFS

Slide 63

Slide 63 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } } 4FOEBNFTTBHFUPZPVSUFBDIFS

Slide 64

Slide 64 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } } 3FQMZUPUIFTQFDJ fi FEBDUPS 0ODFBMMTUVEFOUTIBWFDPNQMFUFEUIFUFTU

Slide 65

Slide 65 text

readonly class ClassroomActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof StartsClass: $ref = $context->spawn( Props::fromProducer( fn() => new TeacherActor( $this->subject, $this->students, $context->self() ) ) ); $context->send($ref, new PrepareTest($msg->subject)); break; case $msg instanceof FinishTest: $context->send( $this->stream, new ClassFinished($msg->subject) ); \Swoole\Coroutine::sleep(0.1); $context->stop($context->self()); break; } } } 4UPQBOESFMFBTFSFTPVSDFT

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

Teacher

Slide 68

Slide 68 text

class TeacherActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof PrepareTest: $context->logger()->info( sprintf("Teacher has issued a %s test", $msg->subject) ); foreach ($this->students as $student) { $ref = $context->spawnNamed( Props::fromProducer(fn() => new StudentActor()), sprintf('student-%d', $student) ); $context->send($ref->getRef(), new StartTest($msg->subject)); } break; case $msg instanceof SubmitTest: $this->endOfTests[] = $msg; if (count($this->endOfTests) === count($this->students)) { $context->send($this->replyTo, new FinishTest($this->subject)); $context->poison($context->self()); } break; } } }

Slide 69

Slide 69 text

class TeacherActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof PrepareTest: $context->logger()->info( sprintf("Teacher has issued a %s test", $msg->subject) ); foreach ($this->students as $student) { $ref = $context->spawnNamed( Props::fromProducer(fn() => new StudentActor()), sprintf('student-%d', $student) ); $context->send($ref->getRef(), new StartTest($msg->subject)); } break; case $msg instanceof SubmitTest: $this->endOfTests[] = $msg; if (count($this->endOfTests) === count($this->students)) { $context->send($this->replyTo, new FinishTest($this->subject)); $context->poison($context->self()); } break; } } } 'SPN $MBTTSPPN

Slide 70

Slide 70 text

class TeacherActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof PrepareTest: $context->logger()->info( sprintf("Teacher has issued a %s test", $msg->subject) ); foreach ($this->students as $student) { $ref = $context->spawnNamed( Props::fromProducer(fn() => new StudentActor()), sprintf('student-%d', $student) ); $context->send($ref->getRef(), new StartTest($msg->subject)); } break; case $msg instanceof SubmitTest: $this->endOfTests[] = $msg; if (count($this->endOfTests) === count($this->students)) { $context->send($this->replyTo, new FinishTest($this->subject)); $context->poison($context->self()); } break; } } } 4QBXO4UVEFOUT 6OJRVF

Slide 71

Slide 71 text

class TeacherActor implements ActorInterface { // omit public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof PrepareTest: $context->logger()->info( sprintf("Teacher has issued a %s test", $msg->subject) ); foreach ($this->students as $student) { $ref = $context->spawnNamed( Props::fromProducer(fn() => new StudentActor()), sprintf('student-%d', $student) ); $context->send($ref->getRef(), new StartTest($msg->subject)); } break; case $msg instanceof SubmitTest: $this->endOfTests[] = $msg; if (count($this->endOfTests) === count($this->students)) { $context->send($this->replyTo, new FinishTest($this->subject)); $context->poison($context->self()); } break; } } } *OUFSOBM4UBUF$IBOHF 0ODFBMMTUVEFOUTIBWFDPNQMFUFEUIFUFTU 4UPQBOESFMFBTFSFTPVSDFT

Slide 72

Slide 72 text

Student(s)

Slide 73

Slide 73 text

class StudentActor implements ActorInterface { public function receive(ContextInterface $context): void { $msg = $context->message(); if ($msg instanceof StartTest) { sleep(random_int(1, 9)); $context->logger()->info( sprintf( '%s is submitting the answer to the %s test', $context->self(), $msg->subject ) ); $context->send( $context->parent(), new SubmitTest( $msg->subject, (string)$context->self(), ) ); $context->poison($context->self()); } } }

Slide 74

Slide 74 text

class StudentActor implements ActorInterface { public function receive(ContextInterface $context): void { $msg = $context->message(); if ($msg instanceof StartTest) { sleep(random_int(1, 9)); $context->logger()->info( sprintf( '%s is submitting the answer to the %s test', $context->self(), $msg->subject ) ); $context->send( $context->parent(), new SubmitTest( $msg->subject, (string)$context->self(), ) ); $context->poison($context->self()); } } } 'SPN5FBDIFS

Slide 75

Slide 75 text

class StudentActor implements ActorInterface { public function receive(ContextInterface $context): void { $msg = $context->message(); if ($msg instanceof StartTest) { sleep(random_int(1, 9)); $context->logger()->info( sprintf( '%s is submitting the answer to the %s test', $context->self(), $msg->subject ) ); $context->send( $context->parent(), new SubmitTest( $msg->subject, (string)$context->self(), ) ); $context->poison($context->self()); } } } 3BOEPNMZTFMFDUUIFUJNFUPBOTXFSUIFUFTU TT

Slide 76

Slide 76 text

class StudentActor implements ActorInterface { public function receive(ContextInterface $context): void { $msg = $context->message(); if ($msg instanceof StartTest) { sleep(random_int(1, 9)); $context->logger()->info( sprintf( '%s is submitting the answer to the %s test', $context->self(), $msg->subject ) ); $context->send( $context->parent(), new SubmitTest( $msg->subject, (string)$context->self(), ) ); $context->poison($context->self()); } } } QBSFOU BSF 5FBDIFS"DUPS %POUGPSHFUUIBUJUTBIJFSBSDIJDBM

Slide 77

Slide 77 text

class StudentActor implements ActorInterface { public function receive(ContextInterface $context): void { $msg = $context->message(); if ($msg instanceof StartTest) { sleep(random_int(1, 9)); $context->logger()->info( sprintf( '%s is submitting the answer to the %s test', $context->self(), $msg->subject ) ); $context->send( $context->parent(), new SubmitTest( $msg->subject, (string)$context->self(), ) ); $context->poison($context->self()); } } } 4UVEFOU/BNF 'PSFYBNQMF TUVEFOU TQBXO/BNFE 4UPQBOESFMFBTFSFTPVSDFT

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS 4UVEFOU"DUPS

Slide 81

Slide 81 text

Example • Teacher -> Actor / Aggregate • Students -> Actor • Classroom -> Actor / Aggregate

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

Training

Slide 85

Slide 85 text

and more

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

In closing • It's a somewhat different programming paradigm. • It's quite dif fi cult to understand just by listening, so please try it out yourself. • Once you understand it, you can apply it to your regular programming and modeling!

Slide 88

Slide 88 text

͍͞͝ʹ • ϓϩάϥϛϯάύϥμΠϜ͕ͪΐͬͱҧ͏΋ͷ • ฉ͚ͩ͘Ͱ͸ͳ͔ͳ͔೉͍͠ͷͰɺͥͻ৮ͬͯΈ͍ͯͩ͘͞ • ཧղͰ͖ΔͱීஈͷϓϩάϥϛϯάɺϞσϦϯάʹ΋׆͔ͤΔʂ

Slide 89

Slide 89 text

Welcome to the World of the Actor Model. Α͏ͦ͜ΞΫλʔϞσϧͷੈք΁