Slide 1

Slide 1 text

PHP / PhluxorͰ ΞΫλʔϞσϧΛ ཧղɾମݧ͠Α͏ ୈ165ճ PHPษڧձˏ౦ژ ytake

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

PHPͰ΋ΞΫλʔϞσϧ͕ ಋೖͰ͖Δʂ

Slide 4

Slide 4 text

؆୯ʹಋೖ͢ΔͨΊͷπʔϧΩοτ

Slide 5

Slide 5 text

PhluxorʢϑϥΫαʔʣެ։ https://github.com/ytake/phluxor

Slide 6

Slide 6 text

Phluxor • ScalaͳͲͰ͓ͳ͡ΈͷAkka / Pekko • Go΍.NETͰ࢖ΘΕ͍ͯΔ Proto.Actor • ͜ΕΒͱಉ֓͡೦ʹج͖ͮ PHPͰΞΫλʔϞσϧΛ࣮ݱ͢ΔͨΊͷ΋ͷ͕Phluxor • ΫϥελɾҰ෦ϧʔλʔػೳ͸ݱࡏ࣮૷தʢ਺ϲ݄Ҏ಺ʹఏڙʣ

Slide 7

Slide 7 text

WebΞϓϦέʔγϣϯ ϑϨʔϜϫʔΫͰ͸͋Γ·ͤΜ ϓϩάϥϛϯάΞϓϩʔν΍ ΞϧΰϦζϜతͳ΋ͷ

Slide 8

Slide 8 text

Phluxor͕αϙʔτ͍ͯ͠Δ΋ͷ • ΞΫλʔͷجຊཁૉશൠ • EventStream • Supervision • Future (Async/AwaitΈ͍ͨͳ΋ͷ) • Behavior • Persistence • Router • DeadLetter • Middleware • Reentrancy

Slide 9

Slide 9 text

Proto.Actorͱಉ౳ͷػೳ

Slide 10

Slide 10 text

஫ҙ • Swoole / OpenSwoole͕ಋೖ͞Ε͍ͯͳ͍ͱಈ͖·ͤΜ • coroutineΛແޮʹͨ͠؀ڥͰ͸ಈ͖·ͤΜ • Laravel OctaneͰ͸ಈ࡞͠·ͤΜʢcoroutineແޮͷͨΊʣ • MezzioͳͲ͸ಛʹۤ࿑ͤͣ݁߹Ͱ͖·͢

Slide 11

Slide 11 text

$ composer require phluxor/phluxor

Slide 12

Slide 12 text

ΞΫλʔϞσϧͱ͸ʁ

Slide 13

Slide 13 text

ॳݟͰ࢖͏ʹ͸ ͪΐͬͱ೉͍͠

Slide 14

Slide 14 text

ΞΫλʔϞσϧʹ͍ͭͯཧղ͢ΔͨΊʹ

Slide 15

Slide 15 text

ͪΐͬͱݹ͍ຊͰ͕͢΄΅ಉ͡Ͱ͢ ಡΈସ͑ͯशಘͰ͖·͢

Slide 16

Slide 16 text

Erlang/OTPͳͲ΋ࢀߟʹ

Slide 17

Slide 17 text

*͜Ε͔Β࿩͢಺༰͸PHPͰ͢

Slide 18

Slide 18 text

Ξϯϥʔχϯά͍͖ͯ͠·͠ΐ͏

Slide 19

Slide 19 text

ΞΫλʔϞσϧ • ฒߦܭࢉͷ਺ֶཧ࿦Ϟσϧ • શͯͷ΋ͷ͸ΞΫλʔͰɺ ֤ΞΫλʔ͸ϝοηʔδΛૹ৴͢Δ͜ͱͰͷΈ௨৴͠ɺ ࣗݾ؅ཧͷঢ়ଶΛ࣋ͭ • ஞ࣍ੑ͕લఏͰ͸ͳ͘ɺ͢΂͕ͯಠཱͯ͠ฒߦʹ

Slide 20

Slide 20 text

ΞΫλʔϞσϧ࣮ݱͷͨΊʹ • ઃఆ΍ϛυϧ΢ΣΞ͸࣋ͨͣɺΞΫλʔ͝ͱʹMailboxΛ࣋ͭ ʢϝοηʔδΩϡʔʹࣅͯΔʣ • ฒߦͰඇಉظ ϝοηʔδ͕དྷͳ͚Ε͹Կ΋ͤͣɺԿઍݸ΋࡞੒Մೳ • ϝοηʔδ͸γϯϓϧͳ΋ͷͰΠϛϡʔλϒϧʢมߋෆՄʣ

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

ΞΫλʔϞσϧ͕΋ͨΒ͢΋ͷ • ϩʔΧϧʹ͋ͬͯ΋ผͳαʔόʹ͋ͬͯ΋ҙࣝͤͣʹॲཧՄೳ ʢҐஔಁաੑαϙʔτʣ • ҰͭͷΞΫλʔ͕ࣦഊͯ͠΋ɺଞͷΞΫλʔʹ͸Өڹͳ͠ • ෛՙ෼ࢄ͕༰қͰɺಠཱ͍ͯ͠Δ͔Βͦ͜ഉଞ੍ޚ͸ؾʹͤͣʹ • return͸͋Γ·ͤΜ

Slide 23

Slide 23 text

ҰൠతͳγϣοϐϯάγεςϜྫ • ΦϯϥΠϯγϣοϐϯάΧʔτΛ؅ཧ͢ΔΫϥεͳͲ • ΫϥεϝιουΛ௨ͯ͡ΧʔτʹΞΠςϜ௥Ճɾ࡟আ • ΫϥεͷΠϯελϯε͸ෳ਺ͷ෦෼Ͱڞ༗͞Εɺ ঢ়ଶͷҰ؏ੑΛอͭͨΊʹಉظʢDBͳͲʣ

Slide 24

Slide 24 text

ΞΫλʔΛ࢖ͬͨγϣοϐϯάγεςϜྫ • ΦϯϥΠϯγϣοϐϯάΧʔτΛ؅ཧ͢ΔΞΫλʔ • ΞΫλʔʹΞΠςϜͷ௥Ճ΍࡟আΛࢦࣔ͢ΔඇಉظϝοηʔδΛૹ৴ • ଞͷΞΫλʔͱͷঢ়ଶڞ༗͸ߦΘͣɺ ͢΂ͯͷঢ়ଶมߋ͸ϝοηʔδʹΑͬͯ؅ཧ

Slide 25

Slide 25 text

let it crash Approach

Slide 26

Slide 26 text

ͱ͸͍͑೉͍͠ • ୯७ʹΫϥογϡͤͯ͞͠·͑͹Α͍ɺͱ͍Θ͚Ͱ͸ͳ͍ • ౸ୡ͠ͳ͔ͬͨϝοηʔδͷѻ͍͸ʁ • ૹ৴ݩ΍ૹ৴ઌΛ஌͍ͬͯͳ͍ͱ͍͚ͳ͍͜ͱ΋͋Δ ʢಠཱ͍ͯͯ͠΋ϝοηʔδपลʹґଘʣ

Slide 27

Slide 27 text

ιϑτ΢ΣΞΞʔΩςΫνϟ ͱ͍͏ΑΓ΋ ৘ใ఻ୡઃܭ

Slide 28

Slide 28 text

ΑΓཧղ͢ΔͨΊʹ

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Phluxor ΞΫλʔͷجຊʢҰ෦ൈਮʣ

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

ActorRef • ΞΫλʔͷࢀরΛ୲౰ • ΞΫλʔͷҐஔ৘ใʢΞυϨεʣͷΈΛ࣋ͭ΋ͷ ૹ৴ݩɾૹ৴ઌͱͯ͠ར༻ • ΞΫλʔͷ಺෦Λૢ࡞Ͱ͖Δ΋ͷͰ͸͋Γ·ͤΜ ʢͦΜͳػೳ͸ΞΫλʔʹ͋Γ·ͤΜʣ

Slide 33

Slide 33 text

Children • ͢΂ͯͷΞΫλʔ͸ࢠΛੜ੒͢Δ͜ͱ͕Ͱ͖ʢࢠΞΫλʔʣɺ Ϋϥογϡ͠΍͍͢λεΫͳͲΛґཔ͢Δ͜ͱ͕Մೳ • spawnͰੜ੒͠ɺstopͰఀࢭ • ͢΂ͯඇಉظͰಠཱ͠ɺͳʹͱ΋ڞ༗͠ͳ͍ΞΫλʔΛੜ੒

Slide 34

Slide 34 text

֊૚ߏ଄Λ΋ͭ

Slide 35

Slide 35 text

Supervision • ࢠΞΫλʔͰԿ͔͠ΒΫϥογϡ͕ى͖ͨ৔߹ʹඋ͑ɺ ઓུΛࢦఆ͢Δ͜ͱ͕Մೳ • ࢠΞΫλʔͷࣗಈ࠶ىಈ΍ɺճ਺্ݶΛઃ͚ͨ࠶ىಈɺ ִ࣌ؒؒΛঃʑʹ޿͛ͯ࠶ىಈͤ͞ΔͳͲ͍͔ͭ͘༻ҙ͍ͯ͠·͢

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

ProcessRegistry • ΞΫλʔγεςϜΛੜ੒͢Δͱ࡞੒͞ΕΔϨδετϦ • ΞΫλʔ͕ੜ੒͞ΕΔͱɺSwooleΛ࢖ͬͯഉଞ੍ޚͰ ΞΫλʔ৘ใΛอ؅ʢΞυϨεͳͲͷ؅ཧʣ

Slide 40

Slide 40 text

Actor • ੜ੒͞ΕͨΞΫλʔ͸ίϯςΩετʢ৭Μͳ৘ใʣΛ࣋ͭ • ੜ੒࣌ʹΞΫλʔ͝ͱʹMailbox͕ੜ੒͞ΕɺϨδετϦʹอ؅ • ϝοηʔδ͕౸ୡ͢ΔͱDispatcher͕ΞΫλʔʹϓογϡ(coroutine) • ΞΫλʔݸผʹSwooleͷAtomicͰ੍ޚ͞Ε͍ͯ·͢

Slide 41

Slide 41 text

Supervision / Strategy • ಺෦ΠϕϯτͷWatchΛൃߦͯ͠؂ࢹͷؔ܎ੑΛ࣋ͭ • Ϋϥογϡ͢ΔͱStrategyʹج͍ͮͨઓུͰ෮چΛࢼΈΔ SwooleͷTimerͳͲΛར༻ • ϥΠϑαΠΫϧؔ࿈Ͱ͸ ಺෦ϝοηʔδͷStarted΍TerminatedɺRecoveringͳͲ͕͋Γ

Slide 42

Slide 42 text

Phluxor αϯϓϧΛݟͯΈΑ͏ https://github.com/ytake/phluxor- http-application-samples

Slide 43

Slide 43 text

BoxO ff i ce • ֤छνέοτΛऔΓѻ͏ • ͢΂ͯͷґཔΛड͚࣋ͭΞΫλʔ • ΦʔέετϨʔλʔ

Slide 44

Slide 44 text

TicketSeller • ΞʔςΟετɾΠϕϯτຖʹνέοτΛ؅ཧ͢ΔΞΫλʔ • Spawn࣌ʹࣝผՄೳͳ໊લΛ༩͑Δ͜ͱͰɺ Ґஔ͕͸͖ͬΓΘ͔Δಠཱͨ͠ΞΫλʔʹ • ୭ͷ͍ͭͷΠϕϯτ͔ɺ͢΂ͯฒߦͰಠཱͯ͠؅ཧ

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

#PY0 ff i DFΞΫλʔ 5JDLFU4FMMFSΞΫλʔ YYΞΫλʔ

Slide 47

Slide 47 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } }

Slide 48

Slide 48 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } } NF[[JPTXPPMFΛτϦΨʔʹ ΞΫλʔγεςϜىಈ ϦΫΤετͷͨͼʹΞΫλʔγεςϜΛ ىಈ͢Δͱɺ ߴίετͰঢ়ଶҡ͕࣋Ͱ͖ͳ͍ͨΊ

Slide 49

Slide 49 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } } SPPU͸ΞΫλʔγεςϜτοϓϨϕϧ ઃܭʹΑͬͯ͸ τοϓϨϕϧΞΫλʔΛෳ਺࡞ͬͯ΋0,

Slide 50

Slide 50 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } } ΞΫλʔੜ੒࣌ͷઃఆ ʢͳʹ΋ͳ͍ͱ͖͸ͨͩͷΞΫλʔʣ

Slide 51

Slide 51 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } } ΞΫλʔੜ੒࣌ʹ໊લΛ͚ͭΔ 4QBXO/BNFE

Slide 52

Slide 52 text

readonly class BootAppActor { public function __construct( private ContainerInterface $container ) { } public function __invoke(WorkerStartEvent $event): void { $system = ActorSystem::create(); $spawned = $system->root()->spawnNamed( Props::fromProducer(fn() => new BoxOffice()), AppActor::NAME ); if ($this->container instanceof ServiceManager) { $this->container->setService(AppActor::class, new AppActor($system->root(), $spawned->getRef())); return; } throw new RuntimeException('Container is not a ServiceManager'); } } #PY0 ff i DFͷΞΫλʔΛ CPY@P ff i DFͱ͍͏໊લͰ τοϓϨϕϧʹ഑ஔ

Slide 53

Slide 53 text

public function handle(ServerRequestInterface $request): ResponseInterface { $actor = $this->appActor; $post = $request->getParsedBody(); if (! isset($post['tickets'])) { return new JsonResponse(['error' => 'tickets is required'], 400); } $eventName = $request->getAttribute('name'); $future = $actor->root->requestFuture( $actor->actorRef, new EventDescription($eventName, (int) $post['tickets']), 2000 ); $fr = $future->result(); if ($fr->error() !== null) { return new JsonResponse(['error' => $fr->error()], 400); } $v = $fr->value(); return match (true) { $v instanceof EventCreated => new JsonResponse(['message' => 'event created', 'event' => $eventName]), $v instanceof EventExists => new JsonResponse(['message' => 'event exists', 'event' => $eventName], 409), default => new JsonResponse(['message' => 'unknown'], 400), }; }

Slide 54

Slide 54 text

public function handle(ServerRequestInterface $request): ResponseInterface { $actor = $this->appActor; $post = $request->getParsedBody(); if (! isset($post['tickets'])) { return new JsonResponse(['error' => 'tickets is required'], 400); } $eventName = $request->getAttribute('name'); $future = $actor->root->requestFuture( $actor->actorRef, new EventDescription($eventName, (int) $post['tickets']), 2000 ); $fr = $future->result(); if ($fr->error() !== null) { return new JsonResponse(['error' => $fr->error()], 400); } $v = $fr->value(); return match (true) { $v instanceof EventCreated => new JsonResponse(['message' => 'event created', 'event' => $eventName]), $v instanceof EventExists => new JsonResponse(['message' => 'event exists', 'event' => $eventName], 409), default => new JsonResponse(['message' => 'unknown'], 400), }; } τοϓϨϕϧΞΫλʔʹ νέοτ࡞੒Λґཔ

Slide 55

Slide 55 text

public function handle(ServerRequestInterface $request): ResponseInterface { $actor = $this->appActor; $post = $request->getParsedBody(); if (! isset($post['tickets'])) { return new JsonResponse(['error' => 'tickets is required'], 400); } $eventName = $request->getAttribute('name'); $future = $actor->root->requestFuture( $actor->actorRef, new EventDescription($eventName, (int) $post['tickets']), 2000 ); $fr = $future->result(); if ($fr->error() !== null) { return new JsonResponse(['error' => $fr->error()], 400); } $v = $fr->value(); return match (true) { $v instanceof EventCreated => new JsonResponse(['message' => 'event created', 'event' => $eventName]), $v instanceof EventExists => new JsonResponse(['message' => 'event exists', 'event' => $eventName], 409), default => new JsonResponse(['message' => 'unknown'], 400), }; } SFRVFTU'VUVSF͸ BTZODBXBJUͷΑ͏ʹ ޙʹૹΒΕͯ͘Δ஋͕ར༻Ͱ͖Δ

Slide 56

Slide 56 text

public function handle(ServerRequestInterface $request): ResponseInterface { $actor = $this->appActor; $post = $request->getParsedBody(); if (! isset($post['tickets'])) { return new JsonResponse(['error' => 'tickets is required'], 400); } $eventName = $request->getAttribute('name'); $future = $actor->root->requestFuture( $actor->actorRef, new EventDescription($eventName, (int) $post['tickets']), 2000 ); $fr = $future->result(); if ($fr->error() !== null) { return new JsonResponse(['error' => $fr->error()], 400); } $v = $fr->value(); return match (true) { $v instanceof EventCreated => new JsonResponse(['message' => 'event created', 'event' => $eventName]), $v instanceof EventExists => new JsonResponse(['message' => 'event exists', 'event' => $eventName], 409), default => new JsonResponse(['message' => 'unknown'], 400), }; } GVUVSFͷ݁Ռ

Slide 57

Slide 57 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break;

Slide 58

Slide 58 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; #PY0 ff i DFΞΫλʔ SFDFJWFϝιουͷΈ࣮૷

Slide 59

Slide 59 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; ྲྀΕͯ͘Δϝοηʔδ͸ DPOUFYUNFTTBHF Ͱऔಘ

Slide 60

Slide 60 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; &WFOU%FTDSJQUJPOϝοηʔδ΁ͷ ରԠΛهड़ NBUDI౳Ͱ΋0,

Slide 61

Slide 61 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; ࢓ࣄΛґཔ͢ΔࢠΞΫλʔΛੜ੒

Slide 62

Slide 62 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; ΠϕϯτνέοτΛ୲౰͢Δ ΞΫλʔ ࣝผͰ͖Δ໊લΛ͚ͭΔྫ

Slide 63

Slide 63 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; ࣝผՄೳͳΞΫλʔ͕ઌʹ ىಈ͍ͯ͠Δঢ়ଶ͕͢Ͱʹ͋Δ ͜ͷͨΊ࡞੒͠ͳ͍

Slide 64

Slide 64 text

public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof EventDescription: $result = $context->spawnNamed( Props::fromProducer(fn() => new TicketSeller()), $msg->name ); if ($result->isError() instanceof NameExistsException) { // ΞΫλʔ͕ੜ੒ࡁΈͷ৔߹͸ੜ੒ࡁΈͰ͋Δ͜ͱΛ௨஌͠·͢ $context->respond(new EventExists()); break; } $context->send($result->getRef(), new Add($msg->name, $msg->tickets, $context->sender())); break; νέοτൢചΞΫλʔѼʹ "EEϝοηʔδΛૹ৴

Slide 65

Slide 65 text

No content

Slide 66

Slide 66 text

BoxO ffi ce ΞΫλʔ TicketSeller ΞΫλʔ TicketSeller ΞΫλʔ TicketSeller ΞΫλʔ TicketSeller ΞΫλʔ TicketSeller ΞΫλʔ TicketSeller ΞΫλʔ

Slide 67

Slide 67 text

Slide 68

Slide 68 text

Slide 69

Slide 69 text

class TicketSeller implements ActorInterface { private int $tickets = 0; private string $name = ''; private string $id = ''; public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof Add: // Ұ෦ൈਮ $this->name = $msg->name; $this->tickets = $msg->tickets; $this->id = (string)$context->self(); $context->send($msg->replyTo, new EventCreated($msg->name, $msg->tickets)); break; } } }

Slide 70

Slide 70 text

class TicketSeller implements ActorInterface { private int $tickets = 0; private string $name = ''; private string $id = ''; public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof Add: // Ұ෦ൈਮ $this->name = $msg->name; $this->tickets = $msg->tickets; $this->id = (string)$context->self(); $context->send($msg->replyTo, new EventCreated($msg->name, $msg->tickets)); break; } } } 5JDLFU4FMMFS͕ "EEϝοηʔδΛड͚औͬͨΒ

Slide 71

Slide 71 text

class TicketSeller implements ActorInterface { private int $tickets = 0; private string $name = ''; private string $id = ''; public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof Add: // Ұ෦ൈਮ $this->name = $msg->name; $this->tickets = $msg->tickets; $this->id = (string)$context->self(); $context->send($msg->replyTo, new EventCreated($msg->name, $msg->tickets)); break; } } } 5JDLFU4FMMFSʹঢ়ଶΛอଘ 1FSTJTUFODFར༻࣌͸ɺ ঢ়ଶมߋຖʹอଘ͞ΕΔ

Slide 72

Slide 72 text

class TicketSeller implements ActorInterface { private int $tickets = 0; private string $name = ''; private string $id = ''; public function receive(ContextInterface $context): void { $msg = $context->message(); switch (true) { case $msg instanceof Add: // Ұ෦ൈਮ $this->name = $msg->name; $this->tickets = $msg->tickets; $this->id = (string)$context->self(); $context->send($msg->replyTo, new EventCreated($msg->name, $msg->tickets)); break; } } } νέοτൢചΠϕϯτͷ ঢ়ଶ͕อଘ͞ΕͨΒ &WFOU$SFBUFEΠϕϯτΛฦ٫ ʢSFQMZ5Pʹࢦఆ͞ΕͨΞΫλʔѼʣ

Slide 73

Slide 73 text

BoxO ff i ce • ݱࡏͷࡏݿΛௐ΂͍ͨͱ͖͸ɺࢠΞΫλʔʹݱࡏͷঢ়ଶΛฉ͘ • ·ͱΊͯฦ͢ • ൢചऴྃͳͲ͕͋ͬͨ৔߹͸֘౰ΞΫλʔʹఀࢭࢦࣔ ϨδετϦ͔Β΋࡟আ͞Εͯ։์

Slide 74

Slide 74 text

private function fetchEvents(ContextInterface $context): array { $wg = new WaitGroup(); $events = []; foreach ($context->children() as $child) { $wg->add(); $future = $context->requestFuture($child, new GetEvent(), 2000); $fr = $future->result(); if ($fr->error() !== null) { $wg->done(); continue; } if ($fr->value() instanceof Event) { $events = array_merge($events, [$fr->value()]); $wg->done(); } } $wg->wait(); return $events; }

Slide 75

Slide 75 text

private function fetchEvents(ContextInterface $context): array { $wg = new WaitGroup(); $events = []; foreach ($context->children() as $child) { $wg->add(); $future = $context->requestFuture($child, new GetEvent(), 2000); $fr = $future->result(); if ($fr->error() !== null) { $wg->done(); continue; } if ($fr->value() instanceof Event) { $events = array_merge($events, [$fr->value()]); $wg->done(); } } $wg->wait(); return $events; } ࣗ਎͕ੜ੒ͨ͠ࢠΞΫλʔ ʢ֤ΠϕϯτνέοτൢചΞΫλʔʣ ʹݱࡏͷঢ়ଶΛਘͶΔ

Slide 76

Slide 76 text

private function fetchEvents(ContextInterface $context): array { $wg = new WaitGroup(); $events = []; foreach ($context->children() as $child) { $wg->add(); $future = $context->requestFuture($child, new GetEvent(), 2000); $fr = $future->result(); if ($fr->error() !== null) { $wg->done(); continue; } if ($fr->value() instanceof Event) { $events = array_merge($events, [$fr->value()]); $wg->done(); } } $wg->wait(); return $events; } ͢΂ͯͷνέοτൢചঢ়گΛϚʔδ

Slide 77

Slide 77 text

஫ҙ • Persistenceͷྫ͸αϯϓϧίʔυɺ΋͘͠͸ςετίʔυΛࢀর • ΠϯλʔϑΣʔε࣮૷͢Δ͜ͱͰRDBMSͳͲʹରԠ • ͨͩ͠ίϧʔνϯͰಈͨ͘Ίɺ͓ؾʹೖΓͷORM࢖͏ͳͲ͸ ݕূ͍ͯͩ͘͠͞

Slide 78

Slide 78 text

PR͓଴͓ͪͯ͠Γ·͢

Slide 79

Slide 79 text

ଞʹ΋ ֶߍͷςετΛ໛ͨ͠αϯϓϧ΋ https://github.com/ytake/phluxor- example

Slide 80

Slide 80 text

Proto.Actor޲͚αϯϓϧ ಡΈସ͑Ε͹ͦͷ··ಈ͖·͢ https://github.com/ytake/ protoactor-go-example

Slide 81

Slide 81 text

·ͱΊ • ͢΂ͯΛղܾ͢Δ΋ͷͰ͸ͳ͍ • ฒߦͰඇಉظͰɺεέʔϧ͍ͤͨ͞ͳΒબ୒ࢶͷҰͭ • ES+CQRSͳͲΛ࣮ફ͍ͨ͠৔߹͸ମݧͯ͠ΈΔ • Α͏ͦ͜ɺϝοηʔδύογϯάͷੈք