himanago

Azure・C#などのMS系技術やLINE関連技術など、好きな技術について書くブログ

【Azure Functions】Durable Entity を使った「リプライトークンリレー」で LINE Bot の返信処理を拡張する

はじめに

LINE Bot では、無料で使うことのできる返信(Reply API)を効果的に使うことが求められます。

今回は、その返信をより柔軟に・効果的に拡張するアイディアを紹介します。

リプライトークンの有効期限が長くなった

ここ数年で LINE Messaging API による Bot 開発も大きく変わっていている点の一つに、リプライトークンの有効期限が大幅に延長されたことが挙げられます。

Messaging API では、Webhook で受信したメッセージに返信する際の Reply API では、受信メッセージに含まれるリプライトークンを使用する必要があります。

このトークンは従来は有効期限が短く、30 秒ほどで使えなくなってしまっており、時間のかかる処理では使えなかったほか、サーバーレスで組む際などにコールドスタートに悩まされることもあったかと思います。

これが現在では、ドキュメントを見ると

Webhookを受信してから1分以内に使用する必要があります。1分を超える場合の使用については、動作は保証されません。

という記載になっており、実は数分はトークンが有効のままでいてくれるため、ある程度時間のかかる処理でも、終わってから返信をする余裕ができました。

参考:Messaging APIリファレンス | LINE Developers

返信の入れ違いの問題

一方で、時間のかかる処理を通常の返信で対応しようとすると新しい課題も浮上してきます。

処理中にユーザーから新たに別のメッセージが送られてしまうと、返信が入れ違ってしまいます。

時間のかかる処理を連続でリクエストされてしまっては、サーバー側の負荷も気になるところですし、ものによってはおかしな動作をする可能性もあると思います。

そこで、返信の入れ違いを防ぐために、時間のかかる処理中の場合は新たなリクエストを受け付けないようにトークンをうまく使って返信をコントロールしてみる方法を考えてみます。

Durable Entity を利用したトークン管理

今回考えたのは、Durable Entity をトークンストアとして活用し、時間のかかる処理に対するリプライトークンをいったんストアし、それをリレーして引き継ぎながら時間のかかる処理を返信したかどうかを管理する方法です。

まず Durable Entity についてですが、これは Azure Functions の拡張機能である Durable Functions の 1 機能で、状態を保持するための仕組みです。

「エンティティ ID」という値をキーにエンティティを作成し、そのエンティティ内に状態を保持しておくことができます。

learn.microsoft.com

処理の流れ

今回やりたいのはこういう流れです。

「猫の絵を描いて」というリクエストに対する処理が終わるまで、別のリクエストは受け付けず、「待ってね」と返します。

Durable Entity を用いて、これを実現します。

全体像はこんな感じ。

通常のフローとしては、① 時間のかかる処理の依頼メッセージを受け取った Webhook エンドポイントの関数は、② 依頼したLINEユーザーの LINE ユーザー ID をエンティティ ID(エンティティの識別子)として起動し、そこにリプライトークンをセットします。

その後 Webhook エンドポイントは ③ 外部の時間のかかる処理本体を呼び出します。単純に外部 API をコールするほか、人への作業・承認依頼や、何かデバイスなどに指示を出すような IoT シナリオが想定されます。

時間のかかる処理が終わったら、そこから ④ 返信用関数をコールしてもらいます。

その返信用関数から、処理を依頼している LINE ユーザー ID のエンティティを確認し、⑤ 保存されているリプライトークンを取得し、エンティティ内からリプライトークンを削除します。

このような動きを実現するために、③ → ④ の経路で処理を依頼している LINE ユーザー ID の情報は引き渡していく必要があります。

最後に、⑥ ⑤ で取得したリプライトークンで結果を返信すれば完了です。

これを基本の流れとするのですが、時間のかかる処理完了前に次の依頼メッセージを送ってしまった時の対策フローは以下です。

処理が完了する前に新たに ① 時間のかかる処理の依頼メッセージを送った場合、② すでに同じLINEユーザーIDのエンティティが存在し、かつ未使用のリプライトークンを持っていれば「処理中」と判断し、③ エンティティにセットされていたリプライトークンを受け取ります。

次に ④ ①で受け取った新しいリプライトークンを代わりにエンティティにセットしておきます。(リプライトークンを入れ替える「リレー」を行っています)

最後に ⑤ セットされていたリプライトークンで、処理中である旨の返信を行う、という流れです。

この方法により、時間のかかる処理の完了 / 未完了をエンティティがリプライトークンを持っているかどうかで判断できます。

さらに、処理中に再リクエストした際にリプライトークンを入れ替えて処理中である旨の返信をすることで、リプライトークンの有効期限を実質的に伸ばす副次的なメリットも得られます。

まとめ

この方法を使えば、時間のかかる処理に対して、返信の入れ違いを防ぎながら、リプライトークンの有効期限を有効に使うことができます。

この仕組みを実装し、ちょっとした便利ツールを開発して使っているので、次回はサンプルコードも交えてその実例の紹介もしたいと思います。

ハンズオンイベントやります

3/28(木) の夜にオンラインでハンズオンイベントをやります!

linedevelopercommunity.connpass.com

今回紹介した「リプライトークンリレー」の動きも実際に体験できるので、気になった方はぜひ!