让我们构建一个实时服务仪表板!
我们的服务仪表板将实时向我们显示真实数据。 它将以接近实时,异步,非阻塞的方式向我们展示服务器和微服务上发生的事情。
看看什么是完整的客户端可以像在这里 。
在这里可以看到服务器的演示。
我们将使用AngularJS框架和大量带有大量实时数据的实时图表来构建此仪表盘的较小版本。 我们还将使用.NET 4.5中的SignalR和Web 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
您将需要下载以下文件:
在我们的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进行设置非常简单。
只需通过转到File -> New -> Project
来设置一个空的Web应用程序,然后选择Web作为模板类型。
然后只需右键单击该项目,转到Manage Nuget Packages
并搜索并下载jQuery,AngularJS,Bootstrap,D3和SignalR JavaScript客户端。
下载并安装它们之后,您应该在Scripts和Contents文件夹中全部看到它们。 此外,在已安装的Nuget软件包下,您将看到以下内容:
最后,Nuget不包含Epoch,ng-epoch和n3图表库,因此您需要手动添加它们。 只需按照上一节中详述的步骤进行操作即可。
现在我们准备编写一些代码。
首先,让我们创建基本的index.html
文件,该文件将包含我们的Angular JavaScript代码。
AngularJS - SignalR - ServiceDashboard
这里发生了一些事情。 首先,我们要添加所有依赖项,以便它们加载。 其次,我们引用了一些尚不存在的新文件(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;
}]);
这段代码使用了流行的on
和off
(无需关闭,因为我们在这里不需要它)订阅模式,并使用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标签下添加以下内容:
Memory Performance
{{currentRamNumber}}
这只会为服务器创建一个回推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值数据:
chart-class
是指D3.js的配色方案, chart-height
是您所怀疑的颜色, chart-stream
是从SignalR服务器返回的数据。
有了这些,我们应该可以实时看到图表:
现在让我们将大量数据点连接到此图表,并从n3-pie框架中添加其他整个图表(因为谁不喜欢派!)。
要从n3-pie框架添加饼图,只需将以下内容添加到我们的控制器中:
$scope.data = [
{ label: 'CPU', value: 78, color: '#d62728', suffix: '%' }
];
当然,该value
将由SignalR服务器更新。 您可以在控制器的完整代码中看到这一点。
我们还应该花点时间考虑一下完整的代码以获取我们的观点 。
而且我们应该在屏幕上看到以下数据:
我们已经看到,Angular可以非常容易地连接到SignalR –通过简单地将端点插入AngularJS服务或工厂即可。 AngularJS工厂是一种与SignalR通信的封装机制。 谁知道AngularJS和.NET在“结婚”时会很好地协同工作?
我将介绍一些.NET代码,该代码可以在后端进行通信。 (您可以在此处找到源代码。)
首先开始构建服务器代码,您需要在Visual Studio解决方案中运行SignalR。 为此,只需遵循ASP.NET上的出色教程,即可运行基本的SignalR解决方案。 ( 这是最简单的一个。)
一旦启动并运行,将C# Hub
类更改为以下内容:
public class PerformanceHub : Hub
{
public void SendPerformance(IList 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 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的实时图表框架上。
可以在此处看到客户端最终版本的演示,您可以从此处获取代码。
可以在此处看到服务器最终版本的演示,您可以从此处获取代码。
希望您喜欢这个演练。 如果您尝试过类似的操作,请在评论中告诉我们!
From: https://www.sitepoint.com/build-real-time-signalr-dashboard-angularjs/