异步IO在ISAPI扩展中的应用

异步IO(Input/Output)在网络编程中是一种提高应用程序性能可扩展性的有效方法。虽然它看起来像是黑魔法,但实际上它相当直接,并且能够为应用程序带来最高水平的性能和可扩展性。

示例项目:异步IO的展示

在浏览代码项目网站时,偶然发现了一个由Jorge Lodos Vigil提供的ISAPI项目示例,该项目展示了如何发送HTML页面和图像。在这个示例中,Jorge的ISAPI通过两种不同的数据类型返回内容:页面存储在内存缓冲区中,而图像存储在磁盘上的文件中。将数据缓冲区发送给客户端和将文件发送给客户端恰好涵盖了使用ISAPI API进行异步数据发送的两种可能方式——WriteClient和TransmitFile。

ImageServer ISAPI扩展本质上是从某个隐藏目录中代理图像请求。ImageServer通过返回一个包含IMG元素的HTML页面来满足这些请求,IMG元素的源就是ImageServer扩展。源URL被特别格式化,以便让ImageServer知道它应该发送回实际的图像而不是包含页面。

在ISAPI模块的上下文中,有两种方式可以异步接收或发送数据。可以使用ReadClient或WriteClient异步读取/写入任意数据缓冲区到客户端,或者可以使用TransmitFile异步将文件发送给客户端。TransmitFile是一个Win32函数,通过使用ISAPI函数ServerSupportFunction并指定HSE_REQ_TRANSMIT_FILE选项来访问。

MSDN在线库提供了关于异步IO的良好概述,但通常需要执行以下步骤:

  • 设置一个回调,当IO完成事件发生时将被通知。
  • 启动异步IO。
  • 从HttpExtensionProc返回HSE_STATUS_PENDING。
  • 当IO完成时,清理任何上下文信息,并通知IIS已经完成了这个会话。

关于异步IO,还有一件事需要注意。那就是发送给客户端的任何数据必须存活直到收到完成通知。这意味着,它几乎总是被放置在堆上。

这个最后的要求通常从设计角度来看是最令人头疼的。ISAPI扩展所需的极端性能和可靠性排除了不断访问堆的设计。限制对堆的访问通常是通过实现缓冲对象池来重用来完成的。

ImageServer的要求没有那么严格。当ImageServer需要一个缓冲区或传输上下文时,它简单地在堆上创建它。同样,当它完成一个缓冲区的使用时,它就销毁它。虽然创建一个受保护的缓冲池并不困难,但这会分散ImageServer的中心目的——展示基本的异步IO。

代码简单且注释丰富,应该很容易理解。以下是将发现的亮点。

有一个主类CImageServer,它封装了这个应用程序所需的所有功能。这个类在Server.h中声明,并在Server.cpp中实现。

CImageServer的一个静态实例位于ImageServer.cpp中。这个文件中还有ISAPI DLL的主要入口点——DllMain、GetExtensionVersion、TerminateExtension和HttpExtensionProc。它们都是基本的样板代码,除了HttpExtensionProc,它将其行为委托给CImageServer::ServiceRequest。

还有两个类作为异步IO调用的传输上下文。第一个是CFilePacket,它是TransmitFile的上下文。第二个是tWriteClientPacket,实际上是一个结构而不是一个类。它作为WriteClient的上下文。CFilePacket和tWriteClientPacket都被声明为CImageServer声明中的嵌套类/结构。实际上没有必要这样做,但这使得代码更干净,因为这些类只在CImageServer的函数中使用。

理解CImageServer的工作原理最好是通过阅读源代码。一切都从CImageServer::ServiceRequest开始,注释将从那里引导。

与任何ISAPI模块一样,需要将DLL放置在Web服务器可以访问的路径中——具有执行权限。还需要一个包含图像的目录,这与在CImageServer::GetImageDirectory中硬编码的相匹配。

所在的公司Financial Insight Systems Inc.专注于使用Microsoft Back Office产品创建高性能的互联网应用程序。有MCSD认证,拥有Visual C++和SQL Server的认证,在过去五年中,大部分时间都在开发ISAPI模块和基于Winsock的客户端/服务器解决方案。欢迎提出建议和评论。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485