AJAX启用性能计数器Web控件

分享于 

34分钟阅读

Web开发

  繁體
Screenshot - performancecountercontrol.gif

介绍

让潜在客户浏览你的Office的印象比任何东西都多? 为关键服务器显示实时性能数据的大屏幕。 我开玩笑的只是部分的: 这使得公司看起来非常专业,而且如果服务器出现了问题,那么在几秒钟内可以对网络操作员。 一个理想的表示 medium 是一个网页,因为你可以实现网络操作的多个版本。 在这个主题( 如 ,本文在 4 GuysFromRolla ) 上有几个基本的web控件和教程,但是没有什么特别的特性。 这种令我感到惊讶的是,我决定自己开发和开发你今天在这里看到的ASP.NET web控件。

背景

首先,需要对 Windows 环境中的性能度量数据进行概述。 Windows 机器提供了一种以性能计数器的形式捕获这里数据的理想机制: 他们监控系统组件,如处理器。内存。磁盘 I/O。重要服务实例。等等 和发布各种性能相关数据的。 特定性能计数器由4 个数据组成,其中第一个数据计数器是计数器的类别 NAME。 This到它的,是性能计数器的常规类别,可以是内存,处理器,网络接口,等等 第二个数据点是这里监视器为它的读取数据的计数器的计数器 NAME: 例如在内存类别中,你可以监视这些数据块,如可用的mb。页/秒。系统代码总字节和其他数据。 我们有实例 NAME,它是我们监视的计数器的实例。 许多性能计数器不是实例,但是一些针对特定组件成员的性能度量。 例如,在类别流程和计数器 % 过程中,我们有一个实例,以及当前在系统上运行的每个进程。 性能计数器的final 数据是计算机 NAME,它指示网络上的哪台计算机存在我们检查的性能计数器。 这意味着你可以从单个中央机器中观察许多计算机的性能计数器,然后以网页的形式发布这些度量。 如果你希望机器( 你可以收集到惊人数量的数据) 上有 List 性能计数器的,那么只需转到 Start-> 运行,然后输入 perfmon.exe。 然后你可以右键点击图表,然后去添加计数器来获得可用计数器的完整 List。 .NET 通过 System.Diagnostics 命名空间中的PerformanceCounter 类提供对性能计数器的出色编程访问,我们将在这个项目的( 你可以在这里找到这个类的msdn的覆盖。) 中大量使用它。

实现

首先,这里项目的先决条件: 你将需要安装microsoft框架,在这里可以找到它。 这不是它的AJAX回调功能,ASP.NET 2.0已经提供了一个非常基本的形式( 这个项目足够了),而是在我的大多数项目中使用。 它们给客户端代码更加组织。C# 感觉,还提供了增强的功能,比如 inheritence。名称空间和枚举。 虽然对于这个项目没有必要全面了解这个功能,但有兴趣的读者可以在这里找到更多的信息。

基本功能

对于这个项目,90%是令人惊讶的实现,但最后 10%包含了一些有趣的ASP.NET 呈现进度。 首先,我们将讨论这里控件提供的功能。 方法在非常基本的级别上,将性能计数器数据呈现到屏幕上,并在预先确定的时间间隔之后通过AJAX调用自动。 可以在 PerformanceCounterDisplayType 枚举中设置性能计数器数据可以在屏幕上执行的形式:

publicenum PerformanceCounterDisplayType
{
 ///<summary>/// Display the current value of the counter.///</summary> Text,
 ///<summary>/// Display a single, updating bar representing the percentage value of the /// counter between its floor and ceiling.///</summary> ProgressBar,
 ///<summary>/// Display a line graph of the counter's historical data.///</summary> LineGraph,
 ///<summary>/// Display a histogram (bar graph) of the counter's historical data.///</summary> Histogram
}

可以看到,我们可以将数据呈现为简单文本标签。进度条。历史线图或者历史直方图( ( 条形图) )。 控件的属性很多你希望为web控件找到,以及定义它呈现的性能计数器。 CategoryNameCounterNameInstanceNameMachineName 完成后者,每个都为在 background 部分中讨论的性能计数器定义了一个数据点。 RefreshInterval 属性定义这里控件的数据刷新尝试之间的时间( 以秒为单位)。 属性的绘制进度栏。直方图和行图用于绘制进度条,以便我们可以确定计数器的性能值可能会下降到 in。 直方图和折线图用于确定屏幕上显示多少个历史数据点。 在返回性能计数器的实际值之前,Modifier 属性是一个值,它用于在不同度量单位之间进行转换,例如字节和字节数。 Invert 属性指示是否在返回之前反转计数器( 从天花板上减去它)的性能值。 控件的WidthHeightCssClass 属性控制屏幕上控件的外观。 在屏幕上显示计数器之前,使用 FormatString 属性来设置计数器的性能值。 Orientation 属性用于进度条,以确定进度条"增长"中的direction ( 水平或者垂直)。 finally,OnChange 属性表示在刷新计数器性能数据时应调用的客户端JavaScript函数。 函数应该接受客户端 PerformanceCounter 对象( 稍后将被覆盖) 作为参数,并可以用于向用户显示警告消息,更改控件的CSS类等等。

渲染和数值计算

现在我们开始讨论有趣的实现细节,首先是实际构建控件层次结构,以在屏幕上显示性能计数器数据。 因此,我们必须查看 CreateChildControls() 方法:

protectedoverridevoid CreateChildControls()
{
 string hashKey = categoryName + ":" + counterName + ":" + instanceName + 
 ":" + machineName;
 switch (displayType)
 {
 // In the case of a text display type, simply create a Label child objectcase PerformanceCounterDisplayType.Text:
 Label textLabel = new Label();
 textLabel.CssClass = cssClass;
 Controls.Add(textLabel);
 break;
 // In the case of a progress bar, create a label whose width and height // represent the maximum width and height of the progress bar and // another label that will represent the current value of the countercase PerformanceCounterDisplayType.ProgressBar:
 Label containerLabel = new Label();
 Label progressBarLabel = new Label();
 containerLabel.Width = width;
 containerLabel.Height = height;
 progressBarLabel.CssClass = cssClass;
 progressBarLabel.Style["position"] = "absolute";
 progressBarLabel.Style["overflow"] = "hidden";
 // Set the actual progress bar's style attributes according to // whether it grows horizontally or verticallyif (orientation == RepeatDirection.Vertical)
 {
 progressBarLabel.Style["bottom"] = "0px";
 progressBarLabel.Width = width;
 progressBarLabel.Height = 1;
 }
 else {
 progressBarLabel.Style["left"] = "0px";
 progressBarLabel.Height = height;
 progressBarLabel.Width = 1;
 }
 containerLabel.Controls.Add(progressBarLabel);
 Controls.Add(containerLabel);
 break;
 // In the case of a histogram, create a container panel and a // sub-container panel, the latter of which will hold the histogram // barscase PerformanceCounterDisplayType.Histogram:
 int sampleWidth = Convert.ToInt32(Math.Floor(
 (double)(width/historyCount)));
 Panel containerPanel = new Panel();
 Panel subContainerPanel = new Panel();
 containerPanel.Width = width;
 containerPanel.Height = height;
 containerPanel.Style["position"] = "relative";
 // Two panels are necessary so that the histogram bars are rendered // properly subContainerPanel.Width = width;
 subContainerPanel.Style["position"] = "absolute";
 subContainerPanel.Style["bottom"] = "0px";
 Label[] histogramEntries = new Label[historyCount];
 for (int i = 0; i < historyCount; i++)
 {
 histogramEntries[i] = new Label();
 histogramEntries[i].CssClass = cssClass;
 histogramEntries[i].Width = sampleWidth;
 histogramEntries[i].Height = 1;
 histogramEntries[i].Style["position"] = "absolute";
 histogramEntries[i].Style["left"] = Convert.ToString(i * 
 sampleWidth) 
 + "px";
 histogramEntries[i].Style["bottom"] = "0px";
 histogramEntries[i].Style["overflow"] = "hidden";
 subContainerPanel.Controls.Add(histogramEntries[i]);
 }
 containerPanel.Controls.Add(subContainerPanel);
 Controls.Add(containerPanel);
 break;
 // In the case of a line graph, simply create a container panel: the // vector graphics JavaScript library will take care of actually // drawing the graphcase PerformanceCounterDisplayType.LineGraph:
 Panel lineContainerPanel = new Panel();
 lineContainerPanel.Width = width;
 lineContainerPanel.Height = height;
 lineContainerPanel.Style["position"] = "relative";
 Controls.Add(lineContainerPanel);
 break;
 }
 // Create the performance counter object if it doesn't already exist and, // if it's a non-instantaneous counter, add a sample to the history for it; // we add a sample now so that when we go to actually calculate the initial // value in the PreRenderComplete event handler, sufficient time will have // passed:// 1. All controls call CreateChildControls(), adding the initial counter // sample to the history// 2. PreRenderComplete event handler is called for the first control: it // gets the counter value from the Value property// 3. If the Value property detects that less than 100 milliseconds have // passed since the history entry was added in CreateChildControls, it // sleeps for 100 milliseconds and then gets another sample// 4. At that point, the calls to the Value property for all other // controls will occur after the necessary 100 milliseconds and no more // pausing will be necessaryif (performanceCounter == null && 
!performanceCounters.ContainsKey(hashKey))
 {
 performanceCounter = new PerformanceCounter(categoryName, counterName, 
 instanceName, machineName);
 performanceCounters[hashKey] = performanceCounter;
 if (!IsPerformanceCounterInstantaneous(performanceCounter))
 {
 performanceCounterSamples[hashKey] = new List<CounterSample>();
 performanceCounterSamples[hashKey].Add(
 performanceCounter.NextSample());
 }
 }
 // If the counter object already exists, just use the copy from the cacheelseif (performanceCounter == null && 
 performanceCounters.ContainsKey(hashKey))
 performanceCounter = performanceCounters[hashKey];
 // Add an event handler to the page's pre-render complete event Page.PreRenderComplete += new EventHandler(Page_PreRenderComplete);
 base.CreateChildControls();
}

控件层次结构的创建相对简单: 如果我们创建一个对象,我们创建一个容器 Panel 对象,表示进度条的最大可能大小,然后创建代表图形的maximum对象,然后创建一个容器对象,我们稍后会绘制一个容器对象。 有趣的代码在函数的最后几行中。 如果 PerformanceCounter 对象尚不存在,我们将创建它。 如果已经存在,则使用存储在缓存中,这是为了防止在有多个控件的情况下创建重复的对象。 我们还捕获那些需要它的计数器的初始计数器样本。 这是因为某些计数器需要多个样本来计算它们的值。 一个很好的例子就是网络流量: 我们需要捕获两个示例,因为我们需要两个示例之间的时间和网卡传输的字节数。 一旦我们有这两个值,我们就可以计算出在那个时间段上传输/秒的字节数。 但是,我们需要在两个阶段过程中这样做,并确保示例之间经过了最少的时间: 如果我们捕获两个样本,只有一个非常少的时间在两者之间运行,我们无法计算统计值。 因这里我们在 ASP.NET 呈现过程中进行了两个阶段,以确保我们能够计算精确的结果。 评论有这个过程,但我将在这里再次讨论:

  • 页面中的每个 PerformanceCounterControl 都将调用 CreateChildControls() 方法。
  • 对于非即时计数器,我们将把计数器的初始样本添加到历史数据中。
  • 在页面发生 PreRenderComplete 事件时,每个控件都将调用它的Page_PreRenderComplete() 处理程序方法。
  • 第一个非瞬时控件将调用 Value 属性以获取计数器的当前值。
  • Value 属性内,我们检查在步骤 2中添加了第一个历史条目之后,是否至少有 100毫秒的时间。
  • 如果没有,那么我们会睡觉 100毫秒,然后采取计数器样本并根据两个历史示例计算值。
  • 此时,每次非即时控制都会看到超过 100毫秒的时间超过第 2步,因此在获得新计数器样本和计算值之前,不需要线程睡眠。

上述 Page_PreRenderComplete() 方法如下所示:

protectedvoid Page_PreRenderComplete(object sender, EventArgs e)
{
 // Add the client-side performance counter initialization snippetif (!Page.ClientScript.IsStartupScriptRegistered(GetType(), 
 "PerformanceCounterInitialize"))
 Page.ClientScript.RegisterStartupScript(GetType(), 
 "PerformanceCounterInitialize", 
 "var performanceCounters = new Array();n", true);
 string childIDs = "";
 // Get the list of client IDs for all child elements for client-side // registrationswitch (displayType)
 {
 case PerformanceCounterDisplayType.Text:
 case PerformanceCounterDisplayType.LineGraph:
 childIDs = "'" + Controls[0].ClientID + "'";
 break;
 // For progress bars and histograms, get the value of performance // counters and render the data appropriatelycase PerformanceCounterDisplayType.ProgressBar:
 if (orientation == RepeatDirection.Horizontal)
 ((Label)Controls[0].Controls[0]).Width = 
 Convert.ToInt32(Math.Max(Math.Min(Math.Floor(
 (Value - floor)/ceiling * width), width), 1));
 else ((Label)Controls[0].Controls[0]).Height = 
 Convert.ToInt32(Math.Max(Math.Min(Math.Floor(
 (Value - floor)/ceiling * height), height), 1));
 childIDs = "'" + Controls[0].Controls[0].ClientID + "'";
 break;
 case PerformanceCounterDisplayType.Histogram:
 foreach (Control control in Controls[0].Controls[0].Controls)
 childIDs += "'" + control.ClientID + "',";
 ((Label)Controls[0].Controls[0].Controls[historyCount - 1]).Height = 
 Convert.ToInt32(Math.Max(Math.Min(Math.Floor(
 (Value - floor)/ceiling * height), height), 1));
 childIDs = childIDs.Substring(0, childIDs.Length - 2);
 break;
 }
 // Register the necessary client script resources and emit the registration // snippet for this control Page.ClientScript.GetCallbackEventReference(this, null, 
 "RenderPerformanceCounter", "'" + ClientID + "'");
 Page.ClientScript.RegisterClientScriptResource(typeof(ScriptManager), 
 "MicrosoftAjax.js");
 Page.ClientScript.RegisterClientScriptResource(typeof(ScriptManager), 
 "MicrosoftAjaxWebForms.js");
 Page.ClientScript.RegisterClientScriptResource(GetType(), 
 "Stratman.Web.UI.Resources.PerformanceCounter.js");
 Page.ClientScript.RegisterStartupScript(GetType(), ClientID, 
 String.Format("performanceCounters['{0}'] = new" +
 "Stratman.Web.UI.PerformanceCounter('{0}'," + 
 "Stratman.Web.UI.PerformanceCounterDisplayType.{1}," +
 "{2}, {3}, {4}, {5}, {6}, {7}, '{8}'," + 
 "Sys.UI.RepeatDirection.{9}, {10}, '{11}', '{12}'," + 
 "'{13}', '{14}', '{15}', {16}, {17});n", ClientID, 
 displayType.ToString(), Value, refreshInterval, width, 
 height, ceiling, floor, formatString, orientation, 
 historyCount, cssClass, categoryName, counterName, 
 instanceName, machineName, 
 (onChange == ""? "null" : onChange), childIDs), true);
 // Only include the vector graphics library if the control is a line graphif (displayType == PerformanceCounterDisplayType.LineGraph)
 Page.ClientScript.RegisterClientScriptResource(GetType(), 
 "Stratman.Web.UI.Resources.VectorGraphics.js");
}

如前所述,通过设置文本显示的文本内容或者调整进度条或者直方图的子元素来设置控件的值。 它还注册必要的客户端脚本包括并发出用于实例化控件的客户端实例的JavaScript代码 Fragment。 finally,Value 属性如下所示:

publicfloat Value
{
 get {
 // Make sure that child controls have been created first EnsureChildControls();
 string hashKey = categoryName + ":" + counterName + ":" + instanceName + 
 ":" + machineName;
 // If the performance counter is instantaneous (i.e. requires no // historical data) then just get the current value for it, modify/invert // it if necessary, and return itif (IsPerformanceCounterInstantaneous(performanceCounter))
 {
 float calculatedValue = (invert? 
 ceiling - performanceCounter.NextValue() : 
 performanceCounter.NextValue());
 return (modifier!= 0? calculatedValue * modifier : calculatedValue);
 }
 else {
 List<CounterSample> samples = performanceCounterSamples[hashKey];
 // Get the previous sample for this counter CounterSample previousSample = 
 samples[performanceCounterSamples[hashKey].Count - 1];
 // If less than 100 milliseconds have passed since the previous sample // was obtained and we have only one historical sample then sleep the // thread; this is to ensure that enough time has passed between // samples for a statistically relevant value to be calculatedif (performanceCounterSamples[hashKey].Count == 1 && 
 DateTime.Now.ToFileTimeUtc() - previousSample.TimeStamp100nSec < 
 1000000)
 Thread.Sleep(100);
 // If more than 100 milliseconds have passed, then obtain a new sample // and record it in this history data; otherwise we just use the // previous two samples to calculate the valueif (DateTime.Now.ToFileTimeUtc() - previousSample.TimeStamp100nSec >= 
 1000000)
 {
 if (performanceCounterSamples[hashKey].Count >1)
 performanceCounterSamples[hashKey].RemoveAt(0);
 samples.Add(performanceCounter.NextSample());
 }
 // Calculate the value, modify/invert it if necessary, and then return // itfloat calculatedValue = CounterSample.Calculate(samples[0], 
 samples[1]);
 calculatedValue = (invert? ceiling - calculatedValue : 
 calculatedValue);
 return (modifier!= 0? calculatedValue * modifier : calculatedValue);
 }
 }
}

如果我们简单地调用 GetNextValue() 方法,那么我们就会调用 PerformanceCounter 方法,否则我们必须在两个计数器示例之间进行计算。 如果是这种情况,我们只有一个历史示例( 例如。 这是第一次实例化这里计数器,并且尚未向屏幕呈现任何内容,然后检查收集的第一个示例: 如果它是 LESS 超过 100毫秒,那么我们就把线程休眠。 然后,收集另一样本,使用这两个样本计算值,根据需要修改/反转它,并返回它。 但是,如果我们有多个历史样本,那么我们检查最后一个示例上的时间戳。 如果收集的时间超过 100毫秒,我们收集另一个样本并根据先前收集的样本和我们刚才收集的样本计算值。 否则,我们只使用前两个示例来计算值( 不收集一个新的)。

客户端脚本和回调

驱动脚本脚本和客户端更新的JavaScript资源文件是 ResourcesPerformanceCounter.js。 控件的客户端类是 Stratman.Web.UI.PerformanceCounter ( 请注意命名空间,这是通过 ASP.NET AJAX扩展实现的)。 它有所有常见的方法,包括 Render() 在屏幕上呈现计数器数据,SetCssClass() 更新控件使用的CssClassRender() 方法的代码如下所示:

Stratman.Web.UI.PerformanceCounter.prototype.Render = function()
{
 // For text displays, simply call String.format()if (this.Type == Stratman.Web.UI.PerformanceCounterDisplayType.Text)
 document.getElementById(this.ChildElementIDs[0]).innerHTML = 
 String.format(this.FormatString, this.Value);
 // For progress bars, just set the width or height (depending on the // orientation) of the progress barelseif (this.Type == 
 Stratman.Web.UI.PerformanceCounterDisplayType.ProgressBar)
 {
 if (this.Orientation == Sys.UI.RepeatDirection.Vertical)
 document.getElementById(this.ChildElementIDs[0]).style.height = 
 Math.round(Math.max(Math.min(
 (this.Value - this.Floor)/this.Ceiling, 1) * this.Height, 1)) + 
 "px";
 else document.getElementById(this.ChildElementIDs[0]).style.width = 
 Math.round(Math.max(Math.min(
 (this.Value - this.Floor)/this.Ceiling, 1) * this.Width, 1)) + 
 "px";
 }
 // For histograms, set the height of each bar to the value of the // corresponding entry in the history dataelseif (this.Type == 
 Stratman.Web.UI.PerformanceCounterDisplayType.Histogram)
 {
 for (var i = 0; i <this.HistoryCount; i++)
 document.getElementById(this.ChildElementIDs[i]).style.height = 
 Math.max(Math.min(
 (this.HistorySamples[i] - this.Floor)/this.Ceiling, 1) * 
 this.Height, 1) + 
 "px";
 }
 // For line graphs, call the drawLine() function in the vector graphics // library for each entry in the history dataelseif (this.Type == 
 Stratman.Web.UI.PerformanceCounterDisplayType.LineGraph)
 {
 var sampleWidth = Math.round(this.Width/this.HistoryCount);
 var lineHTML = "";
 this.VectorGraphics.setCssClass(this.CssClass);
 this.VectorGraphics.clear();
 for (var i = 0; i <this.HistoryCount - 1; i++)
 this.VectorGraphics.drawLine((i * sampleWidth), 
 this.Height - Math.round(Math.min((this.HistorySamples[i] - 
 this.Floor)/this.Ceiling, 1) * (this.Height - 1)) - 1, 
 ((i + 1) * sampleWidth), 
 this.Height - Math.round(Math.min((this.HistorySamples[i + 1] - 
 this.Floor)/this.Ceiling, 1) * (this.Height - 1)) - 1);
 this.VectorGraphics.paint();
 }
}

实际 C# 代码完全相似的控件函数的呈现,与行图的除外。 在这里,我们使用zorn的Walter优秀的矢量图形库,来处理绘制线条图。 在客户端 PerformanceCounter 类的构造函数中,我们创建了一个 jsGraphics 对象,通过在控件层次结构中创建的DOM对象。 绘制行,我们只需调用历史数据中的每个条目,将它们链接起来,然后调用实际将行呈现到容器控件。

为了更新性能计数器数据,我们使用了对服务器的批处理AJAX调用。 为了完成这一点,在客户端 PerformanceCounter 对象的构造函数结束时,对 RegisterForRefresh 方法进行了调用。 这里方法如下所示:

function RegisterForRefresh(id, refreshInterval)
{
 // Create the refresh hash entry for this interval if it doesn't already // exist and make a call to setTimeout() to initialize the callbackif (refreshHash[refreshInterval] == null)
 {
 refreshHash[refreshInterval] = [];
 window.setTimeout("UpdatePerformanceCounters(" + refreshInterval + 
 ")", refreshInterval * 1000);
 }
 Array.add(refreshHash[refreshInterval], id);
}

所有这些函数都是为这里刷新间隔创建一个 array 条目,如果它不存在,则调用 setTimeout() 以在指定的间隔之后。 然后,将这里性能计数器控件的客户端ID添加到 array的刷新间隔。 我们为什么要这样做? 要避免大量不必要的调用到服务器,请执行以下操作: 我们批处理应在给定时间间隔刷新的所有控件的to,并单独调用服务器获取所有值。 这样,我们避免了客户端不必要的调用和服务器上不必要的负载。 无论如何,UpdatePerformanceCounters() 方法的代码如下所示:

function UpdatePerformanceCounters(refreshInterval)
{
 // Assemble the list of control IDs to update into a comma-delimited // stringvar performanceCounterIDs = refreshHash[refreshInterval][0];
 for (var i = 1; i <refreshHash[refreshInterval].length; i++)
 performanceCounterIDs += "," + refreshHash[refreshInterval][i];
 // Make the callback to the web server and call setTimeout() again to // re-register this callback WebForm_DoCallback(refreshHash[refreshInterval][0], performanceCounterIDs, 
 RenderPerformanceCounters, refreshInterval, null, 
 false);
 window.setTimeout("UpdatePerformanceCounters(" + refreshInterval + ")", 
 refreshInterval * 1000);
}

它所做的就是为这个间隔增加控制 id,通过 WebForm_DoCallback() 对控件进行回调,并通过另一个 setTimeout() 调用注册自己。 现在我们回到服务器上: 为了使控件类能够通过 WebForm_DoCallback() 进行回调,它必须实现 ICallbackEventHandler 接口,我们已经完成了。 它需要实现两种方法,首先是 RaiseCallbackEvent(),它负责对所需的参数( 我们的案例中的控制id字符串) 进行任何解析。 这里方法如下所示:

publicvoid RaiseCallbackEvent(string eventArgument)
{
 performanceCounterIDs = eventArgument.Split(',');
}

因此,它只是将控制ID字符串分割成单个的。 下一个方法是 GetCallbackResult(),它负责实际处理回调:

publicstring GetCallbackResult()
{
 StringBuilder performanceCounterValues = new StringBuilder();
 // Find each control and get its updated valueforeach (string performanceCounterID in performanceCounterIDs)
 {
 PerformanceCounterControl performanceCounterControl = 
 (PerformanceCounterControl)FindControlByClientID(
 performanceCounterID, Page);
 performanceCounterValues.Append(performanceCounterControl.Value + ",");
 }
 string returnValue = performanceCounterValues.ToString();
 return returnValue.Substring(0, returnValue.Length - 1);
}

这个也很简单: 它只搜索我们要更新的每个控件的控件层次结构,获取它的最新值并将它的连接到一个增长的字符串。 然后将值返回给客户端,在这里时调用 List 回调的成功事件处理程序 RenderPerformanceCounters():

function RenderPerformanceCounters(response, context)
{
 var performanceCounterValues = response.split(",");
 // Loop through each control that we're to updatefor (var i = 0; i <performanceCounterValues.length; i++)
 {
 var performanceCounter = performanceCounters[refreshHash[context][i]];
 performanceCounter.Value = 
 Number.parseInvariant(performanceCounterValues[i]);
 // Update the history data for line graphs and histogramsif (performanceCounter.Type == 
 Stratman.Web.UI.PerformanceCounterDisplayType.LineGraph || 
 performanceCounter.Type == 
 Stratman.Web.UI.PerformanceCounterDisplayType.Histogram)
 {
 var samples = performanceCounter.HistorySamples;
 for (var x = 0; x <performanceCounter.HistoryCount - 1; x++)
 performanceCounter.HistorySamples[x] = samples[x + 1];
 samples[performanceCounter.HistoryCount - 1] = 
 performanceCounter.Value;
 }
 // Render the control performanceCounter.Render();
 // Invoke the value changed event handler, if it's setif (performanceCounter.OnChange)
 performanceCounter.OnChange(performanceCounter);
 }
}

现在,我们已经进入了更新过程的最后一。 我们将值字符串分为单个值,然后更新控件值,包括直方图和线条图的历史数据。 然后调用每个控件的Render() 方法,如果设置了该方法,则调用 OnChange 事件处理程序函数。

用法

为了实现演示目的,我包含了一个名为PerformanceCountersTest的测试网站,它的中包含了源下载。 没有太多的事情可以通过它来完成: 它是许多 Windows 任务管理器性能监视器功能的一个实现,它主要由一些布局CSS和一组 PerformanceCounterControl 实例组成。 使用这个控件开发丰富的性能监视网页是一个很好的指示。

历史记录

2007-03-04初始出版物。


相关文章