Asp.net 构建可扩展的的Comet Web 应用(一)

简介:

说明

这篇文章用来提供在asp.net中使用comet的一种理论上的解决方案。它包含了Comet技术在服务端的实现以及怎样去解决可扩展的问题。我将在不久以后发表一般文章,使用我接下来要讲到的Comet 线程池技术演示一个小游戏,来提供客户端的代码。它可能会给你在真实的环境下解决问题带来一些思路。

简介

在过去的六个月里,我一直都在投入精力开发一个在线的象棋应用程序。它能够让玩家注册、登陆,并且像在真实世界中对弈一样。其中,我不得不克服的一个障碍就是,怎样在服务端和客户端实现一个类似在真实世界中的通信。要克服这个障碍,以下一系列的因素需要考虑:

(1) 可扩展性 – 我想让它在一个负载均衡的环境中工作,并且不需要占用巨大的服务器资源。

(2) 兼容性 – 我希望它能够在许多不同特性的浏览器中工作,希望不需要任何的浏览器插件。

(3) 性能 – 我希望在一位玩家到任何可通信的对手之间提供尽可能快的响应。这可以让我更有效得控制时间和提供一个更友好的用户体验。

(4) 简单 – 我想实现通信层儿不想涉及第三方服务应用程序。总得来说,它应该仅仅工作在宿主环境中,例如www.discountasp.net

我评估了以上所有我列出的选项。我构建的第一个原型是使用标准的ajax作为解决方案。它从服务端“拉取”数据。这造成了太长得延时并且太多次数的通信。因此,我很快地从可行性方案中把它移除掉了。我又调研了其他的通信方案。例如使用一个隐藏的Flash小程序进行socket通信。这需要一个浏览器插件,所以也不是我想要的答案。然后我发现了Comet,并且觉得这就是我想要的方案。于是,我做了一些调研,并且构建了一个原型。

Comet的技术原理

Comet在客户端(web浏览器,使用XmlHttpRequest)和服务端使用一个持续的连接。这个持续连接在一段预定义的时间内(例如5秒钟)对服务端保持着打开状态,并且将会客户端两种响应:要么超时信息,要么是服务端应用程序的某些部分的逻辑想要发送的信息。一旦客户端接收到信息,它将可以被实现在客户端的任何应用程序的逻辑处理。这个持续的连接然后会被重新打开,并且重复以上过程。

这种方案解决了性能的问题。它意味着无论什么时候一条消息只要需要被发送,它就可以被发送会客户端。并且如果这个持续的连接被打开着,客户端接收它只需要很短的延时,几乎是瞬间的。

另一个连接被用来发送信息给服务器。这个连接并不是“持续”的。并且通常情况下处理完之后立即返回。从这个象棋游戏的一端看来,这个持续的连接一直都在等待对手棋子的移动。而那个非持续的连接则只是发送我的移动。


一个 Comet请求返回超时的顺序图


一个Comet返回消息的顺序图


真实环境中使用Comet

到现在为止,一切从纸面上看起来都很美好。我们有一种方案能够提供发送信息给一个浏览器的功能,在真实的环境并且不需要插件。但是在实践的过程当中,会遇到更多的麻烦。许多文章都在描述这样一个事实——采用一个持续的连接多么得”Hack”。我不太倾向于同意这样的观点。

Comet在某些浏览器中运行,确实会遇到一些问题(主要是因为HTTP协议规定,每个浏览器只同时支持两个连接,并且限制在同一主机上)。Http协议的这个限制被用来为通常的在低带宽连接下的浏览提供更好地性能,这在运行Comet程序的时候导致了性能问题(有一些解决方法)。这个问题,仅仅只需要被IE关注(我猜想IE直到8.0的版本,才严格执行了标准)。FireFox 2运行更多的连接,并且将他们管理得更好。另外,FireFox 3甚至允许更多的连接,这意味着Comet风格的应用程序的前景是光明的。

第二个问题来着这种技术的可扩展性。这也是这篇文章尝试补救的问题。这个问题源于现阶段各个平台缺乏对Comet这种风格协议的很好的支持,这导致了使用了持久连接的应用程序在未来可能不会具有很好的扩展性。我想说,这不是Comet技术思想本身的失败,而是对Comet服务器特殊实现的失败。

很多其他的开发者已经将那些坐在我们平台之前的服务器放到了一起。这就允许我们将Comet的请求方式从web 服务器中分离开来。并且通过管理他们自己的持续连接可以实现扩展。我在这篇文章中阐明的就是你在哪些情况下不应该在asp.net中使用Comet,并且给出了可能的解决方案。

开始测试Comet

在asp.net中使用持续连接最主要的缺陷是,在asp.net中每一个连接都占用asp.net一个工作线程(那些连接可能持续着五秒,并且一直打开着)。因此,每一个客户端连接都将在asp.net线程池中占用一个线程。最终,无法卸载,服务器将停止响应。

为了展示这种情况,我举一个非常简单的例子。它使用很简单的持续连接向asp.net发起请求。用一个handler占用请求,并在返回客户端之前使其保持打开状态5秒钟。


这个handler非常的简单,它持有请求的执行5秒钟。然后返回。这个简单的Comet请求将最终因为请求超时返回到客户端。

我还写了一个控制台程序。使用WebRequest来调用CometSyncHandler。结果和预期的一样,每一个客户端占用了一个asp.net工作线程,最终在40或者更多的连接下,网站开始招架不住,页面渐渐响应非常缓慢。

下面的截屏可以看到发生了什么:


可以清楚地看到,它不适合任何真实的应用程序。所以我做了一些“挖掘”,并且设计了一个解决方案。

IhttpAsyncHandler

这是方案的第一部分,这个小“魔法”在当我们向一个handler发出一个请求时。

,允许我们在服务器上异步地运行代码。如果你对IasyncHttpHandler不是很熟悉,那就阅读我下面的解释,来了解它如何工作:

IhttpAsyncHandler 开放两个主要的需要被实现的方法。它们是:BeginProcessRequest和EndProcessRequest。通常的做法是:我们在请求开始的时候的处理逻辑放在BeginProcessRequest中,然后我们执行一系列的异步方法,例如数据库查询或者.net的异步方法。当这些异步方法执行完成之后,然后响应客户端的处理放在EndProcessRequest。

下面的顺序图,展示了它如何工作:



CometThreadPool

上面的顺序图介绍了一个自定义的线程池用来处理Comet请求。需要它的原因是因为我们不想asp.net为每一个这样的请求开启一个它自己的线程,知道它需要等待一个Comet请求的超时。

这段线程池技术的实现技术位于网站的CometAsync文件夹中。它包含了如下的文件:

CometAsyncHandler – 这是一个IhttpAsyncHandler接口的实现。

CometAsyncResult – 这是IasyncResult接口的自定义实现。它包含了一个Comet异步操作的状态。

CometThreadPool – 这是一个静态类用来管理Comet线程池。

CometWaitRequest – 这是一个代表从客户端请求的对象。它们被排列自定义线程池中等待被处理。

CometWaitThread –这是一个线程用来处理来自队列中的CometWaitRequest对象。

这个实现在第一次创建一系列的后台CometWaitThread对象。每一个这些对象都包含一个单独的线程,用来处理CometWaitRequest。在我们的web应用程序中,我们将在Application_Start实例化一个线程池。


这个被创建的五个线程一直被闲置在后台,直到等待CometWaitRequest实例的到来,以为这些实例提供服务。

然后CometAsyncHandler等待来自客户端的请求。它的职责是将这些请求排列在线程池中。


为了我们能完全的跟踪哪些线程是存在的,BeginProcessRequest输出了一些debug信息。然后创建了CometAsyncResult类的一个实例来跟踪HttpContext,并且向asp.net返回并说明它已经开始了一个异步处理。在返回之前,它调用了BeginWaitRequest,用来把请求加入线程池。


这段代码创建了一个CometWaitRequest类的新的实例并且把它排列到线程池中。


这段代码逻辑中,挑选了一个CometWaitThread并基于循环方法来分配CometWaitRequest(例如,如果线程1接收之前的一个请求,线程2将接收第二个)。

CometWaitThread类


请求被加入到一个被选中的线程的用来存放CometWaitRequest对象的列表中。

这一时刻,CometAsyncHandler已经将asp,net线程返回到线程池中。并且正在等待CometWaitThread完成异步处理,然后它就可以完成客户端的请求。CometWaitThread的代码看起来像下面这样:



QueueCometWaitRequest_WaitCallback是这个线程的入口点。它在Application_Start方法返回的时候开始执行。它执行了一个循环,并等待一个CometWaitRequest对象加入到它的队列中。一旦一个客户端请求CometAsyncHandlerhandler,它就会出现在队列中。

它顺序处理队列中的每一个对象,在每一次循环中。例如,如果这里有三个请求。它将检查请求1,2,3 然后继续循环并且处理继续处理请求1,2,3 。这能够确保每一个请求都被尽可能快地处理而不是等到它的5秒超时完成。

循环检查是否CometWaitRequest已经是否排列在队列中的时间已经超过了它预定的5秒超时时间。否则,它会检查是否有一个事件正在等待被返回给客户端。如果,这两种情况都不是,它就完成了CometWaitRequest的处理,返回所需的响应对象。然后从队列中将其移除。


QueueCometWaitRequest_Finished方法完成异步操作,通过调用CometAsyncResult 对象的SetCompleted方法。然后调用CometAsyncResult上的回调方法。它指向CometAsyncHandler对象的EndProcessRequest方法。接下来的代码会被执行。


该方法以序列化任意的我们设置到request's HttpContext输出流的对象来响应客户端。

有一个需要被提及的事情是,无论那些线程最终对请求做什么处理,当它到达BeginProcessRequest方法时,它是一个正在被执行的asp.net工作线程。并且当CometWaitThread完成时,要么是一个超时信息要么是一个返回信息,EndProcessRequest方法是被CometThreadPool线程池中的一个线程执行的。这意味着,asp.net只是使用了它线程池中的一个线程来初始化Comet请求。其余的5秒不是被asp.net线程处理。

我们在执行时,屏幕的截图中可以看到:


从这点上来看,值得一提的是来自网站的输出是非常棒的。考虑到这里有200个持续连接,可以看到任务管理器重的CPU/内存数据也是非常正常(并且还同时在这台机子上运行着客户端)。

为了检查一切都工作地非常正常,我为每一个请求/响应对做了一个计数器,来确保每一个请求都有一个响应。下面的截图显示了运行着200个客户端五分钟的测试输出。



它显示了所有的请求都完成地非常得成功

结论

通过实现一个客户端的线程池,我们能构建一个Comet的解决方法在我们的asp.net服务端代码而不是实现一个自定义的服务器,或者甚至实现任何复杂的消息程序。只是一个简单的线程池来管理对个请求。例如我们用五个线程来管理了所有的200个Comet请求。


原文链接:http://www.codeproject.com/KB/aspnet/CometAsync.aspx




原文发布时间为:2011-08-31


本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

目录
相关文章
|
2天前
|
数据库 Python
Python实践:从零开始构建你的第一个Web应用
使用Python和轻量级Web框架Flask,你可以轻松创建Web应用。先确保安装了Python,然后通过`pip install Flask`安装Flask。在`app.py`中编写基本的"Hello, World!"应用,定义路由`@app.route('/')`并运行`python app.py`启动服务器。扩展应用,可添加新路由显示当前时间,展示Flask处理动态内容的能力。开始你的Web开发之旅吧!【6月更文挑战第13天】
17 2
|
6天前
|
SQL 安全 数据库
如何构建一个安全的Web应用:技术与策略的全面指南
【6月更文挑战第12天】构建安全Web应用的全面指南:了解SQL注入、XSS等威胁,采用输入验证、安全编程语言,配置安全服务器和数据库,使用HTTPS,实施会话管理、访问控制,正确处理错误和日志,定期进行安全审计和漏洞扫描。确保用户数据和应用安全。
|
7天前
|
中间件 API Go
使用Echo和Gin构建高性能Web服务的技术文档
本文档对比了Go语言中的两个流行Web框架——Echo和Gin。Echo是一个高性能、可扩展的框架,适合构建微服务和API,强调简洁API和并发性能。Gin基于net/http包,具有Martini风格API,以其快速路由和丰富社区支持闻名。在性能方面,Gin的路由性能出色,两者并发性能均强,内存占用低。文中还提供了使用Echo和Gin构建Web服务的代码示例,帮助开发者了解如何运用这两个框架。选择框架应考虑项目需求和个人喜好。
21 2
|
3天前
|
开发框架 前端开发 .NET
LIMS(实验室)信息管理系统源码、有哪些应用领域?采用C# ASP.NET dotnet 3.5 开发的一套实验室信息系统源码
集成于VS 2019,EXT.NET前端和ASP.NET后端,搭配MSSQL 2018数据库。系统覆盖样品管理、数据分析、报表和项目管理等实验室全流程。应用广泛,包括生产质检(如石化、制药)、环保监测、试验研究等领域。随着技术发展,现代LIMS还融合了临床、电子实验室笔记本和SaaS等功能,以满足复杂多样的实验室管理需求。
12 3
LIMS(实验室)信息管理系统源码、有哪些应用领域?采用C# ASP.NET dotnet 3.5 开发的一套实验室信息系统源码
|
7天前
|
开发框架 Dart JavaScript
深入探讨Flutter中的Web支持功能,以及如何利用Flutter构建跨平台Web应用的最佳实践
【6月更文挑战第11天】Flutter,Google的开源跨平台框架,已延伸至Web支持,让开发者能用同一代码库构建移动和Web应用。Flutter Web基于Dart转JavaScript,利用WebAssembly和JavaScript在Web上运行。构建Web应用最佳实践包括选择合适项目、优化性能、进行兼容性测试和利用Flutter的声明式UI、热重载等优势。尽管性能挑战存在,Flutter Web为跨平台开发提供了更多机会和潜力。
34 1
|
7天前
|
SQL 缓存 测试技术
RESTful API设计的最佳实践:构建高效、可维护的Web服务接口
【6月更文挑战第11天】构建高效、可维护的RESTful API涉及多个最佳实践:遵循客户端-服务器架构、无状态性等REST原则;设计时考虑URL结构(动词+宾语,使用标准HTTP方法)、使用HTTP状态码、统一响应格式及错误处理;确保数据安全(HTTPS、认证授权、输入验证);实施版本控制;并提供详细文档和测试用例。这些实践能提升Web服务接口的性能和质量。
|
8天前
|
缓存 前端开发 数据库
构建高性能Web应用的关键技术
本文将介绍构建高性能Web应用所需的关键技术。我们将探讨前端、后端、数据库等多个方面的技术,并提供实用的建议,帮助开发者优化应用性能并提升用户体验。
|
10天前
|
存储 人工智能 安全
Web3 在 AI 民主化中的作用:构建去中心化的 AI 模型
Web3 带动人工智能进入新阶段,通过去中心化模型实现 AI 民主化,提高访问性和安全性。借助区块链、智能合约和去中心化数据存储,AI 开发变得更开放、透明。平台如 Soroosh SSE 降低准入门槛,促进协作,同时增强隐私保护。代币经济激励参与者,但面临数据质量、计算基础设施和治理等挑战。Web3 对 AI 的革新将塑造更公平、开放的未来。
|
3天前
|
前端开发 JavaScript Java
计算机Java项目|基于Web的足球青训俱乐部管理后台系统的设计与开发
计算机Java项目|基于Web的足球青训俱乐部管理后台系统的设计与开发
|
5天前
|
传感器 小程序 搜索推荐
(源码)java开发的一套(智慧校园系统源码、电子班牌、原生小程序开发)多端展示:web端、saas端、家长端、教师端
通过电子班牌设备和智慧校园数据平台的统一管理,在电子班牌上,班牌展示、学生上课刷卡考勤、考勤状况汇总展示,课表展示,考场管理,请假管理,成绩查询,考试优秀标兵展示、校园通知展示,班级文化各片展示等多种化展示。
29 0
(源码)java开发的一套(智慧校园系统源码、电子班牌、原生小程序开发)多端展示:web端、saas端、家长端、教师端