最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。
どこまで続くかわからないお勉強メモ。今日は6回目です。今回はBlob Storage出力バインドを使った実装をSpring Cloud Functionを使って実装しました。過去のものは以下を参照。
- Azure Functionsのお勉強メモ(1)TypeScriptでチュートリアルを実施する - miyohide's blog
- Azure Functionsのお勉強メモ(2)Spring Cloud FunctionでHTTP Triggerを実装する - miyohide's blog
- Azure Functionsのお勉強メモ(3)Spring Cloud FunctionでTimer Triggerを実装する - miyohide's blog
- Azure Functionsのお勉強メモ(4)RubyでHTTP Triggerを実装する - miyohide's blog
- Azure Functionsのお勉強メモ(5)カスタムハンドラーを使ってRubyでTimer Triggerを実装する - miyohide's blog
概要
これまでは主にFunctionsが動く契機となるTriggerを取り上げていましたが、Azure Functionsで抑えておきたい概念としてバインドという概念もあります。詳細は以下のドキュメントに書かれています。
ものすごく単純に言えば、予め定義しておいた入出力の設定といった感じでしょう。
バインドには色々なものがありますが、ここではBlob Storage出力バインドを使ってみます。
実装
環境
Blob Storageにファイルを出力するために、開発環境にAzure StorageエミュレーターであるAzuriteを使います。
Dockerイメージが用意されているので、今回はそれを使います。以下のようなdocker-compose.yml
ファイルを作成しておくと良いかと思います。
version: '3' services: storage: image: mcr.microsoft.com/azure-storage/azurite ports: - 10000:10000 - 10001:10001 environment: AZURITE_ACCOUNTS: "a1:k1;a2:k2"
上記の例ではアカウントキーを短いものにしていますが、今後の動作に不具合が生じることがあったので、実際には長い文字列(azuriteのデフォルトアクセスキーでOK)を指定した方が良いかと思います。
なお、本記事執筆時の最新バージョンであるAzure Storage Explorerの1.18.1には以下のIssueのようにカスタムアカウントへの接続に問題が発生するので、利用される場合は注意が必要です。
Functions
Azure Functionsの中身を実装してみます。以下のドキュメントを参考にしました。
接続先を@StorageAccount
で指定し、出力パスを@BlobOutput
のpath
属性で指定します。こんな感じで実装しました。
@FunctionName("hello") @StorageAccount("OutputStorage") public HttpResponseMessage hello( @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS)HttpRequestMessage<String> request, @BlobOutput(name = "target", path = "myblob/sample.txt") OutputBinding<String> outputItem, ExecutionContext context ) { // 処理内容 }
注意点は以下2点。
@StorageAccount
で指定するのはDefaultEndpointsProtocol
からはじまる接続文字列ではなく、環境変数名。ローカル実行の場合はlocal.settings.json
にキーと値を設定する。- Blobには予めコンテナ(上記の例では
myblob
)を作成しておく。
上記1.について@StorageAccount
でDefaultEndpointsProtocol
からはじまる接続文字列を記述した場合、起動時に
Warning: Cannot find value named 'DefaultEndpointsProtocol=http;AccountName=a1(省略)QueueEndpoint=http://127.0.0.1:10001/a1;' in local.settings.json that matches 'connection' property set on 'blob' in '/xxxxx/function.json'. You can run 'func azure functionapp fetch-app-settings <functionAppName>' or specify a connection string in local.settings.json.
という文字列が出力され、Functions実行時に
[2021-04-18T06:19:39.526Z] System.Private.CoreLib: Exception while executing function: Functions.hello. Microsoft.Azure.WebJobs.Host: Storage account connection string 'AzureWebJobsDefaultEndpointsProtocol=http;AccountName=a1(省略)QueueEndpoint=http://127.0.0.1:10001/a1;' does not exist. Make sure that it is a defined App Setting.
と出力されます。
出力バインドに値を出力するには、setValue
メソッドを使えば良さそうです。OutputBinding
インターフェースには他にはgetValue
メソッドしかないので、出力するにはsetValue
メソッド一択でしょう。
こんな感じで実装しました。
public class HelloHandler extends FunctionInvoker<String, String> { @FunctionName("hello") @StorageAccount("OutputStorage") public HttpResponseMessage hello( @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS)HttpRequestMessage<String> request, @BlobOutput(name = "target", path = "myblob/sample.txt") OutputBinding<String> outputItem, ExecutionContext context ) { context.getLogger().info("***** HTTP Trigger Start *****"); outputItem.setValue("[" + LocalDateTime.now() + "] This is sample txt."); context.getLogger().info("***** HTTP Trigger End *****"); return request.createResponseBuilder(HttpStatus.OK) .body("***** HTTP Trigger Response *****") .header("Content-Type", "application/json") .build(); } }
実行
今回はHTTP Triggerで実装しました。gradlew azureFunctionsRun
でアプリを実行し、localhost:7071/api/hello
にアクセスすると、以下の画面が出力されます。
Blobを参照してみると、ファイルが作られていることがわかります。
中身も意図したものが作成されています。
再度localhost:7071/api/hello
にアクセスすると、ファイルが上書きされていました。