Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPでもserverless framework!?

Avatar for Kaz Watanabe Kaz Watanabe
October 09, 2017

PHPでもserverless framework!?

Avatar for Kaz Watanabe

Kaz Watanabe

October 09, 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 7474$PEFΛ࢖ͬͯͷσόοά ϩʔΧϧɾϦϞʔτʣ

    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. 'VODUJPOͷߏ੒  ᵓᴷᴷ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); }