Azure Web Appsお勉強メモ(1)Azure Web AppsにてRailsアプリを動かす(2021年6月版)

はじめに

2年ぐらい前にRailsアプリをAzure Web Appsで動かすと言うことをやったのですが、Azure Web Appsについて調べる必要性が出てきたので今の状態で動かしてみます。

miyohide.hatenablog.com

2021年6月時点でのAzure Web AppsにおけるRubyの対応

Azure Web AppsはRubyを動かすことができますが、2021年6月時点でRubyのバージョンは2.6。さすがに新しいバージョンで動かしたいものです。

f:id:miyohide:20210613172714p:plain

そこで、RailsアプリをDockerイメージ化して、それを動かすことにします。

RailsアプリのDockerイメージ化

RailsアプリのDockerイメージ化はいろんな情報が入り乱れていて分かりにくいのですが、以下の記事がとっかかりにはいいと感じました。

sinsoku.hatenablog.com

本番環境を作成するにはもう少しDockerイメージサイズを小さくする必要がありますが、とっかかりとしては十分かと思います。Dockerイメージを作成して、Docker Hubにpushしておけば準備完了です。

DockerイメージをAzure Web Appsで動かす

作成したDockerイメージをAzure Web Appsで動かすには、Azure Web Appsの作成時に対象のDockerイメージを指定すればOKです。

f:id:miyohide:20210613174227p:plain

ただ、作成した時点でDockerイメージをPullをするだけではなく、WebアプリにはじめてアクセスしたときにDockerイメージをpullします。このため、初回起動時には時間がかかります。プランにもよるかと思いますが、BasicのB1インスタンスで実行したところ、3分ぐらいかかりました。

f:id:miyohide:20210613174607p:plain
初回アクセス時

Dockerイメージのpullが終了時点

f:id:miyohide:20210613174649p:plain
Dockerイメージのpullが終了したとき

以上でAzure Web AppsでRailsアプリを動かすことができます。

ログを見る

アプリケーションを動かすときに、大事なことの一つがログです。ログがないとトラブル発生時にどのようなことが起きたのかすら分かりません。ログに関しては、以下のドキュメントがありますが、私は読んでもよく分かりませんでした。

docs.microsoft.com

実際にApp Service ログを有効化してみましたが、FTPでファイルを取らないとダメっぽくてちょっと現実的ではなさそうです。

f:id:miyohide:20210613175554p:plain

さらに調べてみると、プレビュー機能ではありますが診断設定を使うことでLog AnalyticsやAzure Storageにログを送ることができるようです。

azure.github.io

Azure Storageとの連携をやってみると...

f:id:miyohide:20210613175914p:plain

以下のような感じで出力されました。

f:id:miyohide:20210613175940p:plain

それぞれのコンテナを見てみると、深いディレクトリが作成され、PT1H.jsonというファイルが作成されていることが確認できました。

Azure Functionsのお勉強メモ(12)Azure Functionsのモニター機能の小ネタ

最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。

どこまで続くかわからないお勉強メモ。今日は12回目です。今回はAzure Functionsのモニター機能について小ネタ。過去のものは以下を参照。

あれ?回数がおかしい?

前回のブログ記事を書いていたとき、ふとAzure Functionsにある「モニター」を見ていると、表示されている回数がおかしいことに気がつきました。Queueには300件ほどのメッセージを入れたはずなのにそれよりも少ない数字が出ている(スクリーンショット取り忘れ)...

最初は下のスクリーンショットのように「結果は、最大で5分遅れる可能性があります」ということであまり気にも留めていませんでした。

f:id:miyohide:20210606155454p:plain

しかしながら、だんだんと気になったので簡単なhttpトリガーのアプリを作って試してみました。

検証

アプリはJavaで作り、Consumption PlanでLinux環境で動かしました。アプリのコードは以下のようなものですが、今回はこれは本質ではないです。

package com.github.miyohide;

import com.microsoft.azure.functions.*;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import org.springframework.cloud.function.adapter.azure.FunctionInvoker;

public class HttpHandler extends FunctionInvoker<String, String> {
    @FunctionName("hello")
    public HttpResponseMessage hello(
            @HttpTrigger(
                    name = "req",
                    methods = {HttpMethod.GET},
                    authLevel = AuthorizationLevel.FUNCTION
            )HttpRequestMessage<String> req,
            ExecutionContext context
    ) {
        String msg = req.getQueryParameters().getOrDefault("msg", "Hello World");
        return req.createResponseBuilder(HttpStatus.OK)
                .body(msg)
                .header("Content-Type", "application/json")
                .build();
    }
}

このアプリをAzure上にデプロイし、abコマンドで1,000回のリクエスト・同時接続数100で実行することを3回行いました。動作確認用に1回リクエストを流したので、3,001回の成功回数が出てくれるはずですが、私の今回の場合は2,045回以上は記録されませんでした。

f:id:miyohide:20210606160042p:plain

上記問題の回避

何か対策はないかなと探してみると、マイクロソフトのドキュメントに以下の記述がありました。

ログ サンプリングが原因で、一部の実行が Application Insights の [モニター] ブレードに表示されない場合があります。 ログ サンプリングを回避するには、excludedTypes: "Request" を samplingSettings 値に追加します。

docs.microsoft.com

なるほど、デフォルトではログサンプリングが行われているようです。ここではドキュメントに記載の通りに設定してみます。host.jsonを以下のように記述してデプロイします。

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "excludedTypes": "Request"
      }
    }
  }
}

再検証

再度Azure上にデプロイし、abコマンドで1,000回のリクエスト・同時接続数100で実行することを3回しました。動作確認用に1回リクエストを飛ばしたので、合計で3,001回のリクエストが出てくれることを期待します。

f:id:miyohide:20210606160626p:plain

期待通り、3,001回という数字が出ていました。

Azure Functionsのお勉強メモ(11)Queue Trigger利用の深掘り(一部未解決)

最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。

どこまで続くかわからないお勉強メモ。今日は11回目です。今回はQueue Triggerの利用について深掘りしてみます。過去のものは以下を参照。

Queueにメッセージを送る

テスト用にQueueにメッセージを送るプログラムを以下の記事で作成しました。

miyohide.hatenablog.com

記事ではAzuriteで作成したエミュレーター用の記述ですが、以下のように少し書き換えてやればAzure上でのQueueに対してデータを送ることができます。

# 省略
client = Azure::Storage::Queue::QueueService.create(
  storage_account_name: ストレージアカウント名,
  storage_access_key: アクセウスキー,
  storage_queue_host: "https://ストレージアカウント名.queue.core.windows.net/"
)
# 省略

上記記事の初出時は、メッセージ送付を

client.create_message(キュー名, "test message #{index}")

としていましたが、これではAzure FunctionsのQueue Triggerにて以下のエラーが発生しました。

Unable to translate bytes [B5] at index 0 from specified code page to Unicode.

f:id:miyohide:20210530164826p:plain

これを解決するには、メッセージをBase64エンコードしてあげれば良いです。Rubyのコードで書けば以下の通り。

client.create_message(キュー名, Base64.encode64("test message #{index}"))

多くのQueueを送ったときにNullPointer Exceptionが発生する(未解決)

上記のプログラムでQueueに多くの(といっても100件ぐらい)メッセージを送ると以下のメッセージがでて処理が失敗しました。

Result: Failure Exception: NullPointerException: Stack: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at com.microsoft.azure.functions.worker.broker.JavaMethodInvokeInfo.invoke(JavaMethodInvokeInfo.java:22) at com.microsoft.azure.functions.worker.broker.EnhancedJavaMethodExecutorImpl.execute(EnhancedJavaMethodExecutorImpl.java:55) at com.microsoft.azure.functions.worker.broker.JavaFunctionBroker.invokeMethod(JavaFunctionBroker.java:57) at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:33) at com.microsoft.azure.functions.worker.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:10) at com.microsoft.azure.functions.worker.handler.MessageHandler.handle(MessageHandler.java:45) at com.microsoft.azure.functions.worker.JavaWorkerClient$StreamingMessagePeer.lambda$onNext$0(JavaWorkerClient.java:92) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.base/java.util.concurrent.FutureTask.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source) Caused by: java.lang.NullPointerException at org.springframework.cloud.function.adapter.azure.FunctionInvoker.handleRequest(FunctionInvoker.java:117) at com.github.miyohide.QueueHandler.queueHandler(QueueHandler.java:17) ... 16 more

f:id:miyohide:20210530170452p:plain

正直、この原因はわからなかったので、対処療法を行います。

対処療法としては以下二つを試しました。

  1. batchSizeを1にする
  2. batchSizeを2以上にしつつ、visibilityTimeoutを設定する

以下ドキュメントに書かれている通りのことをやっただけです。

docs.microsoft.com

batchSizeを1にすると1つのインスタンスで並行に動くことは無くなります。どうしても並行に動かしたいと言うことであれば、NullPointerExceptionが発生することは受け入れて、リトライで問題なければOKというスタンスでも良いかもしれません。動かすものによるかと思いますが、今回のサンプルではhost.jsonを以下のようにしました。

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "extensions": {
    "queues": {
      "visibilityTimeout": "00:00:05",
      "batchSize": 5
    }
  }
}

すると、1回目では失敗したメッセージが2回目では正常に処理できていることを確認できました。

1回目の処理。NullPointerExceptionが発生しています。 f:id:miyohide:20210530170705p:plain

2回目の処理。特に例外は発生していません。 f:id:miyohide:20210530170718p:plain

RubyからAzure Storage Queueにアクセスする

今週は小ネタ。あまり時間が取れなかった...

RubyからAzure Storage Queueにアクセスする

Azure Storage Queueにたくさんのデータを登録しようと思い、簡単なプログラムを書いて動かそうとしたらちょっとハマったので、備忘録で残しておきます。

公式ドキュメントが古すぎる件

まずは公式ドキュメントを見るようにしているので、公式ドキュメントを探してみると、そのものズバリの記事が以下にあります。が、結論から言うと以下のドキュメントに書かれている方法では動きませんでした。

docs.microsoft.com

Ruby 3.0の環境で動かそうとすると以下のエラーメッセージが出てrequire azureすら動きません。

/Users/hoge/bundle/ruby/3.0.0/gems/azure-0.7.10/lib/azure.rb:21:in `require': cannot load such file -- rexml/document (LoadError)
    from /Users/hoge/bundle/ruby/3.0.0/gems/azure-0.7.10/lib/azure.rb:21:in `<top (required)>'
    from lib/app.rb:2:in `require'
    from lib/app.rb:2:in `<main>'

また、azure gemのソースコードリポジトリであるAzure/azure-sdk-for-rubyのREADMEには2021年5月24日時点で以下の記述がされていました。

As of February 2021, Azure Resource Management SDK for Ruby has entered a retirement phase and is no longer officially supported by Microsoft.

もうメンテナンスされないので、別の方法を見つける必要があります。

Microsoft Azure Storage Client Library for Ruby

何か別の方法はないかなと探していたところ、Microsoft Azure Storage Client Library for Rubyを見つけました。azure gemのバージョン0.7.10からAzure Storage関係は別リポジトリに分かれたようです。

メンテナンスも(2021年5月24日時点で)行われているようなので、安心して使えます。以下ではこれを使います。

Azure Storage Queueにアクセスする

READMEに書いてあることそのままですが、念のため試してみます。試した環境は以下の通りです。

  • Ruby 3.0.0
  • azure-storage-queue gem 2.0.2

Azure Storage Queueにアクセスするにはazure-storage-queueを使います。gem install azure-storage-queueでgemをインストールして、以下のようなプログラムを書きます(追記:初出時、Base64.encode64を使って送信するメッセージをエンコードする処理を記述していませんでした。これによりメッセージを受け取ることはできなくなることがあります)

require 'azure/storage/queue'
require 'base64'

client = Azure::Storage::Queue::QueueService.create(
  use_development_storage: true
)

QUEUE_NAME = "myqueue"

client.create_queue(QUEUE_NAME)

100.times do |index|
  client.create_message(QUEUE_NAME, Base64.encode64("test message #{index}"))
end

REAMEには

Against local Emulator (Windows Only)

と書かれていますが、macでも動きます。Azuriteを起動しておいて、上記プログラムを実行すると、画像のようにQueueにメッセージが溜まっていることが確認できました。

f:id:miyohide:20210524213835p:plain

ソース

ソースは以下。gemをGemfileに入れたり、アプリのソースをlib以下に格納していますが、本質は変わらないかなと。

github.com

Azure Functionsのお勉強メモ(10)Blob Storageにファイルが登録された時に処理を実行する(Queue利用編)

最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。

どこまで続くかわからないお勉強メモ。今日は10回目です。前回・今回はBlob Storageにファイルが登録された時に処理を実行することをやってみました。前回うまくできなかったものを解決してみます。過去のものは以下を参照。

前回の振り返り

前回は、Blob Storageにファイルが登録された時にEvent Gridトリガーを使い、そこから画像変換処理を実行しようとしていました。しかしながら、出力ファイル名がうまく設定できなかったのが課題として残りました。

今回はBlob Storageにファイルが登録されると、以下の順番で処理をするように実行するようにします。

  1. Event Gridトリガーで処理を呼び出す
  2. 対象のファイル名をQueueに登録するだけのFunctionを実行する
  3. 別のFunctionにてQueueトリガーで処理を呼び出す
  4. 呼び出されたFunctionでBlobからファイルを読み込み、画像変換処理を実行する

それぞれ詳細を見ていきます。

Event Gridトリガーで処理を呼び出す

ここの部分は前回と同じです。マイクロソフトがドキュメントをしっかり書いているので、それを見ると良いかと思います。

docs.microsoft.com

対象のファイル名をQueueに登録するだけのFunctionを実行する

Queue Storage出力バインディングを使って対象のファイル名をQueueに登録する処理を実装します。出力バインディングについてはドキュメントがありますのでそれを参考にすると良いかと思います。

docs.microsoft.com

こんな感じの実装になります。

@QueueOutput(name = "queue", queueName = "myqueue", connection = "MyQueueConnection") OutputBinding<String> queue

値を設定するにはsetValueメソッドを使えばできます。アップロードしたファイルのURLはEvent GridトリガーのPOJOパラメータであるeventに対してevent.data.get("url")を実行することで取得できますので、それを使って以下のような実装をすれば良さそうです(サブディレクトリ切られた場合はもう少し考える必要あり)。

URL url = new URL(event.data.get("url").toString());
String filename = Paths.get(url.getPath()).getFileName().toString();
queue.setValue(filename);

これでこのFunctionでやることは終了です。

別のFunctionにてQueueトリガーで処理を呼び出す

新しくFunctionを作成し、Queueトリガーを使ったものを作ります。このFunctionで画像変換処理を実装します。

Queueトリガーは以下のドキュメントを見れば良いでしょう。

docs.microsoft.com

呼び出されたFunctionでBlobからファイルを読み込み、画像変換処理を実行する

画像変換処理の実装です。ほとんど同じようなサンプルがドキュメントに記載されていますのでそれに目を通しておきます。

docs.microsoft.com

入力ファイル名は{queueTrigger}を使えばよく、出力ファイル名の指定も{queueTrigger}を使ってpath = "thumbnails/s-{queueTrigger}"という感じで設定できます。

実装例としては、以下のようなものになりました。

public class ImageResizeHandler extends FunctionInvoker<byte[], byte[]> {
  @FunctionName("ImageResize")
  @StorageAccount("MyStorageAccount")
  @BlobOutput(name = "target", path = "thumbnails/s-{queueTrigger}", dataType = "binary")
  public byte[] imageResizeHandler(
      @QueueTrigger(name = "msg", queueName = "myqueue", connection = "MyQueueConnection")
          String msg,
      @BlobInput(name = "file", dataType ="binary", path = "images/{queueTrigger}") byte[] file,
      final ExecutionContext context) throws IOException {
    byte[] resizeImage = ResizeImage.resize(file);
    return resizeImage;
  }
}

アプリケーション設定の設定

Azure Blob StorageやQueue Storageに接続するために、Azure Functionsのアプリケーション設定を設定する必要があります。下図で赤枠で囲った部分のようにMyStorageAccountMyQueueConnectionに接続文字列を設定しておきます。

f:id:miyohide:20210516160943p:plain

実行

実際にファイルをアップロードしてみます。

f:id:miyohide:20210516161128p:plain

@BlobOutputにて指定したthumbnailsコンテナーを参照すると、アップロードしたファイル名の前にs-がついたファイルが作成されていることが確認できます。

f:id:miyohide:20210516161420p:plain

ソース

現時点でのソースです。そろそろ整理しよう。

github.com

Azure Functionsのお勉強メモ(9)Blob Storageにファイルが登録された時に処理を実行する(未完成)

最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。

どこまで続くかわからないお勉強メモ。今日は9回目です。今回はBlob Storageにファイルが登録された時に処理を実行することをやってみました。今回のものは未完成で、いくつか課題が残っています。過去のものは以下を参照。

お題

お題として、Blob Storageに画像ファイルが登録された時にサムネイルを作る処理を実装してみます。参考になりそうなものとして、マイクロソフトC#の実装例を公開しています。

docs.microsoft.com

上記の例ではEvent Gridを使っています。これは、Blob Triggerの以下の記述に起因しているものと思われます。

ストレージ ログは "ベスト エフォート" ベースで作成されます。 すべてのイベントがキャプチャされる保証はありません。 ある条件下では、ログが欠落する可能性があります。

docs.microsoft.com

そこで私もEvent Triggerを使うことにします。

サムネイルを作成する

Javaでサムネイルを作成する方法はいくつか方法があるかと思いますが、Azure Functions上では任意のミドルウェアImageMagickとか)をインストールすることはできないので、Pure Javaで実装されたライブラリを利用することにしました。以下の記事にライブラリの紹介がありますが、私はThumbnailatorを使うことにしました。特に深い思いはありません。

www.baeldung.com

以下のようなクラス・メソッドを作りました。

public class ResizeImage {
    public static byte[] resize(byte[] original) throws IOException {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(original));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Thumbnails.of(image)
                .scale(0.50)
                .outputFormat("PNG")
                .toOutputStream(out);
        return out.toByteArray();
    }
}

Event Grid Triggerを使うためのJava実装

JavaでEvent Grid Triggerを使う方法はマニュアルに書かれていますが、@EventGridTriggerを使い、受け取るイベントのJSONパラメータを格納するためのクラスを作っておけばOKです。

docs.microsoft.com

登録されたファイルをBlob Storageの入力バインドとするためのJava実装

JavaでBlob Storageの入力バインドを設定する方法は以下のマニュアルに書かれています。

docs.microsoft.com

ここで問題となるのがpathの属性値です。アップロードされるファイル名は都度変わるので、固定値を記述することはできません。前述のC#の実装例を見ると{data.url}が指定されていたので、以下の実装にしました。

@BlobInput(name = "file", dataType = "binary", path = "{data.url}") byte[] content,

作成したサムネイル画像をBlob Storageの出力バインドとするためのJava実装(未解決)

JavaでBlob Storageの出力バインドを設定する方法は以下のマニュアルに書かれています。

docs.microsoft.com

ここで問題となるのがpathの属性値です。アップロードされるファイル名を流用したいのですが、{data.url}https://○○○/とURL全体が入るので不適切です。ここでは解決方法がわかりませんでしたので、

@BlobOutput(name = "target", path = "myblob/{DateTime}-sample.png")OutputBinding<byte[]> outputItem,

としました。

Event Gridを作成する

Blob Storageに紐づくEvent Gridを作成します。ここではAzureポータルで作成しました。参考となるドキュメントは以下です。

docs.microsoft.com

上記のドキュメントではWeb Hookなのですが、Azure Functionsに紐づけるには「エンドポイントの種類」を変えれば良いだけです。

f:id:miyohide:20210509141310p:plain

今回は、以下のことを実現するためにフィルター処理を設定する必要があります。

  • ファイルがアップロードされた時には処理を実行したい
  • 作成されたサムネイルを保存したときには処理を実行しない

フィルター処理については以下のドキュメントを参照しました。

docs.microsoft.com

結果、以下のような設定を実施しました。

f:id:miyohide:20210509141700p:plain

まとめ

以上のことを実装することで、Blob Storageに画像をアップロードするとサムネイルを作成することができました。実際に使うには、

  • アップロードされたファイルが画像であること
  • 元の画像フォーマット(jpegとかpngとかgifとか)と対応してサムネイルの画像フォーマットも変更する

などの対応が必要かと思います。

また前述の通り出力ファイル名がうまく設定できなかったので、その対応も必要です。

Azure Functionsのお勉強メモ(8)Blob Storage出力バインドを使ってBlobにファイルを出力する その3

最近、Azure Functionsのお勉強をチマチマと始めました。色々と分からないことが多かったのでお勉強メモをまとめて記します。

どこまで続くかわからないお勉強メモ。今日は8回目です。今回はBlob Storage出力バインドを使った実装の続きです。過去のものは以下を参照。

出力するファイル名に日時情報を付与する

前回まではBlob Storageに出力するファイル名が固定化したものでしたが、これに日時情報を付与したいと言うことはよくあります。これを実現するためには、バインド式というものを利用すると良さそうです。

docs.microsoft.com

日時のフォーマットは変えたいことがよくあるかと思います。フォーマットはここに載っているものが使えるようです。

docs.microsoft.com

Javaの場合は、以下のように実装すると良さそうです。

@BlobOutput(name = "target", path = "myblob/{DateTime:yyyy}/{DateTime:MM}/{DateTime:dd}/{DateTime:hhmmss}-sample.txt") OutputBinding<String> outputItem,

Azure StorageのBlobは以下のページ内の記述のように、フラットなパラダイムで組織化されます。

docs.microsoft.com

ただ、上記のように仮想的なフォルダを作成することはできます。上記の例では「年」/「月」/「日」という仮想的なフォルダを作成して以下にファイルを作成しています。実際にAzure Functionsのアプリを動かしたら以下のようにファイルが作成されました。

f:id:miyohide:20210502173113p:plain

日時情報は東日本リージョンでも日本時間(JST)ではなく、UTCのようです。

f:id:miyohide:20210502173312p:plain

負荷をかけてみる

この状態でabコマンドを使って負荷をかけてみます。結果としては、以下の通りでまだ失敗するリクエストが発生します(Failed requestsが0でない)。

Concurrency Level:      10
Time taken for tests:   51.195 seconds
Complete requests:      1000
Failed requests:        45
   (Connect: 0, Receive: 0, Length: 45, Exceptions: 0)
Non-2xx responses:      45
Total transferred:      237641 bytes
HTML transferred:       31515 bytes
Requests per second:    19.53 [#/sec] (mean)
Time per request:       511.945 [ms] (mean)
Time per request:       51.195 [ms] (mean, across all concurrent requests)
Transfer rate:          4.53 [Kbytes/sec] received

ランダムな文字列をファイル名に付与する

秒単位の日時情報だけでは複数のインスタンスで重複してしまうことがあるので、ランダムな文字列をファイル名に出力したいところです。これも先ほどのバインド式にある{rand-guid}を使えばできそうです。

docs.microsoft.com

Javaでの実装では以下のようになります。

@BlobOutput(name = "target", path = "myblob/{DateTime:yyyy}/{DateTime:MM}/{DateTime:dd}/{DateTime:hhmmss}-{rand-guid}-sample.txt") OutputBinding<String> outputItem,

実行する

再度abコマンドを使って負荷をかけてみます。結果は以下の通り。

Concurrency Level:      10
Time taken for tests:   43.493 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      239753 bytes
HTML transferred:       33000 bytes
Requests per second:    22.99 [#/sec] (mean)
Time per request:       434.925 [ms] (mean)
Time per request:       43.493 [ms] (mean, across all concurrent requests)
Transfer rate:          5.38 [Kbytes/sec] received

Failed requestsが0であることから失敗はなさそうです。

出力されたファイルを確認してみます。GUIDを付与しているのでやたらと長いですが、同一日時(ファイル名の先頭六文字が一致している)で複数のファイルが出力されていることがわかります。

f:id:miyohide:20210502175034p:plain