これまでは、HTTPリクエストによりAzure Functionsが動作するアプリケーションのみ作成してきたが、Timer Triggerによって、一定時間が来たタイミングでAzure Functionsが動作するアプリケーションも作成することができる。
今回は、Azure Functions上でTimerTriggerによって動作するJavaアプリケーション(Spring Boot上)を作成してみたので、共有する。
前提条件
下記記事のBlob Storageの作成が完了していること。
また、下記記事に従ってAzure Functionsのサンプルプログラムを作成済であること。
作成したサンプルプログラムの内容
作成したサンプルプログラムの構成は以下の通り。
pom.xmlの内容は以下の通りで、Lombokを追加している以外は、一般的なAzure Functionsの場合と同じ設定になっている。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demoAzureFunc</artifactId> <version>0.0.1-SNAPSHOT</version> <name>Hello Spring Function on Azure</name> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <azure.functions.maven.plugin.version>1.9.0</azure.functions.maven.plugin.version> <!-- customize those properties. The functionAppName should be unique across Azure --> <functionResourceGroup>azureAppDemo</functionResourceGroup> <functionAppName>azureFuncDemoApp</functionAppName> <functionAppServicePlan>ASP-azureAppDemo-8679</functionAppServicePlan> <functionPricingTier>B1</functionPricingTier> <functionAppRegion>japaneast</functionAppRegion> <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory> <start-class>com.example.DemoAzureFunction</start-class> <spring.boot.wrapper.version>1.0.25.RELEASE</spring.boot.wrapper.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-adapter-azure</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-function-web</artifactId> <scope>provided</scope> </dependency> <!-- lombokを利用するための設定 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <!-- Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-function-dependencies</artifactId> <version>3.1.2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <pluginManagement> <plugins> <plugin> <groupId>com.microsoft.azure</groupId> <artifactId>azure-functions-maven-plugin</artifactId> <version>${azure.functions.maven.plugin.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.1.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> </plugins> </pluginManagement> <plugins> <plugin> <groupId>com.microsoft.azure</groupId> <artifactId>azure-functions-maven-plugin</artifactId> <configuration> <resourceGroup>${functionResourceGroup}</resourceGroup> <appName>${functionAppName}</appName> <appServicePlanName>${functionAppServicePlan}</appServicePlanName> <region>${functionAppRegion}</region> <pricingTier>${functionPricingTier}</pricingTier> <runtime> <os>Linux</os> <javaVersion>8</javaVersion> </runtime> <appSettings> <!-- Run Azure Function from package file by default --> <property> <name>WEBSITE_RUN_FROM_PACKAGE</name> <value>1</value> </property> <property> <name>FUNCTIONS_EXTENSION_VERSION</name> <value>~3</value> </property> <property> <name>FUNCTIONS_WORKER_RUNTIME</name> <value>java</value> </property> </appSettings> </configuration> <executions> <execution> <id>package-functions</id> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <executions> <execution> <id>copy-resources</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <overwrite>true</overwrite> <outputDirectory> ${project.build.directory}/azure-functions/${functionAppName} </outputDirectory> <resources> <resource> <directory>${project.basedir}/src/main/azure </directory> <includes> <include>**</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <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> </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> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-thin-layout</artifactId> <version>${spring.boot.wrapper.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/libs-snapshot-local</url> <snapshots> <enabled>true</enabled> </snapshots> <releases> <enabled>false</enabled> </releases> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone-local</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/libs-snapshot-local</url> <snapshots> <enabled>true</enabled> </snapshots> <releases> <enabled>false</enabled> </releases> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone-local</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/libs-release-local</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
また、host.json、local.settings.jsonの内容は以下の通り。
{ "version": "2.0", "functionTimeout": "00:10:00" }
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=azureblobpurinit;AccountKey=55facd(以下Azure Storageのアカウントキーを指定)", "FUNCTIONS_WORKER_RUNTIME": "java", "MAIN_CLASS":"com.example.DemoAzureFunction", "AzureWebJobsDashboard": "" } }
なお、local.settings.jsonの「AzureWebJobsStorage」は、HTTP Trigger以外では指定が必要になるため、Azure Storageのアカウント名・アカウントキーを指定している。アカウントキーは、Azure Storageの以下の画面で確認できるようになっている。
また、TimerTriggerのサービスクラス、Paramクラス、Resultクラスの内容は以下の通りで、サービスクラスが呼び出されたタイミングで、ログ出力するようになっている。なお、Azure上でFunctionsのログが出力できるよう、ログ出力時に、Paramクラスに追加したログ出力クラスを利用している。
package com.example.service; import org.springframework.stereotype.Service; import com.example.model.TimerTriggerParam; import com.example.model.TimerTriggerResult; @Service public class TimerTriggerService { /** * タイマートリガーのテストを行うサービス. * @param param TimerTrigger呼出用Param * @return タイマートリガーのテストを行うサービスクラスの呼出結果 */ public TimerTriggerResult timerTriggerTest(TimerTriggerParam param) { // タイマートリガーのテストを行うサービスが呼び出されたことをログ出力する param.getLogger().info("TimerTriggerService timerTriggerTest triggered: " + param.getTimerInfo()); // タイマートリガーのテストを行うサービスクラスの呼出結果を返却する TimerTriggerResult result = new TimerTriggerResult(); result.setResult("success"); return result; } }
package com.example.model; import lombok.Data; import java.util.logging.Logger; @Data public class TimerTriggerParam { /** TimerTrigger情報 */ private String timerInfo; /** ログ出力クラス */ private Logger logger; }
package com.example.model; import lombok.Data; @Data public class TimerTriggerResult { /** 結果情報 */ private String result; }
さらに、メインクラスの内容は以下の通りで、TimerTriggerのサービスクラスのファンクション定義を追加している。
package com.example; import java.util.function.Function; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import com.example.model.TimerTriggerParam; import com.example.model.TimerTriggerResult; import com.example.service.TimerTriggerService; @SpringBootApplication public class DemoAzureFunction { /** タイマートリガーのテストを行うサービスクラスのオブジェクト */ @Autowired private TimerTriggerService timerTriggerService; public static void main(String[] args) throws Exception { SpringApplication.run(DemoAzureFunction.class, args); } /** * タイマートリガーのテストを行い結果を返却する関数 * @return タイマートリガーのテストを行うサービスクラスの呼出結果 */ @Bean public Function<TimerTriggerParam, TimerTriggerResult> timerTriggerTest(){ return timerTriggerParam -> timerTriggerService.timerTriggerTest(timerTriggerParam); } }
また、TimerTriggerイベントが発生したタイミングで呼ばれるハンドラークラスの内容は以下の通りで、TimerTriggerのサービスクラスを呼び出している。
package com.example; import org.springframework.cloud.function.adapter.azure.FunctionInvoker; import com.example.model.TimerTriggerParam; import com.example.model.TimerTriggerResult; import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.annotation.FunctionName; import com.microsoft.azure.functions.annotation.TimerTrigger; public class TimerTriggerTestHandler extends FunctionInvoker<TimerTriggerParam, TimerTriggerResult>{ /** * TimerTriggerによって、DemoAzureFunctionクラスのtimerTriggerTestメソッドを呼び出す. * @param timerInfo TimerTriggerイベント情報 * @param context コンテキストオブジェクト */ // 「schedule = "0 */1 * * * *"」で、1分毎にTimerTriggerイベントが発生するようになっている @FunctionName("timerTriggerTest") public void timerTriggerTest(@TimerTrigger(name = "timerTriggerTest" , schedule = "0 */1 * * * *") String timerInfo, ExecutionContext context) { context.getLogger().info("TimerTriggerTestHandler timerTriggerTest triggered: " + timerInfo); TimerTriggerParam param = new TimerTriggerParam(); param.setTimerInfo(timerInfo); param.setLogger(context.getLogger()); handleRequest(param, context); } }
なお、上記クラスでは、1分毎にTimerTriggerイベントが発生するようになっている。TimerTriggerイベントの発生タイミングの記載方法については、以下のサイトの「NCRONTAB式」を参照のこと。
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-timer?tabs=java#ncrontab-expressions
サンプルプログラムの実行結果(ローカル)
先ほど作成したアプリケーションを、ローカル環境で実行する方法と実行結果は、以下の通り。
1) コマンドプロンプトを起動し、作成したアプリケーションのpom.xmlの存在するディレクトリに移動する。
2) 「mvn clean」コマンドを実行し、デプロイされたjarファイルが含まれるtarget ディレクトリ内をクリアする。
3) 「mvn package」コマンドを実行し、デプロイ用のjarファイルを作成する。
なお、上記画像では、コピーしている箇所を一部省略している。
4) 「mvn azure-functions:run」コマンドを実行し、Azure Functiosを実行する。
5) 1分後以内に、以下の赤枠の、ハンドラークラスとサービスクラスのログが出力されることが確認できる。
6) さらに1分後に、以下の赤枠の、ハンドラークラスとサービスクラスのログが出力されることが確認できる。
7) Azure Functionsを終了するには、「Ctrl+C」コマンドで「バッチジョブを終了しますか(Y/N)?」というメッセージが表示されるので、「Y」を選択しエンターキーを押下する。
サンプルプログラムの実行結果(Azure上)
Azure FunctionsへのSpring Bootを利用したJavaアプリケーションのデプロイ方法と実行結果は、以下の通り。なお、「az login」コマンドによるログインは既に実施済とする。
1) 「mvn azure-functions:deploy」コマンドを実行し、jarファイルをAzure Functionsにデプロイする。
また、一定時間経過後にログ確認を行うため、Azure Portalにログイン後、以下の操作を行う。
2) Azure Functionsの概要を表示した状態で「関数」メニューを押下する。
6) ログに、以下の赤枠の、ハンドラークラスとサービスクラスのログが出力されることが確認できる。
要点まとめ
- Azure Functions上では、Timer Triggerによって、一定時間が来たタイミングでAzure Functionsが動作するアプリケーションも作成することができる。
- Timer Triggerアノテーションのschedule属性で、TimerTriggerイベントの発生タイミングを指定できる。