Azure SignalR和Azure Functions实现无服务器架构

学会Azure Functions的开发方式,搭配着Azure SignalR Service就可以在 Azure 轻松实现即时连线式的无服务器架构,本篇文章我将说明实现这个架构的相关说明与注意事项。

开发环境

要开发 Azure Functions 建议先安装以下开发工具:

1、安装Visual Studio Code 及Azure Functions 扩展套件。

choco install vscode -y

2、安装 Azure Functions Core Tools v3& Azure Functions runtime versions overview

choco install azure-functions-core-tools -y

3、安装Azure Storage Emulator

由于 Azure Functions 在执行时需要 Azure Storage 才能运行,因此在本机开发时建议先启动 Azure Storage Emulator 再来执行测试。

我在安装 Visual Studio 2019 的时候都会安装 Azure development 工作负载(Workload),所以预设就会安装 Azure Storage Emulator 起来。如果你没有安装的话,可以用以下命令进行安装,或是直接下载 MSI 文件进行安装。

choco install azurestorageemulator -y

这个 Chocolatey 套件虽然显示是旧版的,但事实上会安装 Azure Storage Emulator 最新版本!

第一次使用 Azure Storage Emulator 的人,可以参考以下两个命令初始化并启动模拟器:

"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" init
"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe" start

4、安装.NET Core SDK 3.1

choco install dotnetcore-sdk -y

5、建立 Azure SignalR Service 服务,参考Microsft文档

开发 Azure Functions 应用程序

1、初始化 Azure Functions 应用

mkdir demo1
cd demo1
func init --worker-runtime dotnet

这个步骤会创建 5 个文件,其中 local.settings.json 会储存“应用程序”设置,预设会被 .gitignore 排除在版控之外,要特别注意!

.vscode\extensions.json
.gitignore
demo1.csproj
host.json
local.settings.json

host.json用来定义 Azure Functions 的执行环境相关配置,而 local.settings.json 则是只会存留于本机的配置设定,当你要将 Function App 部署到 Azure App Service 时,这些配置要手动注册到 App Service 的应用程式配置设定中!

2、启动 Visual Studio Code 编辑器并初始化开发 Azure Functions 所需的编辑器环境

3、加入 Microsoft.Azure.WebJobs.Extensions.SignalRService 套件 (NuGet)

dotnet add package Microsoft.Azure.WebJobs.Extensions.SignalRService

4、加入 Functions.cs 代码

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Newtonsoft.Json;

namespace CSharp
{
    public static class Function
    {
        private static HttpClient httpClient = new HttpClient();

        [FunctionName("index")]
        public static IActionResult Index([HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req, ExecutionContext context)
        {
            var path = Path.Combine(context.FunctionAppDirectory, "content", "index.html");
            return new ContentResult
            {
                Content = File.ReadAllText(path),
                ContentType = "text/html",
            };
        }

        [FunctionName("negotiate")]
        public static SignalRConnectionInfo Negotiate(
            [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
            [SignalRConnectionInfo(HubName = "serverlessSample")] SignalRConnectionInfo connectionInfo)
        {
            return connectionInfo;
        }

        [FunctionName("broadcast")]
        public static async Task Broadcast([TimerTrigger("*/5 * * * * *")] TimerInfo myTimer,
        [SignalR(HubName = "serverlessSample")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/azure/azure-signalr");
            request.Headers.UserAgent.ParseAdd("Serverless");
            var response = await httpClient.SendAsync(request);
            var result = JsonConvert.DeserializeObject<GitResult>(await response.Content.ReadAsStringAsync());
            await signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "newMessage",
                    Arguments = new[] { $"Current star count of https://github.com/Azure/azure-signalr is: {result.StarCount}" }
                });
        }

        private class GitResult
        {
            [JsonRequired]
            [JsonProperty("stargazers_count")]
            public string StarCount { get; set; }
        }
    }
}

注意:在调用GitHub API 时,在未通过身份验证的状态下,一小时只能发出 60 个 Requests 而已!你可以用 curl -I https://api.github.com/repos/azure/azure-signalr 查出你的目前 IP 还剩馀多少 API 的 RateLimit 调用次数(X-RateLimit-Remaining)以及下次重置时间(X-RateLimit-Reset)。

5、加入 content/index.html 预设首页 HTML 原始代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Azure SignalR Serverless Sample</title>
</head>
<body>

  <h1>Azure SignalR Serverless Sample</h1>
  <div id="messages"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.20/signalr.min.js" integrity="sha512-Tmi7k+eXKdibvPJfx/L8uCogEzWFQvC+heO3urDkTr5px1QfpwY1auftPq85zpGG++KbekSpPL7wn6bOS0oJVg==" crossorigin="anonymous" referrerpolicy="no-referrer"></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>

注意:关于 microsoft-signalr 的 CDN 地址可以从 https://cdnjs.com/libraries/microsoft-signalr/3.1.20 获得。

SignalR 用户端函数库的 .withUrl(apiBaseUrl + ‘/api’) 会自动找出 apiBaseUrl + ‘/api/negotiate’ 进行协商,这个过程会调用 /api/negotiate 这个 API 并取得 Azure SignalR Service 的服务网址与 Access Token,接着就会让浏览器用户端直接连接 Azure SignalR Service 服务。

6、更新 demo1.csproj 并设定 content/index.html 文件会复制到输出目录。

<ItemGroup>
  <None Update="content/index.html">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

7、从 local.settings.json 加入名为 AzureSignalRConnectionString 的配置设置

func settings add AzureSignalRConnectionString "<signalr-connection-string>"

注意:上述命令只会修改 local.settings.json 这个文件,并在该文件加入名为AzureSignalRConnectionString 的应用程序参数,这个参数主要提供给 SignalR Service bindings for Azure Functions 之用。

8、在本机启动 Azure Functions 应用程序

func start

由于 Azure Function 在本机开发时,预设使用7071通讯录,如果你不幸这个端口被占用的话,一样重开机可能会好。或者你也可以改另一个端口进行测试,例如:

func start -p 17071

部署 Azure Functions 应用程序

1、安装 Azure CLI 命令行工具

由于部署Azure Functions的时候,需要先安装好Azure CLI命令行工具,并在 az login 登入后选好默认订用帐户(Subscription)

choco install azure-cli -y
az login --use-device-code
az account set -s "Microsoft Azure Sponsorship"

2、创建 Function App

你可以通过 Azure Portal先建立好 Serverless 的 Function App,或是直接通过Azure CLI创建的Function App服务。

以下命令先创建“资源群组”,再建立“储存体帐户”,再创建名为 mydemo1func 的 Serverless Function App:

az group create -n demo1 -l japaneast
az storage account create -n myfuncstor1 -g demo1 -l japaneast --sku Standard_LRS
az functionapp create -n mydemo1func -s myfuncstor1 -g demo1 -c japaneast --functions-version 3

參考微软文章:

3、将现有 Function App 发行到 Azure Functions 中

func azure functionapp publish mydemo1func --publish-local-settings

加入 –publish-local-settings 参数可以将你的 local.settings.json 配置设定全部自动同步到 Azure 上,执行过程如果看到 Would you like to overwrite value in azure? [yes/no/show] 提示,请记得输入 no 才对,否则你的 AzureWebJobsStorage 配置会被设定为本机开发环境的Storage连接字串!

转载需保留链接来源:VCBeta.CN » Azure SignalR和Azure Functions实现无服务器架构

赞 (0)