Java で Azure Functionsの関数を実装する
Azure Functions とは、イベントドリブンでプログラムを実行してくれるSaaSプラットフォームです。いわゆるサーバレスプラットフォームと呼ばれるもので、AWS で言うのところの Lambda です(知らないんですけど)
Azure Functionsは、様々な言語とトリガサポートします。
言語では、
- C#
- GO
- Java
- JavaScript / TypeScript
- PowerShell
- Python
- Rust
トリガ(イベント)では、
- HTTP
- タイマー
- Storage Queue / ServiceBug
- CosmosDb
- ...
などです。
詳細は以下のDocsを当たってください。
Azure Functions のドキュメント | Microsoft Docs
Javaからの利用
Azure Functions 用の Maven plugin がリリースされていますので、それを使いましょう。
Maven Plugin for Azure Functions | Microsoft Docs
Functions プロジェクトの生成は、以下の通り mvn
から generate
します。サポートされている、Javaのバージョンは Java8 or Java11 です。javaVersion
に使いたいJavaのバージョンを指定してください。
mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=8
デフォルトでHTTPトリガーが自動で生成されます。Web APIを実装できます。
@FunctionName("HttpExample")
public HttpResponseMessage run(
@HttpTrigger(
name = "req",
methods = {HttpMethod.GET, HttpMethod.POST},
authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
context.getLogger().info("Java HTTP trigger processed a request.");
// Parse query parameter
final String query = request.getQueryParameters().get("name");
final String name = request.getBody().orElse(query);
if (name == null) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
} else {
return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
}
}
ローカル実行
ローカルで実行するには、Azure Functions Core Toolsが必要です。Visual Studio Code でプロジェクトを開くと、これらのツールのインストールが促されるのでインストールしてください。手動でもインストールできます。詳細は以下のURLへ。
Azure Functions Core Tools の操作 | Microsoft Docs
インストールが終わると、func
というコマンドがパスに通ると思いますが、Javaからはこのコマンドは直接利用せず、mvn
経由で実行します。package
してから azure-functions:run
するようにしてください。
mvn package azure-functions:run
実行されると、以下のようにホストされた関数が表示されます。CURLなど curl http://localhost:7071/api/HttpExample?name=azure
でクセスするとレスポンスを取得出来ると思います。
[INFO] Azure Functions Core Tools found.
Azure Functions Core Tools (3.0.2931 Commit hash: d552c6741a37422684f0efab41d541ebad2b2bd2)
Function Runtime Version: 3.0.14492.0
[2021-04-14T06:53:42.928] Worker process started and initialized.
Functions:
HttpExample: [GET,POST] http://localhost:7071/api/HttpExample
関数の追加
関数の追加もmvn
で行います。Java以外の言語は、func
コマンドで実行したりもするのですが。
mvn azure-functions:add
対話的に聞かれるので、追加したいトリガーを選択します。
Choose from below options as template for new function
0. HttpTrigger
1. BlobTrigger
2. QueueTrigger
3. TimerTrigger
4. EventGridTrigger
5. EventHubTrigger
6. CosmosDBTrigger
7. ServiceBusQueueTrigger
8. ServiceBusTopicTrigger
Enter index to use: 3
[INFO] Selected function template: TimerTrigger
[INFO] Successfully found function template: TimerTrigger
[INFO]
[INFO] Step 3 of 4: Prepare required parameters
[INFO] Common parameter [Function Name]: name for both the new function and Java class
Enter value for Function Name: TimerTriggerFunction
[INFO] Common parameter [Package Name]: package name of the new Java class
Enter value for Package Name: com.example.moris
[INFO] Trigger specific parameter [schedule]:Enter a cron expression of the format '{second} {minute} {hour} {day} {month} {day of week}' to specify the schedule.
Enter value for schedule(Default: 0 * * * * *):
[INFO]
[INFO] Summary of parameters for function template:
[INFO] schedule: 0 * * * * *
[INFO] functionName: TimerTriggerFunction
[INFO] className: TimerTriggerFunction
[INFO] packageName: com.example.moris
[INFO]
[INFO] Step 4 of 4: Saving function to file
[INFO] Successfully saved new function at c:\p\moris\blog\functions-sample\src\main\java\com\example\moris\TimerTriggerFunction.java
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
デプロイ
WebApps用のプラグインでは config
があったのですが、 Functions 用のプラグインにはありません。あらかじめ構成したい場合は、先ほど紹介したDocsをみてconfiguration
を追加してください。構成がないままazure-functions:deploy
すると、適当にAzureリソースが作られます。簡単にテストしたいときは、これで問題ないでしょう。以下がデプロイしたときに自動で生成されたものです。
<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>westus</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>8</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>
デプロイする前には、正しくパッケージングしましょう。細かな話を言うと、azure-functions:package
がパッケージングのゴールですが、シンプルにpackage
で、必要なJSONファイルなどを生成してくれます。target/azure-functions
が デプロイ用の成果物が格納されたディレクトリになります。各トリガの情報は、functions.json
に自動生成されます。
{
"scriptFile" : "../functions-sample-1.0-SNAPSHOT.jar",
"entryPoint" : "com.example.moris.Function.run",
"bindings" : [ {
"type" : "httpTrigger",
"direction" : "in",
"name" : "req",
"methods" : [ "GET", "POST" ],
"authLevel" : "ANONYMOUS"
}, {
"type" : "http",
"direction" : "out",
"name" : "$return"
} ]
}
デプロイはするには、以下のコマンドを実行します。あらかじめ、az login
しておいてください。
mvn azure-functions:deploy
ワーカーやライブラリの依存関係の話
トリガーを具体的に呼び出すのは Azure Functions Java Worker です。ソースはGithubで公開されています。Java Functions 上での怪しい挙動などは、ここにIssueを投げても良いでしょう(サポートでもいいですけど)
また、Java8とJava11で、クラスロードの優先順位が変わるので注意が必要です。
Java8では、ワーカー側のJarを読み込んで、次にクライアント側のJarを読み込みますが、Java11では、常にクライアント側のJarを読み込みます。Java8でこの挙動を変更したいときは、環境変数 FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS
を 1
or True
に設定してください。
とくに、FunctionsのランタイムにJava8を選択したとき、 Azure SDK for Java との相性が悪いので 例外が出たり、ハングしてタイムアウトする場合があるので注意が必要です。
まとめ
WebApps のほうは、azure-webapp
なのに、Functions の方は、azure-functions
でいつもプラグイン名が混乱します。単複系は難しい。