快速入门:使用 Java 通过 Azure SignalR 服务和 Azure Functions 来创建一个应用,用于显示 GitHub 星数
在本文中,你将使用 Azure SignalR 服务、Azure Functions 和 Java 生成一个无服务器应用程序,以便向客户端广播消息。
注意
GitHub 上提供了本文中的代码。
先决条件
代码编辑器,如 Visual Studio Code
具有活动订阅的 Azure 帐户。 试用订阅。
Azure Functions Core Tools。 用于在本地运行 Azure Functions 应用。
- Java 中所需的 SignalR 服务绑定仅在 Azure Function Core Tools 版本2.4.419(主机版本 2.0.12332)或更高版本中受支持。
- 若要安装扩展,Azure Functions Core Tools 需要安装 .NET Core SDK。 但是,生成 Java Azure Function 应用不需要了解 .NET。
Java 开发人员工具包版本 11
Apache Maven 3.0 或更高版本。
本快速入门可以在 macOS、Windows 或 Linux 上运行。
创建 Azure SignalR 服务实例
在本部分中,你将创建一个基本 Azure SignalR 实例来用于你的应用。 以下步骤使用 Azure 门户创建新实例,但你也可以使用 Azure CLI。 有关详细信息,请参阅 Azure SignalR 服务 CLI 参考中的 az signalr create 命令。
- 登录 Azure 门户。
- 在页面的左上角,选择“+ 创建资源” 。
- 在“创建资源”页上,在“搜索服务和市场”文本框中,输入“signalr”,然后从列表中选择“SignalR 服务”。
- 在“SignalR 服务”页上,选择“创建”。
- 在“基本信息”选项卡上,输入新 SignalR 服务实例的基本信息。 输入以下值:
字段 | 建议的值 | 描述 |
---|---|---|
订阅 | 选择订阅 | 选择要用于创建新的 SignalR 服务实例的订阅。 |
资源组 | 创建一个名为 SignalRTestResources 的资源组 | 为 SignalR 资源选择或创建资源组。 对于本教程,创建新的资源组比使用现有资源组更为合适。 若要在完成本教程后释放资源,请删除资源组。 删除资源组还会删除属于该组的所有资源。 此操作不能撤消。 删除资源组之前,请确保它不包含你希望保留的资源。 有关详细信息,请参阅 Using resource groups to manage your Azure resources(使用资源组管理 Azure 资源)。 |
资源名称 | testsignalr | 输入用于 SignalR 资源的唯一资源名称。 如果你的区域中已使用了 testsignalr,请添加一个数字或字符,以将名称设为唯一。 该名称必须是包含 1 到 63 个字符的字符串,只能包含数字、字母和连字符 ( - ) 字符。 该名称的开头或末尾不能是连字符字符,并且连续的连字符字符无效。 |
区域 | 选择你的区域 | 为新的 SignalR 服务实例选择合适的区域。 Azure SignalR 服务当前并非在所有区域中都可用。 有关详细信息,请参阅 Azure SignalR 服务区域可用性。 |
定价层 | 选择“更改”,然后选择“免费(仅限开发/测试)”。 选择“选择”以确认你选择的定价层。 | Azure SignalR 服务有三个定价层:免费、标准和高级。 教程使用的是免费层,除非在先决条件中另行说明。 若要详细了解各个层级之间的功能差异以及定价,请参阅 Azure SignalR 服务定价 |
服务模式 | 选择适当的服务模式 | 在 Web 应用中托管 SignalR 中心逻辑并使用 SignalR 服务作为代理时,请使用“默认”。 使用无服务器技术(如 Azure Functions)托管 SignalR 中心逻辑时,请使用“无服务器”。 “经典”模式仅用于向后兼容,不建议使用。 有关详细信息,请参阅 Azure SignalR 服务中的服务模式。 |
对于 SignalR 教程,你不需要更改“网络”和“标记”选项卡上的设置。
- 选择“基本信息”选项卡底部的“查看 + 创建”按钮。
- 在“查看 + 创建”选项卡上检查各个值,然后选择“创建”。 部署需要几分钟时间才能完成。
- 在部署完成后,选择“转到资源组”按钮。
- 在 SignalR 资源页面上,从左侧菜单中选择“设置”下的“密钥”。
- 复制主密钥的连接字符串。 在本教程中,稍后你将需要使用此连接字符串来配置你的应用。
配置和运行 Azure 函数应用
确保已安装 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>
本示例的客户端界面是网页。 我们将在
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 需要一个存储帐户才能工作。 可以安装并运行 Azurite 存储模拟器。
操作即将完成。 最后一步是将 SignalR 服务的连接字符串设置为 Azure Functions 设置。
使用 Azure 门户中的“搜索”框搜索前面部署的 Azure SignalR 实例。 选择该实例以将其打开。
选择“密钥”以查看 SignalR 服务实例的连接字符串。
复制主连接字符串,然后运行以下命令:
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 函数后,转到
http://localhost:7071/api/index
,你将看到当前的星数。 如果你在 GitHub 中添加了星标或取消添加了星标,星数会每隔几秒刷新一次。
清理资源
如果不打算继续使用此应用,请按照以下步骤删除本快速入门中创建的所有资源,以免产生任何费用:
在 Azure 门户的最左侧选择“资源组”,,然后选择创建的资源组。 或者,可以使用搜索框按名称查找资源组。
在打开的窗口中选择资源组,然后单击“删除资源组”。
在新窗口中键入要删除的资源组的名称,然后单击“删除”。
遇到问题? 请试用故障排除指南。
后续步骤
在本快速入门中,你在本地主机中生成并运行了一个实时无服务器应用程序。 接着,详细了解了如何通过 SignalR 服务在客户端与 Azure Functions 之间进行双向通信。