65.9K
CodeProject 正在变化。 阅读更多。
Home

COMET:适用于 ASP.NET Web 应用程序的多客户端连续 PUSH 更新网格 - 第 2 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (36投票s)

2009年4月4日

CPOL

10分钟阅读

viewsIcon

173534

downloadIcon

4522

可伸缩、高性能、低延迟的反向 AJAX 网格。这是一个基于 COMET 的多客户端网格控件。该网格控件可以 COMET 更新到多个具有不同数据需求的客户端。包含完整的性能报告。

注意:您的反馈对我来说很重要。如果您觉得本文有用,请在底部留言并投票。

引言

大自然是如此的神奇,几乎没有两个实体是相同的;每个实体都是独一无二的。没有两张人脸是相同的;每个人都是独一无二的。同样,业务需求也从不相同,而是独一无二的。这带来了为每个企业设计定制软件系统的需求,即使它们属于同一行业——例如金融或医疗保健。也就是说,没有两家在同一领域竞争的金融公司会对同一套软件感到满意。它们的方法不同,要求不同,设计不同。这使得其中一家比另一家更成功。

在关于 COMET 网格控件的文章(点击链接)之后,本文侧重于在此领域更进一步,处理一个特殊案例。在上一篇文章中,我们讨论了一个通用 COMET 网格控件。在本文中,我们将讨论如何使用 COMET 方法更新具有不同数据的多个客户端。也就是说,假设我们有一个显示天气信息的 ASP.NET 页面。一个 Web 客户端,例如 Alice,住在纽约,另一个 Web 客户端,例如 Bob,住在芝加哥。显然,我们需要向 Alice 和 Bob 发布不同的天气数据。上一篇文章的 COMET 网格控件只能用相同的数据更新所有客户端。这不能很好地满足我们的需求。我们需要用不同的数据更新 Alice 和 Bob。本文解释了一个具有该功能的网格控件:一个 COMET 多客户端更新网格控件。一如既往,提供完整的工作示例供下载。

一个更专业的例子:考虑一家拥有许多交易台的金融公司。每个交易台都专注于在特定领域进行交易。Bob 是一名交易员,负责名为“投资组合 1”的投资组合。Alice 恰好管理着 15 个这样的投资组合,例如投资组合 1 到 15。考虑他们需要一个 ASP.NET Web 应用程序来每时每刻跟踪他们的盈亏(Profit/Loss),随时发生。如上所述,上一篇文章中介绍的 COMET 网格在此处不能满足我们的需求。这是因为 Alice 需要 15 个投资组合的总盈亏,而 Bob 只关心他负责的“投资组合 1”。因此,我们需要一个 COMET 网格,可以将不同的数据发布到不同的客户端。使用这个多客户端 COMET 网格,这个业务案例可以在几个小时内投入生产。此案例的示例也提供下载。

示例:ASP.NET Web 应用程序中的 COMET 多客户端网格

示例 1:以下是一个示例盈亏交易应用程序的显示页面。请注意,Alice 正在获取*总计*(投资组合 1 到 15)的盈亏更新,而 Bob 只获取投资组合 1 的盈亏更新。另请注意 Bob 和 Alice 浏览器的地址栏。它们都指向相同的地址(因此是相同的网格控件),但发布的数据(使用 COMET)不同。

Bob 的浏览器窗口

BobView.gif

Alice 的浏览器窗口

AliceView.gif

示例 2:下面显示的是一个示例天气应用程序的显示页面。请注意,Alice 获得的更新与 Bob 不同。但代码只使用一个 COMET 网格控件来更新 Alice 和 Bob。

Alice 的浏览器窗口

AliceWeather.gif

Bob 的浏览器窗口

BobWeather.gif

您想如何阅读?

最好按顺序阅读文章。然而,并非所有人都处于相同的情况。因此,这里有一个简要指南,可以帮助您快速找到所需内容

  • 如果您想快速获取控件以在您的项目中使用,那么您只需阅读“使用代码”部分。
  • 如果您对设计感到好奇,请阅读“COMET 多客户端网格设计说明”部分。
  • 如果您想了解本项目中使用的数据结构,请在此处阅读有关 MultiMap 泛型集合的内容
  • “参考文献”部分建议进一步阅读本主题。

使用代码

先决条件:Visual Studio 2008。

快速演示:从顶部链接下载示例项目。在 VS2008 中运行它。复制地址栏中的链接。打开另一个浏览器实例。将链接粘贴到地址栏中。现在您可以在 Browser_1 实例中看到 Alice 的天气信息,在 Browser_2 实例中看到 Bob 的天气信息。

通过三个简单步骤使用此基于 COMET 的网格控件

  1. 将此控件添加到您的项目中
    1. 右键单击“工具箱”| 选择“选择项...”| 单击“浏览”。然后,浏览选择下载的(从本文第一行)程序集(GridControlCometAjax.dll)。
    2. 从“工具箱”中,将新添加的控件拖放到您的网页上。
  2. 在您的 web.config 文件的 'httpHandlers' 部分中添加一个异步处理程序,如下所示
  3. <add verb="GET, POST" path="GridControlCometAjax.ashx" 
      type="BK.Util.GridAsynchHandler, GridControlCometAjax" validate="false">
  4. 在您的代码隐藏文件(例如,Default.aspx.cs)中,在您的 'Page_Load' 方法中添加以下行。请记住,您需要先调用 LoadControl 方法,然后才能使用 Dyn 方法(下面将解释)进行任何客户端更新调用。
  5. protected void Page_Load(object sender, EventArgs e)
    {
       // Your code goes here.
       // Assuming the control instance's name
       // is GridControlCometAjax1, add the below line:
       GridMultiClientControlComet1.LoadControl(this);
    }

好的,就是这样。现在开始使用该控件。您使用任何以“Dyn”为前缀的方法在此网格控件中更新的任何内容都将自动传播到客户端。此外,这些方法是线程安全的。因此,您可以从多个线程调用这些方法。

用例 1:如何声明并向(或初始化)此多客户端 COMET 网格控件添加元素。

using System;
using BK.Util;
    
namespace WeatherWatch 
{
   public partial class _Default 
   {
      // Other declarations can go here
      protected global::BK.Util.GridMultiClientControlComet 
                GridMultiClientControlComet1;   
      // Other declarations go here.
   }
}

文件 default.aspx.cs

using System;
using BK.Util;
    
namespace WeatherWatch
{
    public partial class _Default : System.Web.UI.Page
    {  
        protected void Page_Load(object sender, EventArgs e)
        {
            GridMultiClientControlComet1.LoadControl(this);
    
            // Error checks are avoided for brevity
            int rowCount, colCount;
            // Get the Number of rows available to update
            GridMultiClientControlComet1.DynGetRowCount(
                     this.Session.SessionID, ref rowCount);
            // Get the number of Columns available to update
            GridMultiClientControlComet1.DynGetColCount(
                     this.Session.SessionID, ref colCount);

            // Modify values of each Row element to value RowItem*ColItem;
            for (int RowItem = 0; RowItem < rowCount; RowItem++)
            {
                for (int ColItem = 0; ColItem < colCount; ColItem++)
                {
                    int Update = RowItem * ColItem;
                    GridMultiClientControlComet1.DynModifyTableItem(this.Session.SessionID, 
                                   RowItem, ColItem, Update.ToString(), false);
                }
            }
           
        }
   }
}

在上面的代码中,第一行加载控件[如步骤 (3) 所述]。接下来的几行循环填充元素。

用例 2:如何更新此 COMET 网格控件的背景颜色。

代码解释可在行内找到

// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is Color to update
// Parameter 5 states if the update need to be propogated to the client or not.
GridMultiClientControlComet1.DynModifyTableFColor(SessionID, 0, 0, 
                      System.Drawing.Color.LightBlue, false);

用例 3:如何更新此 COMET 网格控件的前景或文本颜色。

代码解释可在行内找到

// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is Color to update
// Parameter 5 states if the update need to be propogated to the client or not.
GridMultiClientControlComet1.DynModifyTableFColor(session1, 0, 0, 
                             System.Drawing.Color.DarkBlue, false);

用例 4:如何动态修改背景图片。

代码解释可在行内找到

// Parameter 1 is Session Id
// Parameter 2 is Row 
// Parameter 3 is Column
// Parameter 4 is ralative Image path to update
// Parameter 5 states if the update need to be propogated to the client or not.
    
GridMultiClientControlComet1.DynModifyBackImage(session1, 0, 0, "Img2/Rain.gif", false);

您可能已经注意到,使用此 COMET 网格控件与上一篇文章(常规 COMET)控件的唯一区别是 SessionID 参数。此参数告诉网格将更新发送到哪个客户端。然后所有其他客户端都将排除在更新之外。

性能结果

我使用微软的 Web 应用程序压力测试工具进行了一项压力测试。完整的报告已作为可下载文件附加在上面的链接中。但是,下面给出了一个概述。我使用了 100 个线程,每个线程有 10 个套接字,这意味着 1000 个并发连接。我使用了更新客户端有两个网格控件的测试应用程序。结果如下

Performance.jpg

COMET 多客户端网格设计说明

如果您只是打算使用此 COMET 网格控件,则可以跳过此部分。但是,如果您好奇,请继续阅读。

简而言之,如下图所示,当客户端连接时,调用被挂起。当需要从服务器端更新数据时,被挂起的特定客户端的调用被处理。通过填充需要更新的数据来完成此调用。其他客户端未更新,其调用保持挂起。

此外,维护更新会以预定的时间间隔对所有客户端执行,以便所有连接的客户端都不会因为浏览器超时而断开连接。

详细解释

  • 步骤 1:客户端向服务器发出调用。
  • 步骤 2:服务器返回包含控件(包括此 COMET 网格)的 ASP.NET 页面。
  • 步骤 3:COMET 网格向服务器发出 AJAX 样式调用以获取更新。
  • 步骤 4:调用未由服务器完成,而是保持挂起。
  • 步骤 5:服务器等待任何服务器端事件,例如对任何客户端的数据更新或超时。
  • 步骤 6:如果服务器端发生事件,则发送客户端更新。其他客户端调用仍保持挂起。
  • 步骤 7:如果发生超时,所有客户端调用都将完成。
  • 步骤 8:从步骤 (3) 继续。

让我们看看上面步骤中有趣的部分。最有趣的部分是保持客户端的调用挂起。这是通过编写基于 IHttpAsyncHandler 的处理程序实现的。我们所需要做的就是重写三个方法:BeginProcessRequestEndProcessRequestProcessRequest。我们在 ProcessRequest 中不做任何事情。在 BeginProcessRequest 中,我们所做的就是:将调用置于挂起状态。这是一个重要的步骤,因为这会将 ASP.NET 线程返回到线程池,从而释放服务器资源。因此,同一个线程可以用于服务任何进一步连接的客户端。代码如下所示

public IAsyncResult BeginProcessRequest(HttpContext Context, 
                    AsyncCallback Callback, object ExtraData)
{
   string ClientId = Context.ApplicationInstance.Request.QueryString[0]; //Line 1
   string SessionId = Context.ApplicationInstance.Request.QueryString[1]; // Line 2
   ResponseDetails respDet = clientDetails.GetFirstItem(ClientId);   // Line 3

   GridAsynchResult asyncResultToAdd = new GridAsynchResult(SessionId, respDet, 
                    Context, Callback, ClientStatus.Updated); // Line 4
   respDet.AddClient(asyncResultToAdd); // Line 5

   return asyncResultToAdd; // Line 6
}

在上面的代码中,第一行获取 clientId。这是网格控件的 ID。如果您有两个网格控件,您将有两个不同的 clientId 来表示它们。第二行获取会话 ID,这是一个区分每个客户端的字符串。第三行检索已为此客户端存储的响应详细信息。响应详细信息类存储连接的客户端队列和网格控件的实例。此信息将添加到新创建的 AsynchResult 实例中。AsynchResult 将返回给调用者。如上所述,此方法释放 ASP.NET 线程,从而允许更多客户端并发连接。

当服务器端事件发生需要更改网格中的数据时,会调用 AsynchResult 的 SetCompleted 方法。请注意,服务器端事件由此控件的用户触发。也就是说,如果您正在使用此控件,您将调用 GridInstanceName.DynUpdateClient() 方法来触发此服务器端事件。为了满足您的调用,会在 AsynchResult 上调用 SetCompleted 方法。这将导致调用 EndProcessRequest。同样,我们在此方法中所做的只是将 XML 响应写入客户端(该客户端发出了 AJAX 样式调用)。这完成了客户端的调用。EndProcessRequest 的代码如下所示

public void EndProcessRequest(IAsyncResult result)
{
   GridAsynchResult asynchResult = (GridAsynchResult)result; // Line 1

   asynchResult.ClientContext.Response.ContentType = "text/xml"; // Line 2
   asynchResult.ClientContext.Response.Write(
     asynchResult.RespDetails.GridCtrlAjaxInstance.GetXmlFeed(
                  asynchResult.SessionId)); // Line 3
   
}

第 1 行获取传入的参数。第 2 行将响应类型标记为“xml”。第 3 行获取 XML 响应并写入 Response 对象。第 4 行完成客户端的调用。

关注点

  • 此多客户端 COMET 控件能够更新特定客户端,同时节省资源。此控件避免了来自多个客户端基于计时器的几次不必要的 AJAX 调用。
  • 线程安全:此多客户端 COMET 网格是线程安全的。您可以从多个线程调用以“Dyn”为前缀的方法。也就是说,您的一个线程可能正在更新值,另一个线程可能调用 DynUpdateClient 向特定客户端发送更新。
  • 此控件不仅支持动态更新特定客户端(Web 浏览器)的网格数据,还支持动态更改背景颜色、文本颜色和背景图像。

重要提示!

我期待听到你们的反馈。即使您投 1 票,我也很乐意听到您的详细反馈。所以,请务必留下您的消息。谢谢!

参考文献

  1. http://msdn.microsoft.com/zh-cn/library/system.web.ihttpasynchandler.aspx
  2. http://msdn.microsoft.com/zh-cn/library/system.web.ihttpasynchandler.beginprocessrequest.aspx
  3. http://msdn.microsoft.com/zh-cn/library/system.web.ihttpasynchandler.endprocessrequest.aspx
  4. http://zh.wikipedia.org/wiki/XMLHttpRequest
  5. http://msdn.microsoft.com/zh-cn/library/ms535874(VS.85).aspx

历史

  • 2009 年 4 月 3 日 - 第一个版本。
© . All rights reserved.