View Slide
BCPVUNFNoriyuki TAKEIҪ ٓߦInformation• サイオステクノロジー株式会社• Microsoft MVP for AzureFavorites• Azure• Squash• Sweets• Runningbloghttps://tech-lab.sios.jp/ core skillContainer、Cloud Native、Serverless全般Twitter@noriyukitakei
技術ブログ「SIOS Tech.Lab」多分わかりやすいDurable Functionshttps://tech-lab.sios.jp/archives/12991
用語解説Durable Functionsとは?オーケストレーター関数アクティビティ関数アクティビティ関数アクティビティ関数アクティビティ関数アクティビティ関数アクティビティ関数
Durable Functionsとは?ฏͨ͘ݴ͏ͱɺɺɺ"[VSF'VODUJPOT͚ͩͰ࣮ݱ͠Α͏ͱ͢Δͱɺͷ͘͢͝ෳࡶͳίʔυʹͳͬͯ͠·͏Α͏ͳෳࡶͳॲཧ͕ɺ%VSBCMF'VODUJPOTΛ͏ͱɺ͍͘͢͝ίʔυͰ؆୯ʹ࣮ݱͰ͖Δɻ͔͠αʔόʔϨεͰɻ
Azure Functionsとは?サーバーが不要!!before• 仮想マシン作成• OSインストール• ミドルウェア(Apache等)インストール• 性能検証• コーディングafterブラウザ上、もしくは専⽤の開発環境でコードを⼊⼒して保存するだけ︕︕アプリ開発の⼿間を⼤幅に削減︕︕
Azure Functionsとは?必要に応じてスケール︕︕before• 負荷テストツール⽤意• テストシナリオ作成• テスト実施• チューニング• 結果報告書作成after特別な設定は不要︕︕負荷に応じてほぼ無限にスケール︕︕負荷に応じて無限にスケールします。
Azure Functionsとは?使った分だけ課⾦︕︕before仮想マシンの場合、アプリケーションが動いてなくても、仮想マシンが起動している時間だけ課⾦が発⽣する。afterアプリケーションが動いた時間だけしか課⾦されないので、仮想マシンと⽐べて料⾦が抑えられる。使った分だけ課⾦します。
Azure Functionsでこんなアプリを作ってみたいグループAグループBグループAユーザーAグループBユーザーB…Azure FunctionsでAzureActive Directoryにグループを作成する。…先程作成したグループにAzure Functionsでユーザーを追加する。グループの追加が全て完了したら・・・
Azure Functionsでこんなアプリを作ってみたいグループAグループBグループAユーザーAグループBユーザーB…Azure FunctionsでAzureActive Directoryにグループを作成する。…先程作成したグループにAzure Functionsでユーザーを追加する。ここが難しい!!グループの追加が全て完了したら・・・
DurableFunctionsが解決!!
前のAzure Functionsが終わるのを待って、次のAzure Functionsを起動できる。並列実⾏しているAzure Functionsが全て終わるのを待ってから、次のAzure Functionsを起動できる。XX時間内に外部からのイベントがない場合は、別の処理を⾏うなんてこともできる。Durable Functionsでは、こんなことができます!!
© SIOS Technology Inc. All rights Reserved.Durable Functionsの実装パターン14
nori“Hello” + $xHello nori$x + “. How are you?”Hello nori.How areyou?sayHello関数 sayHowAreYou関数関数チェーン関数チェーンは、特定の順序で関数のシーケンスを実⾏するパターンです。
関数チェーン(実装)const df = require("durable-functions");module.exports = df.orchestrator(function*(ctx) {const x = yield ctx.df.callActivity(“sayHello”,”nori”);const y = yield ctx.df.callActivity(“sayHowAreYou”, x);return y;});
ファンアウト/ファンインファンアウト/ファンインは、複数の関数を並列に実⾏してすべてが完了するまで待機するパターンです。favoriteFruits関数favoriteVegetables関数appletomatoI like antomat andan apple
ファンアウト/ファンイン(実装)const df = require("durable-functions");module.exports = df.orchestrator(function* (ctx) {const parallelTasks = [];parallelTasks.push(ctx.df.callActivity("favoriteFruits"));parallelTasks.push(ctx.df.callActivity("favoriteVegetables"));const results = yield ctx.df.Task.all(parallelTasks);ctx.log('I like an ' + results[0] + ' and an ' + results[1]);});
人による操作ある関数を実⾏してから、⼈による操作(承認⾏為など)などがなされてから初めて次の関数を実⾏します。
人による操作(実装)const df = require("durable-functions");const moment = require('moment');module.exports = df.orchestrator(function* (context) {yield context.df.callActivity("gatherCode");const expiration = moment.utc(context.df.currentUtcDateTime).add(90, 's');const timeoutTask = context.df.createTimer(expiration.toDate());const challengeResponseTask = context.df.waitForExternalEvent("Response");const winner = yield context.df.Task.any([challengeResponseTask, timeoutTask]);if (winner === challengeResponseTask) {// Do Smething}});
© SIOS Technology Inc. All rights Reserved.Durable Functionsの構成21
オーケストレーター関数を起動するための関数です。この関数を起動するトリガーはAzure Functionsで利⽤する全てものもを利⽤できます。クライアント関数アクティビティ関数を起動するための関数です。アクティビティ関数のオーケストレーター的な役割をします。例えば、アクティビティ関数の結果を受け取って、それを他のアクティビティ関数に渡したりとか、全てのアクティビティ関数の終了を待ってから処理を⾏うとか、⾊々な制御ができます。オーケストレーター関数では実際の処理は⾏いません。あくまで、アクティビティ関数のコントロールです。オーケストレーター関数実際の処理を⾏う関数です。アクティビティ関数Durable Functionsの構成
Durable Functionsの構成
以下の要件のDurable Functionsを考えてみよう!!「URLのクエリパラメーターnumに指定された数字に2を⾜し、さらにその数字に2をかける処理を実⾏する。」という簡単な要件をDurableFunctionsで実⾏してみる。
HTTPトリガーで起動し、クエリパラメーターnumに指定された数字をオーケストレーター関数に渡す。クライアント関数クライアント関数から渡された数字を、アクティビティ関数1(後述)の引数に渡して実⾏し、さらにその結果をアクティビティ関数2(後述)の引数に渡して実⾏する。オーケストレーター関数引数に渡された数字に2を⾜すアクティビティ関数1と、引数に渡された数字に2をかけるアクティビティ関数2の2つの関数から構成される。アクティビティ関数Durable Functionsの構成
© SIOS Technology Inc. All rights Reserved.Durable Functionsを動かしてみよう27
以下のユースケースを想定します。指定された複数のWebサイトのRSSタグを取得して、「Azure」というタグが含まれる記事の合計件数を算出する。各WebサイトのRSSタグの取得・合計件数の算出は並列で実⾏する。
「Azure」というタグの抽出対象に利⽤させて頂くWebサイトは以下の2つです。サイオステクノロジー技術ブログ「SIOS Tech.Lab」 Azureの更新情報
Durable Functionsを使わない場合
必要なもの集計対象のWebサイトのRSSタグを取得し、「Azure」という単語が含まれる記事の合計数を算出します。このAzure Functionsは後述するAzureQueue Storageでトリガーされ、その結果は、これまた後述するAzureTable Storageに格納しますAzureFunctions先程ご紹介したAzure Functionsは、Azure Queue Storageでトリガーされます。そのメッセージが格納されるキューになります。AzureQueue Storage先程ご紹介したAzure Functionsは、Webサイトごとの集計結果を、AzureのキーバリューストアであるAzure Table Storageに返します。Azure TableStorageAzure Queue Storageにメッセージをプッシュしたり、Azure TableStorageに格納された結果を集計したりするアプリケーションです。C#で書いたアプリ
Durable Functionsを使わない場合は仕組みがマジめんどくさい
Durable Functionsを使う場合
Azure Queue StorageAzure Table StorageAzure FunctionsC#で書いたアプリオーケストレーター関数で置き換えアクティビティ関数で置き換え✗✗自分で作る必要なし(実際は裏で動いている)自分で作る必要なし(実際は裏で動いている)
必要なものHTTPトリガーで起動し、オーケストレーター関数を起動します。クライアント関数指定された複数のWebサイトのURLをアクティビティ関数の引数に渡して、合計数を取得し、それらを全て⾜し合わせる。オーケストレーター関数集計対象のWebサイトのRSSタグを取得し、「Azure」という単語が含まれる記事の合計数を算出します。アクティビティ関数
クライント関数const df = require("durable-functions");module.exports = async function (context, req) {const client = df.getClient(context);const instanceId = await client.startNew(req.params.functionName, undefined, req.body);context.log(`Started orchestration with ID = '${instanceId}'.`);return client.createCheckStatusResponse(context.bindingData.req, instanceId);};
オーケストレーター関数const df = require("durable-functions");module.exports = df.orchestrator(function* (context) {const tasks = [];const urls = ["https://tech-lab.sios.jp/feed/","https://azurecomcdn.azureedge.net/ja-jp/updates/feed/"];for(let i = 0; itasks.push(context.df.callActivity("ActivityCountBlog", urls[i]));}const results = yield context.df.Task.all(tasks);const sum = results.reduce((prev, curr) => prev + curr, 0);context.log("result:" + sum);});
アクティビティ関数const Parser = require('rss-parser');module.exports = async function (context, RSS) {const parser = new Parser();let cnt = 0;const feed = await parser.parseURL(RSS);feed.items.forEach(function (item) {if (item.content.indexOf('Azure') != -1)cnt++;});return cnt;};
© SIOS Technology Inc. All rights Reserved.Durable Functionsの構成要素とイベントソーシングについて44
今回の主要な登場人物オーケストレーター関数を起動するための関数です。この関数を起動するトリガーはAzure Functionsで利⽤する全てものもを利⽤できます。クライアント関数アクティビティ関数を起動するための関数です。アクティビティ関数のオーケストレーター的な役割をします。例えば、アクティビティ関数の結果を受け取って、それを他のアクティビティ関数に渡したりとか、全てのアクティビティ関数の終了を待ってから処理を⾏うとか、⾊々な制御ができます。オーケストレーター関数では実際の処理は⾏いません。あくまで、アクティビティ関数のコントロールです。オーケストレーター関数実際の処理を⾏う関数です。アクティビティ関数
Durable Functionsの実⾏履歴を記録するAzure TableStorageである。オーケストレーター関数やアクティビティ関数の実⾏開始終了、出⼒した結果などを逐⼀記録する。History Table今回の主要な登場人物オーケストレーター関数を起動するためのキューであり、オーケストレーター関数はControl Queueのキュートリガーで動作する。Control Queueアクティビティ関数を起動するためのキューであり、アクティビティ関数はWork Item Queueのキュートリガーで動作する。Work ItemQueue
Durable Functionsの半分はイベントソーシングでできています。
イベントソーシング順番 編集履歴1 「こんにちは、たけいさん、ごきげん、いかが」と投稿する。2 「たけいさん」の部分を「やまださん」と編集する。3 「ごきげん、いかが」の部分を「ごきげん、うるわしゅう」と編集する。4 「ごきげん、うるわしゅう」の部分を削除する。アプリケーションの開発手法の一つで、発生したイベントを逐一記録します。最新の状態を取得するためには、そのイベントを順次実行(リプレイ)します。任意の時点に復元できるというメリットはありますが、リプレイの仕組みを作成したり、リプレイ処理自体に一定の負荷がかかるなどデメリットもあります。こんにちは、やまださん① イベントを逐一記録していく。② 今の投稿の最新の状態を取得するために、順番に全てのイベントを実施する。③ これが最新の状態となる。
イベントソーシングint result1= await context.CallActivityAsync("Plus2", num);int result2= await context.CallActivityAsync("MultiplyBy2", result1);int result3= await context.CallActivityAsync("Plus2", result2);int result4= await context.CallActivityAsync("MultiplyBy2", result3);順番 実行関数 結果 戻り値1 Plus2 成功 42 MultiplyBy2 成功 83 Plus2 成功 10Durable Functionsでは、アクティビティ関数が実行されるごとに、その関数名、結果、戻り値を記録していきます。
© SIOS Technology Inc. All rights Reserved.Durable Functionsはどのように動いているのか?50
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数クライアント関数は、オーケストレーター関数を起動するために、Control Queueにメッセージをプッシュします。Message
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数Messageオーケストレーター関数は、Control Queueのキュートリガーにより起動します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数オーケストレーター関数が起動して、Plus2というアクティビティ関数を実行します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryEventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedアクティビティ関数オーケストレーター関数はHistory Tableに、オーケストレーター関数が起動したことを表す「ExecutionStarted」と「OrchestratorStarted」を記録します。前者は最初だけ、後者はリプレイ開始のたびに記録されます。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryEventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedアクティビティ関数オーケストレーター関数はアクティビティ関数を起動するためにWork Item Queueにメッセージをプッシュします。Message
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryEventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedアクティビティ関数オーケストレーター関数はHistory Tableに、アクティビティ関数の起動予約したことを表す「TaskScheduled」と、リプレイが終わったことを表す「OrchestratorCompleted」を記録します。ここで一旦オーケストレーター関数は終了します。Message
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryEventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedアクティビティ関数Messageアクティビティ関数は、Work ItemQueueのキュートリガーにより起動します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryEventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4アクティビティ関数アクティビティ関数はHistory Tableに、タスクが完了したことを表す「TaskCompleted」を記録します。また、アクティビティ関数の結果をResultに記録します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4Messageアクティビティ関数はオーケストレーター関数を起動するためにControl Queueにメッセージをプッシュします。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4Messageオーケストレーター関数は、Control Queueのキュートリガーにより起動します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedアクティビティ関数はHistory Tableに、リプレイが開始したことを表す「OrchestratorStarted」を記録します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedアクティビティ関数Plus2を起動しようとしますが、History Tableには既に実行完了である記録があるため、実行をスキップしてResultに記録されている4という結果だけを返します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedアクティビティ関数MultiplyBy2を起動するために、Work Item Queueにメッセージをプッシュします。Message
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedオーケストレーター関数はHistory Tableに、アクティビティ関数の起動予約したことを表す「TaskScheduled」と、リプレイが終わったことを表す「OrchestratorCompleted」を記録します。ここで一旦オーケストレーター関数は終了します。Message
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedMessageアクティビティ関数は、Work ItemQueueのキュートリガーにより起動します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8アクティビティ関数はHistory Tableに、タスクが完了したことを表す「TaskCompleted」を記録します。また、アクティビティ関数の結果をResultに記録します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8Messageアクティビティ関数はオーケストレーター関数を起動するためにControl Queueにメッセージをプッシュします。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8Messageオーケストレーター関数は、Control Queueのキュートリガーにより起動します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8アクティビティ関数Plus2を起動しようとしますが、History Tableには既に実行完了である記録があるため、実行をスキップしてResultに記録されている4という結果だけを返します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8アクティビティ関数MultiplyBy2を起動しようとしますが、History Tableには既に実行完了である記録があるため、実行をスキップしてResultに記録されている8という結果だけを返します。
オーケストレーター関数int result1 = await context.CallActivityAsync("Plus2", num);int result2 = await context.CallActivityAsync("MultiplyBy2", result1);Control Queueクライアント関数Work Item QueueDurableFunctionsHubHistoryアクティビティ関数EventType Name Input ResultExecutionStarted Orchastrator 2OrchestratorStartedTaskScheduled Plus2OrchestratorCompletedTaskCompleted 4OrchestratorStartedTaskScheduled MultiplyBy2OrchestratorCompletedTaskCompleted 8ExecutionCompletedオーケストレーター関数は、実行が終了したことを表す「ExecutionCompleted」を記録し、終わります。。。
SIOS Tech.Labhttps://tech-lab.sios.jp/世界⼀わかりみの深いクラウドネイティブon Azurehttps://youtube.com/playlist?list=PLbTt_DSTMYgGLUtZ0ewuBwhTBSZnNE2-w様々なメティアで情報発信しています!!技術ブログYouTube配信