Azure Function SignalR 输出绑定 - 如何在 NET Core Web App 中接收消息?

Azure Function SignalR Output Binding - How to receive message in NET Core Web App?

我相信我对 Azure SignalR 在部署使用 SignalR 输出绑定的 Azure 函数时的工作方式有一些误解。

我的环境:

1 - Azure Function 执行一些工作,然后将消息发送到 SignalR 服务。

2 - NET Core Web 应用程序连接到 Azure SignalR 服务并且应该收到这些通知。

MS 示例似乎表明 Net Core Webb App 必须协商令牌并直接与 Azure Function 连接才能接收这些消息。但这就是我现在想要实现的目标。

鉴于这两个应用程序都使用安全 key/connection 字符串与 Azure SignalR 服务连接,我本以为 Azure 会充当中间人并在一端与另一端之间传递消息。 这是正确的还是我完全错了?

作为一个基本示例:Azure Function 的配置与下面类似,我从服务总线队列接收消息,收到此消息后,我将相同的消息发送到 Azure SignalR 服务:

[FunctionName("My-Function-Name")]
public async Task RunAsync([ServiceBusTrigger(
    "sampleQueue", Connection = "AzureServiceBusConnetion")]
    string message,
    [SignalR(HubName = "macroHub")] IAsyncCollector<SignalRMessage> signalRMessage,
    ILogger logger)
{
    logger.LogInformation($"C# ServiceBus queue trigger function processed message: {message}");

    await signalRMessage.AddAsync(
            new SignalRMessage
            {
                Target = "newMessage",
                Arguments = new[] { message }
            });
}

因此,从上面可以看出,不需要任何方法来协商或提供与带有令牌的外部客户端的任何连接,即我不希望必须将我的 NET Core Web 应用程序直接连接到此 Azure 功能。该消息只是发送到 Azure Signal 服务,Azure 会获取此消息。

接下来,使用 Nuget 包将 NET Core Web App 连接到 Azure SignalR,并配置本地集线器 class。在我的 NET Core Web 应用程序的一个页面上,我通过 JavaScript 连接到本地集线器,并且应该收到从 Azure Function 发出后通过 Azure 路由的消息。

NET 核心 Web 应用程序:

public class MacroHub : Hub
{
    public async Task ReceiveMessage(string message)
    {
        await Clients.All.SendAsync("newMessage", message);
    }
}

Net Core Web 应用程序中的示例 JS:

const connection = new signalR.HubConnectionBuilder()
            .withUrl("/macroHub")
            .withAutomaticReconnect()
            .build();

connection.on("newMessage", (message) => {

    alert(message)
});

这并不是关于代码本身的真正问题,之前我在一个 NET Core Web 应用程序中本地使用 SignalR 时它工作正常,但我正在尝试按顺序使用 Azure SignalR 服务通过云连接从 Azure Function 发送的消息,然后在 Net Core Web App 中接收这些消息。

到目前为止我尝试过的所有内容都没有显示任何内容activity,Azure 门户的 Web 应用程序也没有显示任何收到消息的日志或事件。

问题已解决!所以我的假设实际上是正确的,但我在测试场景的代码中遇到了一些问题,所以下面我展示了一个工作示例。我已经改编了一个 MS 示例,因此代码示例与我原来的问题中显示的不同,但至少下面的工作可以从这里向前推进...

重要提示:在挠头后头发已经没有了!为了使下面的示例正常工作,我必须将 Azure 中 SignalR 服务的模式从无服务器更改为默认模式:

Azure 函数应用代码

Function.cs 文件:

下面的函数是 运行 触发计时器,所以我每 5 秒生成一条简单的测试消息“这是一条测试消息!”我们将其发送到 Azure SignalR 服务。

namespace CSharp
{
    public static class Function
    {
        [FunctionName("broadcast")]
        public static async Task Broadcast([TimerTrigger("*/5 * * * * *")] TimerInfo myTimer,
        [SignalR(HubName = "serverless")] IAsyncCollector<SignalRMessage> signalRMessages)
        {
            await signalRMessages.AddAsync(
                new SignalRMessage
                {
                    Target = "newMessage",
                    Arguments = new[] { "This is a test message!" }
                });
        }
    }
}

Azure 函数 local.settings.json 文件:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "AzureSignalRConnectionString": "Endpoint=https://myDomain-signalr.service.signalr.net;AccessKey=youarenothavingmysecretkey=;Version=1.0;"
  },
  "ConnectionStrings": {}
}

.NET 6 网络应用程序代码

首先确保从 Nuget 安装了 SignalR 客户端库:

然后安装 SignalR 客户端库或引用 CDN

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>

或参见link:https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-6.0&tabs=visual-studio

创建连接字符串,我用appsettings.json代替test/development

"Azure": {
    "SignalR": {
      "ConnectionString": "Endpoint=https://myDomain-signalr.service.signalr.net;AccessKey=youarenothavingmysecretkey=;Version=1.0;"
    }
  }

然后创建 SignalR Hub Class:

public class Serverless : Hub
{
    public async Task NewMessage(string message)
    {
        await Clients.All.SendAsync("newMessage", message);
    }
}

Program.cs 文件(我使用的是 .NET 6 最小启动模式,即没有 Startup.cs 文件)

// Add the Azure SignalR Service.
builder.Services.AddSignalR().AddAzureSignalR(builder.Configuration["Azure:SignalR:ConnectionString"]);

var app = builder.Build();

app.MapHub<Serverless>("/serverless");

.NET 6 Web 应用程序中的示例网页(JavaScript 代码部分)

const connection = new signalR.HubConnectionBuilder()
        .withUrl("/serverless")
        .withAutomaticReconnect()
        .build();

connection.on("newMessage", (message) => {

    alert(message)
});

// We need an async function in order to use await, but we want this code to run immediately,
// so we use an "immediately-executed async function"
(async () => {
    try {
        await connection.start();
    }
    catch (e) {
        console.error(e.toString());
    }
})();

测试示例:

我首先启动我的 .NET Core Web 应用程序并加载包含上述 javaScript 代码的示例网页,JS 代码连接到本地 SignalR Hub,后者又连接到 Azure SignalR 服务,所以本质上我与 Azure 有联系:

浏览器调试显示已建立连接:

然后我启动 Azure Function App(在本地,尚未部署到 Azure)

最后,每次 Azure 函数向 Azure SignalR 服务发送测试消息时,我的 Web 应用程序都会弹出测试警报消息:

轰! :)