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 Web应用:优化技巧与最佳实践
本文探讨了如何通过优化技巧和最佳实践来构建高性能的Python Web应用。从代码优化到服务器配置,我们将深入研究提高Python Web应用性能的各个方面。通过本文,读者将了解到一系列提高Python Web应用性能的方法,从而更好地应对高并发和大流量的挑战。
|
3天前
|
XML 开发框架 .NET
C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作
C#/ASP.NET应用程序配置文件app.config/web.config的增、删、改操作
|
7天前
|
开发框架 JSON .NET
.Net4.0 Web.config 配置实践
.Net4.0 Web.config 配置实践
|
7天前
|
数据采集 存储 XML
如何利用Python构建高效的Web爬虫
本文将介绍如何使用Python语言以及相关的库和工具,构建一个高效的Web爬虫。通过深入讨论爬虫的基本原理、常用的爬虫框架以及优化技巧,读者将能够了解如何编写可靠、高效的爬虫程序,实现数据的快速获取和处理。
|
11天前
|
前端开发 JavaScript
【Web 前端】什么是扩展运算符,用于什么场景?
【5月更文挑战第1天】【Web 前端】什么是扩展运算符,用于什么场景?
【Web 前端】什么是扩展运算符,用于什么场景?
|
12天前
|
运维 前端开发 JavaScript
【专栏:HTML进阶篇】HTML与Web标准:构建可访问与可维护的网页
【4月更文挑战第30天】本文探讨了HTML与Web标准的关系,强调遵循标准对创建高质量、可访问、可维护网页的重要性。通过使用语义化标签、提供文本替代、合理使用表格和列表,可提升网页可访问性;通过结构化文档、添加注释、分离结构与表现,能增强网页可维护性。遵循Web标准,可确保网页在不同设备上的兼容性,并满足各类用户需求。
|
13天前
|
开发框架 Dart 前端开发
【Flutter前端技术开发专栏】Flutter中的Web支持:构建跨平台Web应用
【4月更文挑战第30天】Flutter,Google的开源跨平台框架,已延伸至Web领域,让开发者能用同一代码库构建移动和Web应用。Flutter Web通过将Dart代码编译成JavaScript和WASM运行在Web上。尽管性能可能不及原生Web应用,但适合交互性强、UI复杂的应用。开发者应关注性能优化、兼容性测试,并利用Flutter的声明式UI、热重载等优势。随着其发展,Flutter Web为跨平台开发带来更多潜力。
【Flutter前端技术开发专栏】Flutter中的Web支持:构建跨平台Web应用
|
13天前
|
缓存 监控 测试技术
【Go语言专栏】使用Go语言构建高性能Web服务
【4月更文挑战第30天】本文探讨了使用Go语言构建高性能Web服务的策略,包括Go语言在并发处理和内存管理上的优势、基本原则(如保持简单、缓存和并发控制)、标准库与第三方框架的选择、编写高效的HTTP处理器、数据库优化以及性能测试和监控。通过遵循最佳实践,开发者可以充分利用Go语言的特性,构建出高性能的Web服务。
|
14天前
|
网络协议 数据库 开发者
构建高效Python Web应用:异步编程与Tornado框架
【4月更文挑战第29天】在Web开发领域,响应时间和并发处理能力是衡量应用性能的关键指标。Python作为一种广泛使用的编程语言,其异步编程特性为创建高性能Web服务提供了可能。本文将深入探讨Python中的异步编程概念,并介绍Tornado框架如何利用这一机制来提升Web应用的性能。通过实例分析,我们将了解如何在实际应用中实现高效的请求处理和I/O操作,以及如何优化数据库查询,以支持更高的并发用户数和更快的响应时间。
|
14天前
|
机器学习/深度学习 自然语言处理 安全
【专栏】.NET 开发:构建智能应用的关键
【4月更文挑战第29天】本文探讨了.NET开发在构建智能应用中的关键作用,强调了其强大的框架、工具集、高效性能和跨平台支持。通过实例展示了.NET在人工智能、物联网及企业级应用中的应用。同时,指出了.NET开发面临的挑战,如技术更新的学习成本、性能优化、资源管理和安全隐私保护,并提出了应对策略。随着技术进步,.NET将在智能应用领域发挥更大作用,推动创新与便利。