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

世界一わかりみの深いDurable Functions/wakarimi_durablefunctions

Noriyuki TAKEI
February 03, 2022

世界一わかりみの深いDurable Functions/wakarimi_durablefunctions

Noriyuki TAKEI

February 03, 2022
Tweet

More Decks by Noriyuki TAKEI

Other Decks in Technology

Transcript

  1. BCPVUNF Noriyuki TAKEI ෢Ҫ ٓߦ Information • サイオステクノロジー株式会社 • Microsoft

    MVP for Azure Favorites • Azure • Squash • Sweets • Running blog https://tech-lab.sios.jp/ core skill Container、Cloud Native、 Serverless全般 Twitter @noriyukitakei
  2. Azure Functionsとは? サーバーが不要!! before • 仮想マシン作成 • OSインストール • ミドルウェア(Apache

    等)インストール • 性能検証 • コーディング after ブラウザ上、もしくは専 ⽤の開発環境でコードを ⼊⼒して保存するだ け︕︕ アプリ開発の⼿間を⼤幅に削減︕︕
  3. Azure Functionsとは? 必要に応じてスケール︕︕ before • 負荷テストツール⽤意 • テストシナリオ作成 • テスト実施

    • チューニング • 結果報告書作成 after 特別な設定は不要︕︕負 荷に応じてほぼ無限にス ケール︕︕ 負荷に応じて無限にスケールします。
  4. Azure Functionsとは? 使った分だけ課⾦︕︕ before 仮想マシンの場合、アプリ ケーションが動いてなくて も、仮想マシンが起動して いる時間だけ課⾦が発⽣す る。 after

    アプリケーションが動い た時間だけしか課⾦され ないので、仮想マシンと ⽐べて料⾦が抑えられる。 使った分だけ課⾦します。
  5. Azure Functionsでこんなアプリを作ってみたい グループA グループB グループA ユーザーA グループB ユーザーB … Azure

    FunctionsでAzure Active Directoryにグループ を作成する。 … 先程作成したグループに Azure Functionsでユーザー を追加する。 グループの追加が全て完了したら・・・
  6. Azure Functionsでこんなアプリを作ってみたい グループA グループB グループA ユーザーA グループB ユーザーB … Azure

    FunctionsでAzure Active Directoryにグループ を作成する。 … 先程作成したグループに Azure Functionsでユーザー を追加する。 ここが 難しい!! グループの追加が全て完了したら・・・
  7. nori “Hello” + $x Hello nori $x + “. How

    are you?” Hello nori. How are you? sayHello関数 sayHowAreYou関数 関数チェーン 関数チェーンは、特定の順序で関数のシーケンスを実⾏するパターンです。
  8. 関数チェーン(実装) 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; });
  9. ファンアウト/ファンイン(実装) 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]); });
  10. 人による操作(実装) 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 } });
  11. 必要なもの 集計対象のWebサイトのRSSタグを取得し、「Azure」という単語が含まれ る記事の合計数を算出します。このAzure Functionsは後述するAzure Queue Storageでトリガーされ、その結果は、これまた後述するAzure Table Storageに格納します Azure Functions

    先程ご紹介したAzure Functionsは、Azure Queue Storageでトリガーされ ます。そのメッセージが格納されるキューになります。 Azure Queue Storage 先程ご紹介したAzure Functionsは、Webサイトごとの集計結果を、Azure のキーバリューストアであるAzure Table Storageに返します。 Azure Table Storage Azure Queue Storageにメッセージをプッシュしたり、Azure Table Storageに格納された結果を集計したりするアプリケーションです。 C#で書いた アプリ
  12. Azure Queue Storage Azure Table Storage Azure Functions C#で書いた アプリ

    オーケストレーター関数で 置き換え アクティビティ関数で 置き換え ✗ ✗ 自分で作る必要 なし(実際は裏で 動いている) 自分で作る必要 なし(実際は裏で 動いている)
  13. クライント関数 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); };
  14. オーケストレーター関数 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; i<urls.length; i++){ tasks.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); });
  15. アクティビティ関数 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; };
  16. 今回の主要な登場人物 オーケストレーター関数を起動するための関数です。この関数 を起動するトリガーはAzure Functionsで利⽤する全てものも を利⽤できます。 クライアント 関数 アクティビティ関数を起動するための関数です。アクティビ ティ関数のオーケストレーター的な役割をします。例えば、ア クティビティ関数の結果を受け取って、それを他のアクティビ

    ティ関数に渡したりとか、全てのアクティビティ関数の終了を 待ってから処理を⾏うとか、⾊々な制御ができます。オーケス トレーター関数では実際の処理は⾏いません。あくまで、アク ティビティ関数のコントロールです。 オーケストレーター 関数 実際の処理を⾏う関数です。 アクティビティ 関数
  17. イベントソーシング 順番 編集履歴 1 「こんにちは、たけいさん、ごきげん、い かが」と投稿する。 2 「たけいさん」の部分を「やまださん」と 編集する。 3

    「ごきげん、いかが」の部分を「ごきげん 、うるわしゅう」と編集する。 4 「ごきげん、うるわしゅう」の部分を削除 する。 アプリケーションの開発手法の一つで、発生 したイベントを逐一記録します。 最新の状態を取得するためには、そのイベン トを順次実行(リプレイ)します。 任意の時点に復元できるというメリットはあ りますが、リプレイの仕組みを作成したり、 リプレイ処理自体に一定の負荷がかかるなど デメリットもあります。 こんにちは、 やまださん ① イベントを逐一記録 していく。 ② 今の投稿の最新の状態を取 得するために、順番に全てのイ ベントを実施する。 ③ これが最新の状態と なる。
  18. イベントソーシング int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2 =

    await context.CallActivityAsync<int>("MultiplyBy2", result1); int result3 = await context.CallActivityAsync<int>("Plus2", result2); int result4 = await context.CallActivityAsync<int>("MultiplyBy2", result3); 順番 実行関数 結果 戻り値 1 Plus2 成功 4 2 MultiplyBy2 成功 8 3 Plus2 成功 10 Durable Functionsでは、アクティビティ関数が実行されるごとに、その関数名、結果、戻り値を記録 していきます。
  19. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 クライアント関数は、オーケス トレーター関数を起動するため に、Control Queueにメッセージ をプッシュします。 Message
  20. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 Message オーケストレーター関数 は、Control Queueのキュ ートリガーにより起動し ます。
  21. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 オーケストレーター関数が起動 して、Plus2というアクティビテ ィ関数を実行します。
  22. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted アクティビティ関数 オーケストレーター関数はHistory Tableに、オ ーケストレーター関数が起動したことを表す 「ExecutionStarted」と「OrchestratorStarted」 を記録します。前者は最初だけ、後者はリプ レイ開始のたびに記録されます。
  23. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted アクティビティ関数 オーケストレーター関数はアクテ ィビティ関数を起動するために Work Item Queueにメッセージを プッシュします。 Message
  24. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted アクティビティ関数 オーケストレーター関数はHistory Tableに、アクテ ィビティ関数の起動予約したことを表す「 TaskScheduled」と、リプレイが終わったことを表 す「OrchestratorCompleted」を記録します。ここで 一旦オーケストレーター関数は終了します。 Message
  25. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted アクティビティ関数 Message アクティビティ関数は、Work Item Queueのキュートリガーにより起動 します。
  26. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 アクティビティ関数 アクティビティ関数はHistory Tableに、タスクが完 了したことを表す「TaskCompleted」を記録します 。また、アクティビティ関数の結果をResultに記録 します。
  27. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 Message アクティビティ関数はオーケスト レーター関数を起動するために Control Queueにメッセージをプッ シュします。
  28. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 Message オーケストレーター関数 は、Control Queueのキュ ートリガーにより起動し ます。
  29. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted アクティビティ関数はHistory Tableに、リプレイが 開始したことを表す「OrchestratorStarted」を記録 します。
  30. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted アクティビティ関数Plus2を起動しようとしますが、History Table には既に実行完了である記録があるため、実行をスキップして Resultに記録されている4という結果だけを返します。
  31. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted アクティビティ関数MultiplyBy2を起 動するために、Work Item Queueに メッセージをプッシュします。 Message
  32. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted オーケストレーター関数はHistory Tableに、アクテ ィビティ関数の起動予約したことを表す「 TaskScheduled」と、リプレイが終わったことを表 す「OrchestratorCompleted」を記録します。ここで 一旦オーケストレーター関数は終了します。 Message
  33. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted Message アクティビティ関数は、Work Item Queueのキュートリガーにより起動 します。
  34. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 アクティビティ関数はHistory Tableに、タスクが完 了したことを表す「TaskCompleted」を記録します 。また、アクティビティ関数の結果をResultに記録 します。
  35. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 Message アクティビティ関数はオーケスト レーター関数を起動するために Control Queueにメッセージをプッ シュします。
  36. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 Message オーケストレーター関数 は、Control Queueのキュ ートリガーにより起動し ます。
  37. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 アクティビティ関数Plus2を起動しようとしますが、History Table には既に実行完了である記録があるため、実行をスキップして Resultに記録されている4という結果だけを返します。
  38. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 アクティビティ関数MultiplyBy2を起動しようとしますが、 History Tableには既に実行完了である記録があるため、実行をス キップしてResultに記録されている8という結果だけを返します 。
  39. オーケストレーター 関数 int result1 = await context.CallActivityAsync<int>("Plus2", num); int result2

    = await context.CallActivityAsync<int>("MultiplyBy2", result1); Control Queue クライアント関数 Work Item Queue DurableFunctions HubHistory アクティビティ関数 EventType Name Input Result ExecutionStarted Orchastrator 2 OrchestratorStarted TaskScheduled Plus2 OrchestratorCompleted TaskCompleted 4 OrchestratorStarted TaskScheduled MultiplyBy2 OrchestratorCompleted TaskCompleted 8 ExecutionCompleted オーケストレーター関数は、実行が終了したこと を表す「ExecutionCompleted」を記録し、終わりま す。。。