异步 HTTP 处理程序, 用于异步 BLOB 数据读取器






4.78/5 (17投票s)
包含异步 HTTP 处理程序(与替代的 WebHandler 相比)实现的描述,该处理程序使用异步 DB 读取操作将 BLOB DB 列作为图像呈现给任何客户端浏览器。
引言
自 ASP 早期以来,已通过多种方式实现了从数据库列存储和检索 BLOB 图像。可伸缩性(更多用户使用相同资源)和性能(更快的执行时间)是当今高要求网站中的重要因素。有无数种编写代码来完成预定义任务的方法(而且它们可能都有效),但要做到正确需要注意细节。随着 ASP.NET 中 HTTP 处理程序的引入,这可以比使用标准的 ASP.NET(.aspx)页面做得更好;结合 ADO.NET v2 中的异步 DB 操作和异步 HTTP 处理程序,这会做得更好!
背景
- 为什么要使用 HTTP 处理程序?
HTTP 处理程序与普通的 ASP.NET 页面之间的主要区别在于性能,因为 HTTP 处理程序不经过标准 ASP.NET 页面的完整页面事件、浏览器兼容性测试和格式设置。因此,如果您的页面没有输出(例如审计日志记录)或没有 HTML 输出(例如 XML、图像或其他任何内容),那么将其实现为 HTTP 处理程序将获得更高的可伸缩性。
- 为什么要使用异步 HTTP 处理程序?
在 ASP.NET v2 中实现异步页面要容易得多,并且可以提高需要大量 I/O 的页面的可伸缩性(在相同硬件上支持更多用户)(在等待响应时线程会延迟)。当 ADO.NET 执行数据库操作时,执行该操作的线程将在等待数据库的回复时“挂起”;如果后者例如已关闭且超时设置为 5 分钟(这令人难以置信但确实如此),那么该线程将浪费 5 分钟。如果您的线程限制设置为 25,那么该数量的用户将已经导致线程耗尽。通过异步操作,线程在等待响应时会返回到线程池,从而允许响应 Web 上的其他页面(这些页面可能命中数据库,也可能不命中)。无论如何,这主要是一个可伸缩性考虑因素,并且随着时间的推移,性能也会得到改善(更好的资源利用率)。
使用代码
解决方案源代码包含以下多个演示
- syncFlavors/ 文件夹(每个都是一个完整的独立示例)包含“旧的”同步方法
- AddImage.aspx - 最初用于将图像添加到包含的 SQLExpress 数据库
- ShowImageScalar.aspx - 使用 ADO.NET
ExecuteScalar
方法将图像呈现给浏览器的 ASP.NET 页面 - ShowImageReader.aspx - 使用 ADO.NET
ExecuteReader
方法将图像呈现给浏览器的 ASP.NET 页面 - ShowImageAsyncReader.aspx - 使用新的 ADO.NET v2
BeginExecuteReader
方法将图像呈现给浏览器的 ASP.NET 页面
- 异步 HTTP 处理程序
- App_Code/ImageAsyncHandler.cs - HTTP 处理程序的类
- web.config - 需要更改以注册我们的 HTTP 处理程序
- ShowImage.aspx - 包含一个图像标签以使用我们的 HTTP 处理程序检索 BLOB
- 异步 WebHandler(替代实现)
- ImageAsync.ashx - 我们的 WebHandler(与 HTTP 处理程序相同,但无需更改 web.config!)
- ShowImage.aspx - 包含一个图像标签以使用我们的 WebHandler 检索 BLOB
实现之间的唯一区别是 HTTP 处理程序需要在 web.config 文件中进行注册,这使我们在 URL 方面具有更大的灵活性,但例如在部署到 ISP 时会限制我们(因为它们不总是允许更改 web.config 或 IIS 映射)。这就是创建 WebHandlers 来解决这些更改的原因 - 它实际上是一样的,无需注册任何处理程序或 IIS 映射(为了映射到我们新的文件类型,因为安装 ASP.NET 时默认会映射 .ASHX 文件)。就个人而言,我认为 WebHandler 实现“更简洁”且更易于维护,但代价是无法使用有趣的 URL。
关注点
- web.config
ConnectionStrings
属性这个新的 web.config 部分允许我们通过名称集中存储连接字符串。
<connectionStrings> <add name="LocalBLOB" connectionString="Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|BLOBTest.mdf; Integrated Security=True;User Instance=True; Asynchronous Processing=true"/> </connectionStrings>
请注意 "|DataDirectory|" 特殊关键字,它将在运行时由 ASP.NET 替换为我们的 App_Data 文件夹,以避免硬编码路径。在代码中,我们可以通过以下方式检索它:
ConfigurationManager.ConnectionStrings["LocalBLOB"].ConnectionString
- 异步 HTTP 处理程序与异步 DB 读取
异步 HTTP 处理程序使用
IAsyncResult
对象在操作完成时发出通知,从而使我们的线程无需等待响应。结合异步 DB 操作(它也返回一个IAsyncResult
对象!),一旦 DB 操作完成,ASP.NET 将继续执行处理程序(并重新分配线程)。// retrieve result using (SqlDataReader reader = cmd.EndExecuteReader(result)) { this.renderImage(context, reader); }
- 异步 HTTP 处理程序与 WebHandler(.ashx)
HTTP 处理程序需要更改 web.config 文件才能使其对 ASP.NET 可见。“
path
”属性允许我们指定几乎任何我们可以想到的 URL,而“type
”属性指向我们的类。<httpHandlers> <add verb="*" path="img/*" type="ImageAsyncHandler"/> </httpHandlers>
我们的 HTTP 处理程序使用 URL 段来确定 URL 中的“*”;要检索由我们的 HTTP 处理程序提供的图像,我们可以指定一个如下所示的图像标签
<img src="img/960a3be7-80d1-4528-bfaa-975cf9d53800" />
WebHandlers 通过使 .ASHX 扩展名对 ASP.NET 可知(预先配置的 IIS 映射)来克服此要求,因此无需更改 web.config。当然,这意味着我们也必须使用不同的 URL,因此要从我们的 WebHandler(现在使用查询字符串)检索图像,需要以下图像标签
<img src="ImageAsync.ashx?960a3be7-80d1-4528-bfaa-975cf9d53800" />
- ADO.NET v2 异步
DataReader
新的
BeginExecuteReader
方法需要稍微不同的代码设计,因为现在查询的执行和实际使用结果分为两个不同的方法。这还可以更好地重用线程,因为我们的主线程不必等待 DB 操作完成。 - 缓冲 BLOB 字段
使用
DataReader
时,数据库中的每一行在到达我们的代码之前都会完全加载到内存中;显然,如果我们的 BLOB 列包含例如 1GB 数据,这将耗尽我们的内存资源。如果我们改用不同的CommandBehavior
,则可以更好地将其从数据库缓冲,从而节省内存。// start async DB read return cmd.BeginExecuteReader(cb, context, // doesn't load whole column into memory CommandBehavior.SequentialAccess | // performance improve since we only want one row CommandBehavior.SingleRow | // close connection immediately after read CommandBehavior.CloseConnection);
- 将错误消息呈现为图像
返回图像的 HTTP 处理程序使得显示任何异常详细信息变得困难,从而阻碍了实时站点的调试和管理。在我的情况下,我更喜欢通过在运行时呈现包含错误文本的图像来向用户显示真实的异常。
context.Response.ContentType = "image/jpeg"; // calculate the image width by message length Bitmap bitmap = new Bitmap(7 * msg.Length, 30); Graphics g = Graphics.FromImage(bitmap); // create a background filler g.FillRectangle(new SolidBrush(Color.DarkRed), 0, 0, bitmap.Width, bitmap.Height); // draw our message g.DrawString(msg, new Font("Tahoma", 10, FontStyle.Bold), new SolidBrush(Color.White), new PointF(5, 5)); // stream it to the output bitmap.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
历史
- 首次发布 - 欢迎随意使用并告诉我您的反馈!