クイック スタート: Java を使用して Azure Functions と SignalR Service で GitHub の星の数を表示するアプリを作成する
この記事では、Azure SignalR Service、Azure Functions、Java を使用して、クライアントにメッセージをブロードキャストするサーバーレス アプリケーションをビルドします。
注意
この記事にあるコードは、GitHub で入手できます。
重要
この記事に示す生の接続文字列は、デモンストレーションのみを目的としています。
接続文字列には、アプリケーションが Azure Web PubSub サービスにアクセスするために必要な認可情報が含まれています。 接続文字列内のアクセス キーは、サービスのルート パスワードに似ています。 運用環境では、常にアクセス キーを保護してください。 Azure Key Vault を使用してキーの管理とローテーションを安全に行い、Microsoft Entra ID を使用して接続文字列をセキュリティで保護し、Microsoft Entra ID を使用してアクセスを認可します。
アクセス キーを他のユーザーに配布したり、ハードコーディングしたり、他のユーザーがアクセスできるプレーンテキストで保存したりしないでください。 キーが侵害された可能性があると思われる場合は、キーをローテーションしてください。
前提条件
コード エディター (Visual Studio Code など)。
アクティブなサブスクリプションが含まれる Azure アカウント。 アカウントをまだ持っていない場合は、無料でアカウントを作成します。
Azure Functions Core Tools。 Azure 関数アプリをローカルに実行する際に使用します。
- Java では、必須の SignalR Service バインディングが Azure Function Core Tools バージョン 2.4.419 (ホスト バージョン 2.0.12332) 以降でのみサポートされます。
- Azure Functions Core Tools では、拡張機能をインストールするために、.NET Core SDK がインストールされている必要があります。 ただし、Java Azure Functions アプリのビルドには、.NET の知識は必要ありません。
Java Developer Kit、バージョン 11
Apache Maven バージョン 3.0 以降。
このクイック スタートは、macOS、Windows、または Linux で実行できます。
Azure SignalR Service のインスタンスを作成する
このセクションでは、アプリに使う基本的な Azure SignalR インスタンスを作成します。 次の手順では、Azure portal を使って新しいインスタンスを作成しますが、Azure CLI を使うこともできます。 詳細については、Azure SignalR Service CLI リファレンスの az signalr create コマンドを参照してください。
- Azure portal にサインインします。
- ページの左上にある [+ リソースの作成] を選択します。
- [リソースの作成] ページで、[Search services and marketplace](サービスとマーケットプレースを検索) テキスト ボックスに「signalr」と入力し、リストから [SignalR Service] を選びます。
- [SignalR Service] ページで [作成] を選びます。
- [基本] タブで、新しい SignalR Service のインスタンスに必要な情報を入力します。 次の値を入力します。
フィールド | 推奨値 | 説明 |
---|---|---|
サブスクリプション | サブスクリプションの選択 | 新しい SignalR Service インスタンスを作成するために使うサブスクリプションを選びます。 |
リソース グループ | SignalRTestResources というリソース グループを作成する | SignalR リソースのリソース グループを選択または作成します。 既存のリソース グループを使う代わりに、このチュートリアルのために新しいリソース グループを作成すると便利です。 チュートリアルの終了後にリソースを解放するには、リソース グループを削除します。 リソース グループを削除すると、そのグループに所属するすべてのリソースも削除されます。 この削除操作は元に戻すことができません。 保存するリソースが含まれていないことを確認してから、リソース グループを削除してください。 詳細については、 リソース グループを使用した Azure リソースの管理に関するページを参照してください。 |
リソース名 | testsignalr | SignalR リソースに使用する一意のリソース名を入力します。 testsignalr がお使いのリージョンに既に導入されている場合は、数字や文字を追加して名前が一意になるようにします。 名前は 1 ~ 63 文字の文字列で、数字、英字、ハイフン ( - ) 文字のみを使用する必要があります。 名前の先頭と末尾にはハイフン文字を使用できません。また、連続するハイフン文字は無効です。 |
[リージョン] | 自分のリージョンを選択します | 新しい SignalR Service インスタンスに適切なリージョンを選びます。 Azure SignalR Service は、現在すべてのリージョンで使用できるわけではありません。 詳細については、Azure SignalR Service リージョンの可用性に関するページを参照してください |
価格レベル | [変更] を選び、[Free (Dev/Test Only)](無料 (Dev/Test のみ)) を選びます。 [選択] を選び、価格レベルを確認します。 | Azure SignalR Service には、Free、Standard、Premium という 3 つの価格レベルがあります。 チュートリアルでは、前提条件で特に明記されない限り、[Free] レベルを使います。 レベルと価格による機能の違いの詳細については、「Azure SignalR Service の価格」を参照してください |
サービス モード | 適切なサービス モードを選びます | Web アプリで SignalR のハブ ロジックをホストしていて、SignalR Service をプロキシとして使う場合は、[既定] を使います。 Azure Functions などのサーバーレス テクノロジを使って SignalR のハブ ロジックをホストする場合は、[サーバーレス] を使います。 [クラシック] モードは下位互換性のためだけにあり、使わないことをお勧めします。 詳細については、「Azure SignalR Service のサービス モード」を参照してください。 |
SignalR チュートリアルでは、[ネットワーク] と [タグ] タブの設定を変更する必要はありません。
- [基本] タブの下部にある [確認と作成] ボタンを選びます。
- [確認と作成] タブで、値を確認し [作成] を選びます。 デプロイが完了するまでしばらくかかります。
- デプロイが完了したら、[リソースに移動] ボタンを選びます。
- SignalR リソース ページで、左側のメニューから、[設定] の下にある [キー] を選びます。
- 主キーの [接続文字列] をコピーします。 この接続文字列は、このチュートリアルで後ほどアプリを構成するために必要です。
Azure Function アプリを構成して実行する
Azure Function Core Tools、Java (サンプルではバージョン 11)、Maven がインストールされていることを確認します。
Maven を使用してプロジェクトを初期化します。
mvn archetype:generate -DarchetypeGroupId=com.microsoft.azure -DarchetypeArtifactId=azure-functions-archetype -DjavaVersion=11
Maven により、プロジェクトの生成を終了するために必要な値が要求されます。 次の値を指定します。
Prompt 値 説明 groupId com.signalr
Java のパッケージ命名規則に従って、すべてのプロジェクトにわたって対象のプロジェクトを一意に識別する値。 artifactId java
バージョン番号のない、jar の名前である値。 version 1.0-SNAPSHOT
既定値を選択します。 package com.signalr
生成された関数コードの Java パッケージである値。 既定値を使用します。 フォルダー
src/main/java/com/signalr
に移動し、次のコードを Function.java にコピーします。package com.signalr; import com.google.gson.Gson; import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.HttpMethod; import com.microsoft.azure.functions.HttpRequestMessage; import com.microsoft.azure.functions.HttpResponseMessage; import com.microsoft.azure.functions.HttpStatus; import com.microsoft.azure.functions.annotation.AuthorizationLevel; import com.microsoft.azure.functions.annotation.FunctionName; import com.microsoft.azure.functions.annotation.HttpTrigger; import com.microsoft.azure.functions.annotation.TimerTrigger; import com.microsoft.azure.functions.signalr.*; import com.microsoft.azure.functions.signalr.annotation.*; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; import java.nio.charset.StandardCharsets; import java.util.Optional; public class Function { private static String Etag = ""; private static String StarCount; @FunctionName("index") public HttpResponseMessage run( @HttpTrigger( name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS)HttpRequestMessage<Optional<String>> request, final ExecutionContext context) throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("content/index.html"); String text = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); return request.createResponseBuilder(HttpStatus.OK).header("Content-Type", "text/html").body(text).build(); } @FunctionName("negotiate") public SignalRConnectionInfo negotiate( @HttpTrigger( name = "req", methods = { HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> req, @SignalRConnectionInfoInput( name = "connectionInfo", hubName = "serverless") SignalRConnectionInfo connectionInfo) { return connectionInfo; } @FunctionName("broadcast") @SignalROutput(name = "$return", hubName = "serverless") public SignalRMessage broadcast( @TimerTrigger(name = "timeTrigger", schedule = "*/5 * * * * *") String timerInfo) throws IOException, InterruptedException { HttpClient client = HttpClient.newHttpClient(); HttpRequest req = HttpRequest.newBuilder().uri(URI.create("https://api.github.com/repos/azure/azure-signalr")).header("User-Agent", "serverless").header("If-None-Match", Etag).build(); HttpResponse<String> res = client.send(req, BodyHandlers.ofString()); if (res.headers().firstValue("Etag").isPresent()) { Etag = res.headers().firstValue("Etag").get(); } if (res.statusCode() == 200) { Gson gson = new Gson(); GitResult result = gson.fromJson(res.body(), GitResult.class); StarCount = result.stargazers_count; } return new SignalRMessage("newMessage", "Current start count of https://github.com/Azure/azure-signalr is:".concat(StarCount)); } class GitResult { public String stargazers_count; } }
依存関係をいくつか追加する必要があります。 pom.xml を開き、コードで使用される次の依存関係を追加します。
<dependency> <groupId>com.microsoft.azure.functions</groupId> <artifactId>azure-functions-java-library-signalr</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.7</version> </dependency>
このサンプルのクライアント インターフェイスは Web ページです。
index
関数内の content/index.html から HTML コンテンツを読み取り、resources
ディレクトリに新しいファイル content/index.html を作成します。 ディレクトリ ツリーは次のようになります。| - src | | - main | | | - java | | | | - com | | | | | - signalr | | | | | | - Function.java | | | - resources | | | | - content | | | | | - index.html | - pom.xml | - host.json | - local.settings.json
index.html を開き、次の内容をコピーします。
<html> <body> <h1>Azure SignalR Serverless Sample</h1> <div id="messages"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7/signalr.min.js"></script> <script> let messages = document.querySelector('#messages'); const apiBaseUrl = window.location.origin; const connection = new signalR.HubConnectionBuilder() .withUrl(apiBaseUrl + '/api') .configureLogging(signalR.LogLevel.Information) .build(); connection.on('newMessage', (message) => { document.getElementById("messages").innerHTML = message; }); connection.start() .catch(console.error); </script> </body> </html>
Azure Functions が機能するには、ストレージ アカウントが必要になります。 Azure ストレージ エミュレーターをインストールして実行できます。
あともう少しで終了です。 最後の手順では、Azure Functions の設定に SignalR Service の接続文字列を設定します。
Azure portal の [検索] ボックスを使用して、前にデプロイした Azure SignalR インスタンスを検索します。 インスタンスを選択して開きます。
[Key](キー) を選択し、SignalR Service インスタンスの接続文字列を表示します。
プライマリ接続文字列をコピーし、次のコマンドを実行します。
この記事では、デモ目的でのみ生の接続文字列が表示されます。 運用環境では、常にアクセス キーを保護してください。 Azure Key Vault を使用してキーの管理とローテーションを安全に行い、Microsoft Entra ID を使用して接続文字列をセキュリティで保護し、Microsoft Entra ID を使用してアクセスを認可します。
func settings add AzureSignalRConnectionString "<signalr-connection-string>" # Also we need to set AzureWebJobsStorage as Azure Function's requirement func settings add AzureWebJobsStorage "UseDevelopmentStorage=true"
Azure Functions をローカルで実行します:
mvn clean package mvn azure-functions:run
Azure Functions がローカルで実行された後、
http://localhost:7071/api/index
に移動すると、現在の星の数が表示されます。 GitHub で星を付けたり "外したり" すると、数秒ごとに星の数が更新されます。
リソースをクリーンアップする
このアプリの使用を続けない場合は、次の手順に従って、このクイック スタートで作成したすべてのリソースを削除して、課金が発生しないようにします。
Azure Portal の左端で [リソース グループ] を選択し、作成したリソース グループを選択します。 検索ボックスを使用して名前でリソース グループを検索することもできます。
表示されたウィンドウでリソース グループを選択し、[リソース グループの削除] をクリックします。
新しいウィンドウで、削除するリソース グループの名前を入力し、[削除] をクリックします。
問題がある場合は、 トラブルシューティング ガイドをお試しになるか、ご連絡ください。
次のステップ
このクイックスタートでは、ローカル ホストでリアルタイムのサーバーレス アプリケーションをビルドして実行しました。 次に、SignalR Service を使用して、クライアントと Azure Functions の間で双方向通信を行う方法について詳しく説明します。