これは、なにをしたくて書いたもの?
前に、Azure FunctionsのHttpTriggerをローカルで動かしてみました。
Azure Functions(Java)をローカルで動かしてみる - CLOVER🍀
今回は、TimerTriggerを動かしてみたいと思います。
TimerTrigger
TimerTriggerは、Azure Functionsで関数をスケジュールに沿って実行できる仕組みです。
Azure Functions のタイマー トリガー | Microsoft Docs
スケジュールの指定は、cron式とほぼ同じです(NCronTabというものを使うようです)。
Azure Functions のタイマー トリガー / NCRONTAB 式
今回は、TimerTriggerで実行する関数をJavaで作成してローカルで動かしてみます。
環境
今回の環境は、こちらです。
$ func --version 3.0.3388 $ java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04) OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.10, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-70-generic", arch: "amd64", family: "unix"
プロジェクトを作成する
Azure Functionsで、TimerTriggerを使うプロジェクトを作成しましょう。
クイックスタート: コマンド ラインから Azure に Java 関数を作成する / ローカル関数プロジェクトを作成する
$ mvn archetype:generate \ -DarchetypeGroupId=com.microsoft.azure \ -DarchetypeArtifactId=azure-functions-archetype \ -DgroupId=org.littlewings \ -DartifactId=hello-java-function-timer-trigger \ -Dversion=0.0.1-SNAPSHOT \ -Dpackage=org.littlewings.azure.function \ -DappName=hello-java-function-timer-trigger \ -DappRegion=japaneast \ -Dtrigger=TimerTrigger \ -DjavaVersion=11 \ -DinteractiveMode=false
ポイントは、こちらの指定ですね。
-Dtrigger=TimerTrigger
プロジェクトが作成できたら、プロジェクト内へ移動。
$ cd hello-java-function-timer-trigger
作成されたファイル一式。
$ tree -a . ├── .gitignore ├── host.json ├── local.settings.json ├── pom.xml └── src └── main └── java └── org └── littlewings └── Function.java 5 directories, 5 files
テストコードは作成されないみたいですね。
pom.xml
の主要な部分。
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>11</java.version> <azure.functions.maven.plugin.version>1.9.2</azure.functions.maven.plugin.version> <azure.functions.java.library.version>1.4.0</azure.functions.java.library.version> <functionAppName>hello-java-function-timer-trigger</functionAppName> <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory> </properties> <dependencies> <dependency> <groupId>com.microsoft.azure.functions</groupId> <artifactId>azure-functions-java-library</artifactId> <version>${azure.functions.java.library.version}</version> </dependency> <!-- Test --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.4.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.23.4</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>com.microsoft.azure</groupId> <artifactId>azure-functions-maven-plugin</artifactId> <version>${azure.functions.maven.plugin.version}</version> <configuration> <!-- function app name --> <appName>${functionAppName}</appName> <!-- function app resource group --> <resourceGroup>java-functions-group</resourceGroup> <!-- function app service plan name --> <appServicePlanName>java-functions-app-service-plan</appServicePlanName> <!-- function app region--> <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values --> <region>japaneast</region> <!-- function pricingTier, default to be consumption if not specified --> <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values --> <!-- <pricingTier></pricingTier> --> <!-- Whether to disable application insights, default is false --> <!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details for all valid configurations for application insights--> <!-- <disableAppInsights></disableAppInsights> --> <runtime> <!-- runtime os, could be windows, linux or docker--> <os>windows</os> <javaVersion>11</javaVersion> <!-- for docker function, please set the following parameters --> <!-- <image>[hub-user/]repo-name[:tag]</image> --> <!-- <serverId></serverId> --> <!-- <registryUrl></registryUrl> --> </runtime> <appSettings> <property> <name>FUNCTIONS_EXTENSION_VERSION</name> <value>~3</value> </property> </appSettings> </configuration> <executions> <execution> <id>package-functions</id> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>copy-resources</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <overwrite>true</overwrite> <outputDirectory>${stagingDirectory}</outputDirectory> <resources> <resource> <directory>${project.basedir}</directory> <includes> <include>host.json</include> <include>local.settings.json</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.1</version> <executions> <execution> <id>copy-dependencies</id> <phase>prepare-package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${stagingDirectory}/lib</outputDirectory> <overWriteReleases>false</overWriteReleases> <overWriteSnapshots>false</overWriteSnapshots> <overWriteIfNewer>true</overWriteIfNewer> <includeScope>runtime</includeScope> <excludeArtifactIds>azure-functions-java-library</excludeArtifactIds> </configuration> </execution> </executions> </plugin> <!--Remove obj folder generated by .NET SDK in maven clean--> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> <configuration> <filesets> <fileset> <directory>obj</directory> </fileset> </filesets> </configuration> </plugin> </plugins>
設定ファイル。
local.settings.json
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "java" } }
host.json
{ "version": "2.0", "extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle", "version": "[1.*, 2.0.0)" } }
生成されたソースコード。TimerTrigger用になっていますね。
src/main/java/org/littlewings/Function.java
package org.littlewings; import java.time.*; import com.microsoft.azure.functions.annotation.*; import com.microsoft.azure.functions.*; /** * Azure Functions with Timer trigger. */ public class Function { /** * This function will be invoked periodically according to the specified schedule. */ @FunctionName("Function") public void run( @TimerTrigger(name = "timerInfo", schedule = "0 * * * * *") String timerInfo, final ExecutionContext context ) { context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); } }
定義を見ると、どうやら毎分起動してログ出力するTimerTriggerのようです。
@FunctionName("Function") public void run( @TimerTrigger(name = "timerInfo", schedule = "0 * * * * *") String timerInfo, final ExecutionContext context ) { context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); }
動かしてみる
とりあえず、この状態でも動かしてみましょう。
パッケージングして
$ mvn package
実行。
$ mvn azure-functions:run
[INFO] Azure Function App's staging directory found at: /path/to/hello-java-function-timer-trigger/target/azure-functions/hello-java-function-timer-trigger [INFO] Azure Functions Core Tools found. Azure Functions Core Tools Core Tools Version: 3.0.3388 Commit hash: fb42a4e0b7fdc85fbd0bcfc8d743ff7d509122ae Function Runtime Version: 3.0.15371.0 Missing value for AzureWebJobsStorage in local.settings.json. This is required for all triggers other than httptrigger, kafkatrigger. You can run 'func azure functionapp fetch-app-settings <functionAppName>' or specify a connection string in local.settings.json. [ERROR] [ERROR] Failed to run Azure Functions. Please checkout console output. [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------
エラーになってしまいました。どうやら、AzureWebJobsStorage
というものを指定する必要があるようです。
Missing value for AzureWebJobsStorage in local.settings.json. This is required for all triggers other than httptrigger, kafkatrigger. You can run 'func azure functionapp fetch-app-settings <functionAppName>' or specify a connection string in local.settings.json.
local.settings.json
を見ると、該当の部分が空欄になっていますね。
local.settings.json
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "java" } }
TimerTriggerで使いそうなことが、ドキュメントに書かれています。
Azure Functions ランタイムでは、このストレージ アカウント接続文字列は通常の操作に使用されます。 このストレージ アカウントの使用方法としては、キー管理、タイマー トリガー管理、Event Hubs チェックポイントなどがあります。
Azure Functions のアプリケーション設定のリファレンス / AzureWebJobsStorage
こちらを見ても、必須のようですね。ローカルでエミュレーターを使う方法も書いてあるので、こちらに習うことにします。
Azure ストレージ アカウントの接続文字列を含みます。 HTTP 以外のトリガーを使用する場合には必須です。 詳しくは、AzureWebJobsStorage のリファレンスを参照してください。 Azure ストレージ エミュレーターがローカルにインストールされ、AzureWebJobsStorage を UseDevelopmentStorage=true に設定すると、Core Tools でエミュレーターが使用されます。
Azure Functions Core Tools の操作 / ローカル設定ファイル
Azure Storageエミュレーターとしては、Azuriteを使うことにします。
$ npx azurite --version 3.11.0
起動。
$ mkdir data $ npx azurite -l data
この状態で、local.settings.json
のAzureWebJobsStorage
を以下のように指定します。
local.settings.json
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "java" } }
以下でもOKです。
local.settings.json
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;", "FUNCTIONS_WORKER_RUNTIME": "java" } }
再度パッケージングして、実行。
$ mvn package $ mvn azure-functions:run
今度は起動しました。
[INFO] Azure Function App's staging directory found at: /path/to/hello-java-function-timer-trigger/target/azure-functions/hello-java-function-timer-trigger [INFO] Azure Functions Core Tools found. Azure Functions Core Tools Core Tools Version: 3.0.3388 Commit hash: fb42a4e0b7fdc85fbd0bcfc8d743ff7d509122ae Function Runtime Version: 3.0.15371.0 [2021-03-26T05:30:50.795Z] Worker process started and initialized. Functions: Function: timerTrigger For detailed output, run func with --verbose flag. [2021-03-26T05:30:56.321Z] Host lock lease acquired by instance ID '000000000000000000000000C3CC5E65'.
関数も、スケジュールに沿って実行されました。
[2021-03-26T05:30:56.321Z] Host lock lease acquired by instance ID '000000000000000000000000C3CC5E65'. [2021-03-26T05:31:00.052Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T14:31:00.0210745+09:00', Id=dab7f753-3f36-4fb8-a66f-2da51d77b0ff) [2021-03-26T05:31:00.205Z] Java Timer trigger function executed at: 2021-03-26T14:31:00.193219 [2021-03-26T05:31:00.206Z] Function "Function" (Id: dab7f753-3f36-4fb8-a66f-2da51d77b0ff) invoked by Java Worker [2021-03-26T05:31:00.227Z] Executed 'Functions.Function' (Succeeded, Id=dab7f753-3f36-4fb8-a66f-2da51d77b0ff, Duration=198ms)
定義を見返すと、毎分起動してログ出力するようになっています。
@FunctionName("Function") public void run( @TimerTrigger(name = "timerInfo", schedule = "0 * * * * *") String timerInfo, final ExecutionContext context ) { context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); }
以下が、ログ出力した結果ですね。
[2021-03-26T05:31:00.205Z] Java Timer trigger function executed at: 2021-03-26T14:31:00.193219
このまま起動させておくと、毎分実行されるのが確認できます。
[2021-03-26T05:31:00.052Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T14:31:00.0210745+09:00', Id=dab7f753-3f36-4fb8-a66f-2da51d77b0ff) [2021-03-26T05:31:00.205Z] Java Timer trigger function executed at: 2021-03-26T14:31:00.193219 [2021-03-26T05:31:00.206Z] Function "Function" (Id: dab7f753-3f36-4fb8-a66f-2da51d77b0ff) invoked by Java Worker [2021-03-26T05:31:00.227Z] Executed 'Functions.Function' (Succeeded, Id=dab7f753-3f36-4fb8-a66f-2da51d77b0ff, Duration=198ms) [2021-03-26T05:32:00.005Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T14:32:00.0022770+09:00', Id=17c6812a-3adb-4140-b8b8-a07e657c5a7d) [2021-03-26T05:32:00.014Z] Java Timer trigger function executed at: 2021-03-26T14:32:00.013418 [2021-03-26T05:32:00.018Z] Function "Function" (Id: 17c6812a-3adb-4140-b8b8-a07e657c5a7d) invoked by Java Worker [2021-03-26T05:32:00.019Z] Executed 'Functions.Function' (Succeeded, Id=17c6812a-3adb-4140-b8b8-a07e657c5a7d, Duration=17ms) [2021-03-26T05:33:00.002Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T14:33:00.0008706+09:00', Id=8e78ed40-67b2-4076-b767-381f36b86b50) [2021-03-26T05:33:00.008Z] Java Timer trigger function executed at: 2021-03-26T14:33:00.006725 [2021-03-26T05:33:00.009Z] Function "Function" (Id: 8e78ed40-67b2-4076-b767-381f36b86b50) invoked by Java Worker [2021-03-26T05:33:00.010Z] Executed 'Functions.Function' (Succeeded, Id=8e78ed40-67b2-4076-b767-381f36b86b50, Duration=9ms)
Azurite側にも、アクセスが来ていることが確認できます。
127.0.0.1 - - [26/Mar/2021:05:31:00 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/timers/hostname-1510718391/Host.Functions.Function/status HTTP/1.1" 201 - 127.0.0.1 - - [26/Mar/2021:05:31:06 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:08 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:13 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:20 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:21 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:28 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:32 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:36 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:43 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:44 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:51 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:56 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:31:58 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:00 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/timers/hostname-1510718391/Host.Functions.Function/status HTTP/1.1" 201 - 127.0.0.1 - - [26/Mar/2021:05:32:06 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:08 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:13 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:20 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:21 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:28 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:32 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:36 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:43 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:44 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:51 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:56 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:32:58 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:00 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/timers/hostname-1510718391/Host.Functions.Function/status HTTP/1.1" 201 - 127.0.0.1 - - [26/Mar/2021:05:33:06 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:08 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:13 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:20 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:21 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:28 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:32 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:36 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:43 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:44 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:51 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:56 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/host?comp=lease HTTP/1.1" 200 - 127.0.0.1 - - [26/Mar/2021:05:33:58 +0000] "PUT /devstoreaccount1/azure-webjobs-hosts/locks/hostname-1510718391/Host.Functions.Function.Listener?comp=lease HTTP/1.1" 200 -
スケジュールを環境変数で指定する
とりあえずTimerTriggerを動かせたわけですが、実行するスケジュールがソースコードに固定されています。
@FunctionName("Function") public void run( @TimerTrigger(name = "timerInfo", schedule = "0 * * * * *") String timerInfo, final ExecutionContext context ) { context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); }
これを、実行時に変更できないかな?と。
変えられそうな雰囲気はあります。
スケジュール式をアプリ設定に含めて、たとえば "%ScheduleAppSetting%" のように、 % 記号で囲まれたアプリ設定名にこのプロパティを設定できます。
Azure Functions のタイマー トリガー / 構成
アプリ設定、とは…?
Azure Functions のアプリケーション設定のリファレンス | Microsoft Docs
function.json
というファイルを見るのが良さそうな気もします。
次の表は、function.json ファイルと TimerTrigger 属性で設定したバインド構成のプロパティを説明しています。
Azure Functions のタイマー トリガー / 構成
target/azure-functions
ディレクトリの中を見ると、function.json
がありました。
$ tree target/azure-functions target/azure-functions └── hello-java-function-timer-trigger ├── Function │ └── function.json ├── hello-java-function-timer-trigger-0.0.1-SNAPSHOT.jar ├── host.json └── local.settings.json 2 directories, 4 files
中身は、ソースコードの内容が反映されているようですね。
target/azure-functions/hello-java-function-timer-trigger/Function/function.json
{ "scriptFile" : "../hello-java-function-timer-trigger-0.0.1-SNAPSHOT.jar", "entryPoint" : "org.littlewings.Function.run", "bindings" : [ { "type" : "timerTrigger", "direction" : "in", "name" : "timerInfo", "schedule" : "0 * * * * *" } ] }
関数 は Azure Functions の主要な概念です。 関数には 2 つの重要な要素が含まれています。さまざまな言語で記述できるコードと、いくつかの構成、つまり function.json ファイルです。 コンパイル式の言語の場合、この構成ファイルはコード内の注釈から自動的に生成されます。
Azure Functions 開発者ガイド /関数のコード
でも、これはビルド時に作成されていることになります。実行時に変更するには…?
アプリ設定のドキュメントを見ていると、どうやら環境変数で変えられそうです。「ローカルで実行する場合」と但し書きが
ありますけど。
関数アプリのアプリケーション設定には、その関数アプリのすべての関数に影響するグローバル構成オプションが含まれています。 ローカルで実行する場合、これらの設定は、ローカルの環境変数としてアクセスされます。
Azure Functions のアプリケーション設定のリファレンス | Microsoft Docs
ローカルで実行しない場合は、どうなるんでしょう…。
まあ、今回はローカルでの実行のみなので、気にせず進んでみましょう。
schedule
を、%TIMER_SCHEDULE%
としました。
src/main/java/org/littlewings/Function.java
package org.littlewings; import java.time.*; import com.microsoft.azure.functions.annotation.*; import com.microsoft.azure.functions.*; /** * Azure Functions with Timer trigger. */ public class Function { /** * This function will be invoked periodically according to the specified schedule. */ @FunctionName("Function") public void run( @TimerTrigger(name = "timerInfo", schedule = "%TIMER_SCHEDULE%") String timerInfo, final ExecutionContext context ) { context.getLogger().info("Java Timer trigger function executed at: " + LocalDateTime.now()); } }
ビルド。
$ mvn package
生成されるfunction.json
にも、反映されました。
target/azure-functions/hello-java-function-timer-trigger/Function/function.json
{ "scriptFile" : "../hello-java-function-timer-trigger-0.0.1-SNAPSHOT.jar", "entryPoint" : "org.littlewings.Function.run", "bindings" : [ { "type" : "timerTrigger", "direction" : "in", "name" : "timerInfo", "schedule" : "%TIMER_SCHEDULE%" } ] }
あとは、環境変数で実行スケジュールを指定しつつ、起動します。
$ TIMER_SCHEDULE='0 */3 * * * *' mvn azure-functions:run
3分に1回実行するようにしてみました。
[INFO] Azure Function App's staging directory found at: /path/to/hello-java-function-timer-trigger/target/azure-functions/hello-java-function-timer-trigger [INFO] Azure Functions Core Tools found. Azure Functions Core Tools Core Tools Version: 3.0.3388 Commit hash: fb42a4e0b7fdc85fbd0bcfc8d743ff7d509122ae Function Runtime Version: 3.0.15371.0 [2021-03-26T10:10:33.029Z] Worker process started and initialized. Functions: Function: timerTrigger For detailed output, run func with --verbose flag. [2021-03-26T10:10:38.317Z] Host lock lease acquired by instance ID '000000000000000000000000C3CC5E65'.
しばらく待っていると、ログが出力されます。
[2021-03-26T10:12:00.060Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T19:12:00.0215774+09:00', Id=175912f7-8312-4d08-bc34-42df1672e59f) [2021-03-26T10:12:00.179Z] Java Timer trigger function executed at: 2021-03-26T19:12:00.157054 [2021-03-26T10:12:00.180Z] Function "Function" (Id: 175912f7-8312-4d08-bc34-42df1672e59f) invoked by Java Worker [2021-03-26T10:12:00.208Z] Executed 'Functions.Function' (Succeeded, Id=175912f7-8312-4d08-bc34-42df1672e59f, Duration=177ms) [2021-03-26T10:15:00.004Z] Executing 'Functions.Function' (Reason='Timer fired at 2021-03-26T19:15:00.0018087+09:00', Id=413918ed-efe6-4b01-b42c-04c23f0d36fc) [2021-03-26T10:15:00.021Z] Java Timer trigger function executed at: 2021-03-26T19:15:00.011291 [2021-03-26T10:15:00.026Z] Function "Function" (Id: 413918ed-efe6-4b01-b42c-04c23f0d36fc) invoked by Java Worker [2021-03-26T10:15:00.028Z] Executed 'Functions.Function' (Succeeded, Id=413918ed-efe6-4b01-b42c-04c23f0d36fc, Duration=25ms)
上手く指定できたようです。
ちなみに、環境変数を指定しなかった場合は
$ mvn azure-functions:run
値が解決できなくなり、関数の起動に失敗します。
[INFO] Azure Function App's staging directory found at: /path/to/hello-java-function-timer-trigger/target/azure-functions/hello-java-function-timer-trigger [INFO] Azure Functions Core Tools found. Azure Functions Core Tools Core Tools Version: 3.0.3388 Commit hash: fb42a4e0b7fdc85fbd0bcfc8d743ff7d509122ae Function Runtime Version: 3.0.15371.0 [2021-03-26T10:15:23.464Z] Worker process started and initialized. [2021-03-26T10:15:23.520Z] Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.Function'. Microsoft.Azure.WebJobs.Host: '%TIMER_SCHEDULE%' does not resolve to a value. [2021-03-26T10:15:23.541Z] Error indexing method 'Functions.Function' [2021-03-26T10:15:23.542Z] Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.Function'. Microsoft.Azure.WebJobs.Host: '%TIMER_SCHEDULE%' does not resolve to a value. [2021-03-26T10:15:23.542Z] Function 'Functions.Function' failed indexing and will be disabled. [2021-03-26T10:15:23.543Z] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.). Functions: Function: timerTrigger For detailed output, run func with --verbose flag. [2021-03-26T10:15:23.583Z] The 'Function' function is in error: Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.Function'. Microsoft.Azure.WebJobs.Host: '%TIMER_SCHEDULE%' does not resolve to a value. [2021-03-26T10:15:28.676Z] Host lock lease acquired by instance ID '000000000000000000000000C3CC5E65'.
簡単な確認でしたが、こんなところで。