【问题标题】:Using SignalR to broadcast data to Azure Web App from Azure ServiceBus Message Queue使用 SignalR 将数据从 Azure ServiceBus 消息队列广播到 Azure Web App
【发布时间】:2017-01-20 22:51:12
【问题描述】:

我有一个 Azure ServiceBus 消息队列,它从成熟的 Windows 应用程序接收消息。这部分效果很好(每分钟数十条消息)。我知道消息在那里。 ServiceBus 命名空间类似于 dtx-ns,队列名称类似于 dtxrm001。

我还有一个完全开发的 MVC 5 Azure Web 应用程序。我想连接一个新的视图页面,它使用 SignalR 从 Azure 服务总线消息队列接收消息。

我想要做的就是通过访问网页来查看 ServiceBus 队列消息(来自运行成熟 Windows 应用程序的任何人)。

这基本上是传入的传感器数据,比如来自传感器的温度。

在购买了 5 本书 SignalR 并在这方面花费了 3 周之后,我想我需要一些指导。是否将 Web 角色添加到我的 Azure MVC 应用?我要添加工人角色吗?我是否使用依赖注入和 SignalRBackplaneMessageBus?

似乎是一项简单的任务,但我现在不知所措,在这一点上,方法是什么?当您真正进入示例然后尝试连接来自 ServiceBus 消息队列的消息的实际广播时,似乎没有任何意义。

这是我尝试过的 MVC Web 应用启动的一些代码:

    Dim cn1 As String = "Endpoint=sb://dtx-ns.servicebus.windows.net/;SharedAccessKeyName=myname;SharedAccessKey=mykey"
    Dim config As New ServiceBusScaleoutConfiguration(cn1, "dtx1")
    config.TopicCount = 3
    config.BackoffTime = New TimeSpan(1, 0, 1)
    config.IdleSubscriptionTimeout = New TimeSpan(1, 0, 0)
    config.MaximumMessageSize = 20000
    config.MaxQueueLength = 50

    GlobalHost.DependencyResolver.UseServiceBus(config)
    GlobalHost.Configuration.TransportConnectTimeout = TimeSpan.FromSeconds(10)

    app.UseCors(CorsOptions.AllowAll)
    app.MapSignalR()

上面显示的端点是服务总线消息队列的连接字符串的一部分。如果上面看起来是正确的,那么我该怎么做才能对 Hub 进行编程以从这个连接发送消息?

我需要网络角色吗?我是否需要在添加到当前 MVC 应用程序的项目中以某种方式实现“背板集线器”?我被难住了。

【问题讨论】:

  • 这里有很多信息,但我仍然不知道您到底想做什么?你想让 SignalR 发布到 ServiceBus 吗?
  • 不。我要做的就是登录到网页(客户端)并查看通过 Azure 服务总线消息队列传来的数据。
  • 我看到相反的情况,当一条消息进入 ServiceBus 广播给 SignalR 时?
  • 让我说得更具体一些。假设全世界有 50 台钻机。在他们钻探时,有很多传感器,比如温度、压力等……这些传感器数据被发送到 Azure 服务总线消息队列。我希望能够查看网页并查看一些显示此传入数据的图形仪表。我将过滤以仅显示用户单击的工作站点中的数据(我将在 JavaScript 中执行此操作)。
  • @DavidMakogon 聊天会更好,但我不同意,在了解详细信息后,我认为可以给出一个很好的答案。如果可以编辑问题以包含详细信息,那么我们可以清理 cmets。

标签: c# asp.net-mvc azure signalr servicebus


【解决方案1】:

您正在使用 scale out SignalR via ServiceBus 的代码(所谓的 ServiceBus 背板 - 示例中令人困惑的 Web 角色实际上是您的 MVC 应用程序,它可以在应用服务中运行或作为 WebRole 运行。您的 SignalR Hub 确实存在在一般情况下与您的网络应用程序一起使用)。这对于在 Azure Web 应用程序上运行时横向扩展 SignalR 本身是正确的。但不是你要求的。

您要求的是从 ServiceBus 读取消息并将它们广播到 SignalR 连接的客户端。这是完全不同的用例。您可以使用以下几个选项来实现您的目标:

在这两种情况下,SignalR 拥有"native" .NET client这一最不为人知的事实将帮助您(您可以从任何 .NET 应用程序连接到 SignalR,包括在 Windows Phone 或桌面上运行的应用程序)。

我在这里没有提供任何代码示例,但我想我已经解决了你的疑问。


【讨论】:

  • 我正在检查您的答案...我相信这是我需要的解决方案,就像 Ashley Medway 在上面的 cmets 中提出我需要做的事情一样。完成尽职调查后,我会回来并将其标记为答案,谢谢。
【解决方案2】:

我需要让我的 Azure MVC-5 Web 应用程序从我的 Azure 服务总线消息队列中读取,然后通过 SignalR 将数据发送到所有客户端网页,已解决。

在代码方面,它非常简单和优雅。这是我的解决方案:

首先,我的 Visual Studio 解决方案只有一个项目——主要的 Web 应用项目。在项目的根目录中,我有我的 SignalR 集线器类,名为 djwHub;

Imports Microsoft.AspNet.SignalR
Imports Microsoft.AspNet.SignalR.Hubs
Imports Microsoft.AspNet.SignalR.Messaging
Imports System.Threading.Tasks
Imports Microsoft.ServiceBus
Imports Microsoft.ServiceBus.Messaging

<HubName("djwHub")>
Public Class djwHub
    Inherits Hub

    Private connectString As String = "Endpoint=sb://mynamespace.servicebus.windows.net/;SharedAccessKeyName=myKeyName;SharedAccessKey=myKey"
    Private queueName As String = "myQueueName"
    Private m_count As Integer

    Public Sub beginReadingMessageQue()
        Dim rf = MessagingFactory.CreateFromConnectionString(connectString)

        Dim taskTimer = Task.Factory.StartNew(Async Function()
                                                  Dim receiver = Await rf.CreateMessageReceiverAsync(queueName, ReceiveMode.PeekLock)

                                                  While True
                                                      Dim timeNow As String = DateTime.Now.ToString()
                                                      Clients.All.sendServerTime(timeNow)

                                                      Try
                                                          Dim message = Await receiver.ReceiveAsync(TimeSpan.FromSeconds(5))

                                                          If message IsNot Nothing Then
                                                              Dim messageBody = message.GetBody(Of [String])()

                                                              Clients.All.sendNewMessage(messageBody)

                                                              Await message.CompleteAsync
                                                          Else
                                                              'no more messages in the queue
                                                              Exit Try
                                                          End If
                                                      Catch e As MessagingException
                                                          If Not e.IsTransient Then
                                                              'Console.WriteLine(e.Message)
                                                              'Throw
                                                          End If
                                                      End Try

                                                      'Delaying by 1/2 second.
                                                      Await Task.Delay(500)
                                                  End While

                                              End Function, TaskCreationOptions.LongRunning)
    End Sub
End Class

现在,我的 MVC-5 Web 应用的根目录中没有 Startup 类。相反,我的启动发生在位于我的 App_Start 文件夹中的 IdentityConfig.vb 类中。所以这就是我放 app.MapSignalR() 的地方,如图所示;

Imports Microsoft.AspNet.Identity
Imports Microsoft.Owin
Imports Microsoft.Owin.Security.Cookies
Imports Owin
Imports myApp.Users.Infrastructure
Imports Microsoft.Owin.Security.Google
Imports Microsoft.AspNet.SignalR
Imports Microsoft.Owin.Cors
Imports Microsoft.AspNet.SignalR.ServiceBus

Namespace Users
    Public Class IdentityConfig
        Public Sub Configuration(app As IAppBuilder)
            app.CreatePerOwinContext(Of AppIdentityDbContext)(AddressOf AppIdentityDbContext.Create)
            app.CreatePerOwinContext(Of AppUserManager)(AddressOf AppUserManager.Create)
            app.CreatePerOwinContext(Of AppRoleManager)(AddressOf AppRoleManager.Create)

            app.UseCookieAuthentication(New CookieAuthenticationOptions() With { _
                .AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, _
                .LoginPath = New PathString("/Account/Login") _
            })

            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie)
            app.MapSignalR()
        End Sub
    End Class
End Namespace

剩下的唯一部分是网页视图页面。请注意,我现在在网页上有 6 个 FusionChart 仪表。但是您应该能够挑选出与 djwHub 对话的 SignalR 函数调用;

@Code
    Layout = Nothing
End Code

<head>
    <title>Drillers Readout - Job #@ViewBag.JobNumber</title>
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/jqueryui")

    <script src="~/Scripts/jquery.signalR-2.2.1.js"></script>
    <script src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
    <script src="~/signalr/hubs"></script>
    <script type="text/javascript">
        (function () {
            var currentIndex = -1;
            var lastTime;
            var GxDisplay;
            var GyDisplay;
            var GzDisplay;
            var HxDisplay;
            var HyDisplay;
            var HzDisplay;

            var myHub = $.connection.djwHub;
            $.connection.hub.logging = true;

            myHub.client.sendNewMessage = function (message) {
                var myArray = JSON.parse(message);

                currentIndex += 1;
                //var dataId = myArray.dataCount;

                if (currentIndex == 0) {
                    lastTime = new Date(myArray.time1);
                } else {
                    var newTime = new Date(myArray.time1);
                    if (newTime >= lastTime) {
                        var dataId = myArray.dataCount;
                        var Gx = myArray.Gx;
                        var Gy = myArray.Gy;
                        var Gz = myArray.Gz;
                        var Hx = myArray.Hx;
                        var Hy = myArray.Hy;
                        var Hz = myArray.Hz;

                        lastTime = newTime;

                        GxDisplay.feedData("value=" + Gx);
                        GyDisplay.feedData("value=" + Gy);
                        GzDisplay.feedData("value=" + Gz);
                        HxDisplay.feedData("value=" + Hx);
                        HyDisplay.feedData("value=" + Hy);
                        HzDisplay.feedData("value=" + Hz);

                        $("#newMessage").text('#' + dataId + ": " + lastTime + " Gx=" + Gx.toFixed(2) + " Gy=" + Gy.toFixed(2) + " Gz=" + Gz.toFixed(2)
                            + " Hx=" + Hx.toFixed(2) + " Hy=" + Hy.toFixed(2) + " Hz=" + Hz.toFixed(2));
                    }
                }
            };

            $.connection.hub.start().done(function () {
                myHub.server.beginReadingMessageQue();
            });

            myHub.client.sendServerTime = function (serverTime) {
                $("#newTime").text(serverTime);
            };

            FusionCharts.ready(function () {
                GxDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'GxChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Gx",
                            "subcaption": "",
                            "lowerLimit": "-2000",
                            "upperLimit": "2000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-2000",
                                "maxValue": "2000",
                                "code": "#ADD8E6"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcGx",
                                "value": "0"
                            }]
                        }
                    }
                });

                GyDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'GyChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Gy",
                            "subcaption": "",
                            "lowerLimit": "-2000",
                            "upperLimit": "2000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-2000",
                                "maxValue": "2000",
                                "code": "#ADD8E6"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcGy",
                                "value": "0"
                            }]
                        }
                    }
                });

                GzDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'GzChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Gz",
                            "subcaption": "",
                            "lowerLimit": "-2000",
                            "upperLimit": "2000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-2000",
                                "maxValue": "2000",
                                "code": "#ADD8E6"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcGz",
                                "value": "0"
                            }]
                        }
                    }
                });

                HxDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'HxChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Hx",
                            "subcaption": "",
                            "lowerLimit": "-100000",
                            "upperLimit": "100000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-100000",
                                "maxValue": "100000",
                                "code": "#ff1493"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcHx",
                                "value": "0"
                            }]
                        }
                    }
                });

                HyDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'HyChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Hy",
                            "subcaption": "",
                            "lowerLimit": "-100000",
                            "upperLimit": "100000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-100000",
                                "maxValue": "100000",
                                "code": "#ff1493"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcHy",
                                "value": "0"
                            }]
                        }
                    }
                });

                HzDisplay = new FusionCharts({
                    type: 'angulargauge',
                    renderAt: 'HzChart',
                    width: '250',
                    height: '175',
                    dataFormat: 'json',
                    dataSource: {
                        "chart": {
                            "caption": "Hz",
                            "subcaption": "",
                            "lowerLimit": "-100000",
                            "upperLimit": "100000",
                            "lowerLimitDisplay": "",
                            "upperLimitDisplay": "",
                            "showValue": "1",
                            "valueBelowPivot": "1",
                            "theme": "fint"
                        },
                        "colorRange": {
                            "color": [{
                                "minValue": "-100000",
                                "maxValue": "100000",
                                "code": "#ff1493"
                            }]
                        },
                        "dials": {
                            "dial": [{
                                "id": "fcHz",
                                "value": "0"
                            }]
                        }
                    }
                });

                GxDisplay.render();
                GyDisplay.render();
                GzDisplay.render();
                HxDisplay.render();
                HyDisplay.render();
                HzDisplay.render();
            });
        }());
    </script> 
</head>
<body>
    <div id="newTime"></div><br />
    <ul id="newMessage"></ul>
    <div id="gCharts">
        <div id="GxChart"></div>
        <div id="GyChart"></div>
        <div id="GzChart"></div>       
    </div>
    <div id="hCharts">
        <div id="HxChart"></div>
        <div id="HyChart"></div>
        <div id="HzChart"></div>
    </div>
</body>

在此特别感谢 astaykov 和 Ashley Medway 以及 Microsoft 让这一切成为可能!

【讨论】:

  • 感谢您发布解决方案。 cmets 对原始问题的讨论也很有帮助。
猜你喜欢
  • 2021-10-31
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 2015-04-22
  • 2021-10-18
相关资源
最近更新 更多