使用AngularJS构建实时SignalR仪表板

让我们构建一个实时服务仪表板!

服务仪表板屏幕截图

我们的服务仪表板将实时向我们显示真实数据。 它将以接近实时,异步,非阻塞的方式向我们展示服务器和微服务上发生的事情。

看看什么是完整的客户端可以像在这里

在这里可以看到服务器的演示。

我们将使用AngularJS框架和大量带有大量实时数据的实时图表来构建此仪表盘的较小版本。 我们还将使用.NET 4.5中的SignalRWeb API库来构建服务。

技术架构

客户端

AngularJS开箱即用地强制执行出色的应用程序开发实践。 一切都被注入了,这意味着依赖性的耦合度很低。 另外,Angular在视图,模型和控制器之间有很大的分隔。

Angular通过允许服务器端代码保持较小,可管理和可测试的方式来赞美.NET。 服务器端代码仅被利用来发挥其优势-这是繁重的工作。

服务器

将SignalR与用于.NET 4.5的Web API的使用非常类似于将Node.js与Socket.IO的使用,并且允许从服务器到订阅客户端的相同类型的非阻塞,异步推送。 SignalR在下面使用Web套接字,但是由于它抽象了通信,因此在Angular中运行时,它将退回到客户端浏览器支持的任何技术。 (例如,对于较旧的浏览器,它可能会回退到长时间轮询。)

此外,借助动态标签和Json.NET的魔力,.NET框架将JavaScript视为一流的公民。 实际上,在JavaScript中使用Web API和SignalR技术通常比通过本机.NET客户端更容易使用,因为它们是在考虑JavaScript的情况下构建的。

肉和土豆

进行设定

本教程中使用的所有AngularJS代码都可以在这里找到。

我将使用您最喜欢的文本编辑器和普通文件夹,以及用于创建项目的Visual Studio进行创建。

用纯文本文件设置

文件夹和文件结构如下所示:

root
    app     (Angular application specific JavaScript)
    Content (CSS etc.)
    Scripts (Referenced JavaScript etc.)
    ...
    index.html

主要依赖

您将需要下载以下文件:

  • jQuery (选择“下载压缩的,生产版的jQu​​ery 2.1.1”链接)
  • AngularJS (单击大的下载选项,然后单击最新版本的Angular 1.3。+)
  • 引导程序 (单击“下载引导程序”选项)
  • SignalR (单击右侧的“下载ZIP”按钮)
  • D3.js (点击页面下方的“ d3.zip”链接)
  • 时代 (点击“下载v0.6.0链接”)
  • ng-epoch (单击右侧的“下载ZIP”按钮)
  • n3-pie (单击右侧的“下载ZIP”按钮)

在我们的Scripts文件夹中,我们需要:

  • jquery-2.1.1.min.js
  • angular.min.js
  • bootstrap.min.js
  • jquery.signalR.min.js
  • d3.min.js
  • epoch.min.js
  • pie-chart.min.js

在我们的Content文件夹中:

  • bootstrap.min.css
  • epoch.min.css

使用Visual Studio进行安装

如果文本文件对您来说过于简单,则通过Visual Studio进行设置非常简单。

只需通过转到File -> New -> Project来设置一个空的Web应用程序,然后选择Web作为模板类型。

Visual Studio角度安装

然后只需右键单击该项目,转到Manage Nuget Packages并搜索并下载jQuery,AngularJS,Bootstrap,D3和SignalR JavaScript客户端。

下载并安装它们之后,您应该在Scripts和Contents文件夹中全部看到它们。 此外,在已安装的Nuget软件包下,您将看到以下内容:

下载的Nuget软件包

最后,Nuget不包含Epoch,ng-epoch和n3图表库,因此您需要手动添加它们。 只需按照上一节中详述的步骤进行操作即可。

让我们来编写我们的应用程序

现在我们准备编写一些代码。

首先,让我们创建基本的index.html文件,该文件将包含我们的Angular JavaScript代码。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>AngularJS - SignalR - ServiceDashboard</title>
  <link rel="stylesheet" href="Content/bootstrap.min.css" />
  <link rel="stylesheet" href="Content/epoch.min.css" />

  <script src="Scripts/jquery-1.11.0.js"></script>
  <script src="Scripts/bootstrap.min.js"></script>
  <script src="Scripts/jquery.signalR-2.1.2.min.js"></script>
  <script src="Scripts/angular.min.js"></script>

  <script src="Scripts/d3.min.js"></script>
  <script src="Scripts/epoch.min.js"></script>
  <script src="Scripts/ng-epoch.js"></script>
  <script src="Scripts/pie-chart.min.js"></script>

  <script src="app/app.js"></script>
  <script src="app/services.js"></script>
  <script src="app/directives.js"></script>
  <script src="app/controllers.js"></script>

</head>
  <body ng-app="angularServiceDashboard">
  </body>
</html>

这里发生了一些事情。 首先,我们要添加所有依赖项,以便它们加载。 其次,我们引用了一些尚不存在的新文件(app文件夹中的所有文件)。 接下来我们将写这些。

让我们进入我们的app文件夹并创建我们的app.js文件。 这是一个非常简单的文件。

'use strict';

var app = angular.module('angularServiceDashboard', ['ng.epoch','n3-pie-chart']);
app.value('backendServerUrl', 'http://sitepointsignal.cloudapp.net/');

该文件为我们做一些事情。 它设置了我们的主要应用程序模块angularServiceDashboard并注入了两个外部引用ng.epoch (这是我们针对Angular的Epoch.js指令)和n3-pie-chart (这是为Angular制作的图表库),结构化的。

如果您注意到了,我们还为backendServerUrl注入了一个值,该值当然托管在其他地方,我们计划在这里使用。

让我们创建一个将绑定到服务器URL的服务工厂类。 这将是我们在HTML中引用的services.js文件,它将进入app文件夹:

'use strict';

app.factory('backendHubProxy', ['$rootScope', 'backendServerUrl', 
  function ($rootScope, backendServerUrl) {

    function backendFactory(serverUrl, hubName) {
      var connection = $.hubConnection(backendServerUrl);
      var proxy = connection.createHubProxy(hubName);

      connection.start().done(function () { });

      return {
        on: function (eventName, callback) {
              proxy.on(eventName, function (result) {
                $rootScope.$apply(function () {
                  if (callback) {
                    callback(result);
                  }
                 });
               });
             },
        invoke: function (methodName, callback) {
                  proxy.invoke(methodName)
                  .done(function (result) {
                    $rootScope.$apply(function () {
                      if (callback) {
                        callback(result);
                      }
                    });
                  });
                }
      };
    };

    return backendFactory;
}]);

这段代码使用了流行的onoff (无需关闭,因为我们在这里不需要它)订阅模式,并使用Angular工厂为我们的应用封装了与SignalR的所有通信。

最初,这段代码似乎有些让人不知所措,但是当我们构建控制器时,您会更好地理解它。 它所做的只是输入我们后端SignalR服务器的URL和SignalR集线器名称。 (在SignalR中,您可以使用同一服务器中的多个集线器来推送数据。)

此外,此代码还允许位于其他位置的SignalR Server通过on方法调用我们的应用程序。 它允许我们的应用通过invoke方法在SignalR Server内部调用函数。

接下来,我们需要控制器,它将控制器中的数据绑定到我们的范围。 让我们在应用程序文件夹中创建一个名为controllers.js的文件。

'use strict';

app.controller('PerformanceDataController', ['$scope', 'backendHubProxy',
  function ($scope, backendHubProxy) {
    console.log('trying to connect to service')
    var performanceDataHub = backendHubProxy(backendHubProxy.defaultServer, 'performanceHub');
    console.log('connected to service')
    $scope.currentRamNumber = 68;

    performanceDataHub.on('broadcastPerformance', function (data) {
      data.forEach(function (dataItem) {
        switch(dataItem.categoryName) {
          case 'Processor':
            break;
          case 'Memory':
            $scope.currentRamNumber = dataItem.value;
            break;
          case 'Network In':
            break;
          case 'Network Out':
            break;
          case 'Disk Read Bytes/Sec':
            break;
          case 'Disk Write Bytes/Sec':
            break;
          default:
            //default code block
            break;           
        }
      });     
    });
  }
]);

这个控制器在这里做一些事情。 它创建了我们的Angular Service对象并绑定了一个回调函数,以便服务器在我们的控制器中可以调用一些东西。

您将看到,每次服务器回叫我们时,我们都在遍历服务器返回的JSON数组。 然后,对于每种性能类型,我们都有一个switch语句。 现在,我们将设置RAM并重新整理其余部分。

就我们的指令而言,我们的Epoch图实际上只需要一个。 我们将使用一个名为ng-epoch.js的开源指令,该指令已经在存根index.html文件中提供了引用。

我们可以将所有这些图表拆分为不同的指令,使用一些模板并使用UI-Router ,但是我们将在这里保持简单,并将所有视图转储到我们的index.html文件中。

现在,将我们的视图添加到index.html文件中。 为此,我们可以在body标签下添加以下内容:

<div class="row" ng-controller="PerformanceDataController">
  <div class="col-lg-3 col-md-6">
    <div class="panel panel-dashboard">
      <div class="center">Memory Performance</div>
        <div class="panel-body">
          <div class="huge">{{currentRamNumber}}</div>
          <div class="clearfix"></div>
        </div>
      </div>
    </div>
  </div>
</div>

这只会为服务器创建一个回推RAM数据的地方。 数据将首先进入我们的服务,然后进入控制器,最后到达视图。

它看起来应该像这样:

服务器推送的性能编号

现在让我们添加一些图表,这是我们真正想要做的。 我们将为epoch.js时间轴添加一个名为timestamp的变量。 我们还将添加一个称为chartEntry的数组,该数组将绑定到我们的epoch.ng指令。

var timestamp = ((new Date()).getTime() / 1000) | 0;
var chartEntry = [];

然后,让我们在switch语句中映射数据,并添加其余必需的epoch.js数据项。 当然,我们可以进一步扩展(例如使用更多的函数和过滤器),但是为了使本教程更简单。

'use strict';

app.controller('PerformanceDataController', ['$scope', 'backendHubProxy',
  function ($scope, backendHubProxy) {
    ...

    $scope.currentRamNumber = 68;
    $scope.realtimeArea = [{ label: 'Layer 1', values: [] }];

    performanceDataHub.on('broadcastPerformance', function (data) {
      var timestamp = ((new Date()).getTime() / 1000) | 0;
      var chartEntry = [];

        data.forEach(function (dataItem) {
          switch(dataItem.categoryName) {
            case 'Processor':
              $scope.cpuData = dataItem.value;
              chartEntry.push({ time: timestamp, y: dataItem.value });
              console.log(chartEntry)
              break;
            case 'Memory':
              $scope.currentRamNumber = dataItem.value;
              break;
            case 'Network In':
              break;
            case 'Network Out':
              break;
            case 'Disk Read Bytes/Sec':
              break;
            case 'Disk Write Bytes/Sec':
              break;
            default:
              //default code block
              break;
          }
        });
        $scope.realtimeAreaFeed = chartEntry;
      });
      $scope.areaAxes = ['left','right','bottom'];
  }
]);

我们的控制器看起来更加充实了。 我们向范围添加了一个realtimeAreaFeed ,我们将通过ng-epoch指令将其绑定到我们的视图,并且我们还向区域添加了areaAxes ,该区域决定了面积图的布局。

现在,将指令添加到index.html并显示输入的CPU值数据:

<div class="row" ng-controller="PerformanceDataController">
  <div class="panel-body" ng-controller="PerformanceDataController">
    	
    <epoch-live-area chart-class="category10" 
                     chart-height="200" 
                     chart-data="realtimeArea" 
                     chart-stream="realtimeAreaFeed" 
                     chart-axes="areaAxes">
    </epoch-live-area>
  </div>
</div>

chart-class是指D3.js的配色方案, chart-height是您所怀疑的颜色, chart-stream是从SignalR服务器返回的数据。

有了这些,我们应该可以实时看到图表:

遇到第一个图表

现在让我们将大量数据点连接到此图表,并从n3-pie框架中添加其他整个图表(因为谁不喜欢派!)。

要从n3-pie框架添加饼图,只需将以下内容添加到我们的控制器中:

$scope.data = [
  { label: 'CPU', value: 78, color: '#d62728', suffix: '%' }
];

当然,该value将由SignalR服务器更新。 您可以在控制器的完整代码中看到这一点。

我们还应该花点时间考虑一下完整的代码以获取我们的观点

而且我们应该在屏幕上看到以下数据:

内存性能1397,CPU性能23%

我们已经看到,Angular可以非常容易地连接到SignalR –通过简单地将端点插入AngularJS服务或工厂即可。 AngularJS工厂是一种与SignalR通信的封装机制。 谁知道AngularJS和.NET在“结婚”时会很好地协同工作?

服务器的核心方面

我将介绍一些.NET代码,该代码可以在后端进行通信。 (您可以在此处找到源代码。)

首先开始构建服务器代码,您需要在Visual Studio解决方案中运行SignalR。 为此,只需遵循ASP.NET上的出色教程,即可运行基本的SignalR解决方案。 ( 是最简单的一个。)

一旦启动并运行,将C# Hub类更改为以下内容:

public class PerformanceHub : Hub
{
  public void SendPerformance(IList<PerformanceModel> performanceModels)
  {
    Clients.All.broadcastPerformance(performanceModels);
  }

  public void Heartbeat()
  {
    Clients.All.heartbeat();
  }

  public override Task OnConnected()
  {
    return (base.OnConnected());
  }
}

一旦更改了Hub类,Visual Studio就会抱怨,您将需要添加一个性能模型(由于Json.NET,它会在服务器推出时自动转换为JSON):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json;

namespace SignalrWebService.Models
{
  public class PerformanceModel
  {
    [JsonProperty("machineName")]
    public string MachineName { get; set; }

    [JsonProperty("categoryName")]
    public string CategoryName { get; set; }

    [JsonProperty("counterName")]
    public string CounterName { get; set; }

    [JsonProperty("instanceName")]
    public string InstanceName { get; set; }

    [JsonProperty("value")]
    public double Value { get; set; }
  }
}

JsonProperty元数据只是告诉Json.NET在将此模型转换为JSON时自动将属性名称转换为小写。 JavaScript喜欢小写。

让我们添加一个PerformanceEngine类,该类将推送给将收听真实性能数据的任何人。 引擎通过SignalR将这些消息发送到异步后台线程上的所有侦听客户端。

由于篇幅太长,您可以在我们的GitHub repo上找到代码

此代码基本上推动性能度量的阵列出到在每个订阅任何人while迭代。 这些性能指标被注入到构造函数中。 从服务器推送的速度在构造函数参数pollIntervalMillis

请注意,如果您要使用OWIN作为自托管主机托管SignalR,则此方法会正常工作;如果您使用网络工作者,则应可以正常工作。

当然,最后要做的是在服务OnStart()Startup类中的某个地方启动后台线程。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Owin;
using System.Threading.Tasks;
using Microsoft.Owin;
using SignalrWebService.Performance;
using Microsoft.Owin.Cors;
using Microsoft.AspNet.SignalR;
using SignalrWebService.Models;

[assembly: OwinStartup(typeof(SignalrWebService.Startup))]

namespace SignalrWebService
{
  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      app.UseCors(CorsOptions.AllowAll);
      var hubConfiguration = new HubConfiguration();
      hubConfiguration.EnableDetailedErrors = true;
      app.MapSignalR(hubConfiguration);


      PerformanceEngine performanceEngine = new PerformanceEngine(800, GetRequiredPerformanceMonitors());
      Task.Factory.StartNew(async () => await performanceEngine.OnPerformanceMonitor());
    }
  }
}

在后台线程上启动监视的两行(我敢肯定您已经猜到了)是我们实例化PerformanceEngine和调用OnPerformanceMonitor()

现在,我知道您可能会认为我正在将服务器中的数据随机化,这是事实。 但是要推送实际指标,只需使用Windows提供的System.Diagnostics库和PerformanceCounter 。 我正在尝试保持简单,但是代码如下所示:

public static readonly IEnumerable<PerformanceCounter> ServiceCounters = new[]
{
  //http://weblogs.thinktecture.com/ingo/2004/06/getting-the-current-process-your-own-cpu-usage.html
  new PerformanceCounter("Processor Information", "% Processor Time", "_Total"),
  new PerformanceCounter("Memory", "Available MBytes"),
  new PerformanceCounter("Process", "% Processor Time", GetCurrentProcessInstanceName(), true),
  new PerformanceCounter("Process", "Working Set", GetCurrentProcessInstanceName(), true)
};

结论

我们已经看到了如何通过Angular来使用SignalR数据,并且已经将该数据连接到了Angular的实时图表框架上。

可以在此处看到客户端最终版本的演示,您可以从此处获取代码。

演示客户端运行

可以在此处看到服务器最终版本的演示,您可以从此处获取代码。

SignalR / Web API 2.0服务

希望您喜欢这个演练。 如果您尝试过类似的操作,请在评论中告诉我们!

From: https://www.sitepoint.com/build-real-time-signalr-dashboard-angularjs/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值