$30 off During Our Annual Pro Sale. View Details »

PHPでもサーバーレスしたい

 PHPでもサーバーレスしたい

2017PHPカンファレンス関西-PHPでもサーバーレスしたい

Kaz Watanabe

July 15, 2017
Tweet

More Decks by Kaz Watanabe

Other Decks in Technology

Transcript

  1. Α͘ޠΒΕΔϢʔεέʔε w 8FCΞϓϦέʔγϣϯ w ϞόΠϧόοΫΤϯυ w *P5όοΫΤϯυ w ετϦʔϜϓϩηογϯά΍&5- w

    νϟοτϘοτɺίάχςΟϒαʔϏε w όονॲཧ w ӡ༻ͷࣗಈԽ ࠷ॳʹࢼ͢ͷʹΦεεϝʂ
  2. "[VSF'VODUJPOT w ΊͬͪΌ͍҆ w ೖྗɾग़ྗͷ࿈ܞػೳ͕๛෋ τϦΨʔɾόΠϯσΟϯά  w 7JTVBM4UVEJPΛ࢖ͬͯͷσόοά ϩʔΧϧɾϦϞʔτʣ

    w ΦϯϓϨͰ΋͍͚Δ "[VSF4UBDL  w ϥϯλΠϜ͕Φʔϓϯ IUUQTHJUIVCDPN"[VSF"[VSF'VODUJPOT  w (JUIVC͔ΒσϓϩΠͰ͖Δ
  3. %VSBCMF'VODUJPOT Ћ൛ w ෳ਺ͷ'VODUJPOͷ࿈ܞΛൺֱత؆୯ʹ࣮૷Ͱ͖Δ w 'VODUJPODIBJOJOH w 'BOPVU'BOJO w "TZOD)551"1*T

    IUUQTB[VSFHJUIVCJPB[VSFGVODUJPOTEVSBCMFFYUFOTJPOJOEFYIUNM IUUQRJJUBDPN5TVZPTIJ6TIJP!HJUIVCJUFNTFBDCCCGEC
  4. 'VODUJPO$IBJOJOH public static async Task<object> Run(DurableOrchestrationContext ctx) { try {

    var x = await ctx.CallFunctionAsync<object>("F1"); var y = await ctx.CallFunctionAsync<object>("F2", x); var z = await ctx.CallFunctionAsync<object>("F3", y); return await ctx.CallFunctionAsync<object>("F4", z); } catch (Exception) { // error handling/compensation goes here } }
  5. public static async Task Run(DurableOrchestrationContext ctx) { var parallelTasks =

    new List<Task<int>>(); // get a list of N work items to process in parallel object[] workBatch = await ctx.CallFunctionAsync<object[]>("F1"); for (int i = 0; i < workBatch.Length; i++) { Task<int> task = ctx.CallFunctionAsync<int>("F2", workBatch[i]); parallelTasks.Add(task); } await Task.WhenAll(parallelTasks); // aggregate all N outputs and send result to F3 int sum = parallelTasks.Sum(t => t.Result); await ctx.CallFunctionAsync("F3", sum); } 'BOPVU 'BOJO
  6. "TZOD)551"1*T public static async Task<HttpResponseMessage> Run( HttpRequestMessage req, DurableOrchestrationClient starter,

    string functionName, TraceWriter log) { // Function name comes from the request URL. // Function input comes from the request content. dynamic eventData = await req.Content.ReadAsAsync<object>(); string instanceId = await starter.StartNewAsync(functionName, eventData); log.Info($"Started orchestration with ID = '{instanceId}'."); return starter.CreateCheckStatusResponse(req, instanceId); }
  7. -JHIUXFJHIU"DUPST public static async Task Run(DurableOrchestrationContext ctx) { int counterState

    = ctx.GetInput<int>(); string operation = await ctx.WaitForExternalEvent<string>("operation"); if (operation == "incr") { counterState++; } else if (operation == "decr") { counterState--; } ctx.ContinueAsNew(counterState); }
  8. )VNBO*OUFSBDUJPOBOE5JNFPVUT public static async Task Run(DurableOrchestrationContext ctx) { await ctx.CallFunctionAsync("RequestApproval");

    using (var timeoutCts = new CancellationTokenSource()) { DateTime dueTime = ctx.CurrentUtcDateTime.AddHours(72); Task durableTimeout = ctx.CreateTimer(dueTime, timeoutCts.Token); Task<bool> approvalEvent = ctx.WaitForExternalEvent<bool>("ApprovalEvent"); if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout)) { timeoutCts.Cancel(); await ctx.CallFunctionAsync("HandleApproval", approvalEvent.Result); } else { await ctx.CallFunctionAsync("Escalate"); } } }
  9. 'VODTJPOͷߏ੒  ᵓᴷᴷIPTUKTPO ᵓᴷᴷGVOD ᴹᵓᴷᴷGVODUJPOKTPO ᴹᵋᴷᴷSVOQIQ ᵋᴷᴷGVOD ᵓᴷᴷGVODUJPOKTPO ᵋᴷᴷSVOQIQ EJSFDUPSJFT

    pMFT 'VODUJPOຖʹσΟϨΫτϦ τϦΨʔɾόΠϯσΟϯάͳͲͷఆٛ 'VODUJPOͷΤϯτϦϙΠϯτ ϥϯλΠϜݻ༗ͷߏ੒ IUUQTEPDTNJDSPTPGUDPNKBKQB[VSFB[VSFGVODUJPOTGVODUJPOTUSJHHFSTCJOEJOHT
  10. αϯϓϧ'VODUJPOͷߏ੒ . ├── composer.json ├── composer.lock ├── slackbot │ ├──

    function.json │ └── run.php ├── src │ ├── Functions │ │ └── SlackBot.php │ └── Lib │ └── AzureFunction.php ├── tests │ ├── Lib │ │ └── TestCase.php │ └── TestCase │ └── Functions │ └── SlackBotTest.php ├── tmp └── vendor ├── autoload.php … 'VODUJPOͷσΟϨΫτϦ τϦΨʔɾόΠϯσΟϯάͳͲͷఆٛ 'VODUJPOͷΤϯτϦϙΠϯτ 'VODUJPOຊମ ؆୯ͳϥούʔΫϥε ςετΫϥε IUUQTHJUIVCDPNLB[QIQDPOLBOTBJB[VSFGVODUJPO
  11. GVODUJPOKTPO { "bindings": [ { "type": "http", "direction": "out", "name":

    "res" }, { "type": "httpTrigger", "name": "req", "authLevel": "function", "methods": [ "post" ], "direction": "in" } ], "disabled": false }
  12. SVOQIQ <?php require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR

    . 'autoload.php'; use App\Functions\SlackBot; $proc = new SlackBot(); $proc->run();
  13. 4MBDL#PUQIQ <?php namespace App\Functions; use App\Lib\AzureFunction; class SlackBot extends AzureFunction

    { public function __construct($req = 'req', $res = 'res') { $this->init(getenv($req), getenv($res)); } … }
  14. 4MBDL#PUQIQ public function run() { $params = $this->httpParams(); if (!isset($params['user_name'])

    || $params['user_name'] === 'slackbot') { return; } $response = [ 'text' => '', 'username' => 'phpconkansai2017demo', 'icon_emoji' => ':phpkansai:', 'attachments' => [[ "color" => '#00BFFF', // blue "title" => 'ϝοηʔδΛड৴͠·ͨ͠', "fields" => [[ … ]] ]] ]; $this->writeResponse(json_encode($response)); }
  15. 4-BDL#PU5FTUQIQ /** * @test */ public function ௨ৗͷॻ͖ࠐΈͷςετ() { $params

    = [ 'token' => 'DUMMY', 'user_name' => 'userA', 'text' => 'Hello Azure Function' ]; $this->makeHttpRequest($params); $this->makeResponse(); …
  16. 4-BDL#PU5FTUQIQ $this->slackBot = new SlackBot(); $this->slackBot->run(); $result = json_decode($this->getResponse(), true);

    $expected = [ 'username' => 'phpconkansai2017demo', 'icon_emoji' => ':phpkansai:', 'text' => '', 'attachments' => [[ "color" => '#00BFFF', "title" => 'ϝοηʔδΛड৴͠·ͨ͠', "fields" => [[ // লུ ]] ]] ]; $this->assertEquals($expected, $result); }