zoukankan      html  css  js  c++  java
  • ASP.NET异步请求处理(Asynchronous HTTP Handlers)

    ASP.NET中你可以通过继承IHttpHandler这个接口来实现一个同步(Synchronous)处理用户请求的类。比如你希望对于一切类型为fakephp的请求都通过你的Http Hanlder来处理,你可以实现以下这个类:

    using System;
    using System.Web;

    public class FakePHPHttpHandler : IHttpHandler {
      public void ProcessRequest(HttpContext context) {
        //let's pretend we can handle PHP stuff
      }

      public bool IsReusable {
        get { return true; }
      }
    }


    然后通过在IIS里将你的dll注册为.fakephp的handler就可以了。这些在以下的MSDN文档里都有介绍:

    http://msdn2.microsoft.com/en-us/library/system.web.ihttphandler.aspx

    这里想说的是如何实现一个异步(Asynchronous)d的HTTP Handler。说起来其实也简单,只要实现IHttpAsyncHandler这个接口就好了。

    IHttpAsyncHandler有两个method:

     BeginProcessRequest
    Initiates an asynchronous call to the HTTP handler.

     EndProcessRequest
    Provides an asynchronous process End method when the process ends.

    这显然是和.NET Framework中标准的Asynchronous Programming Model (或者叫“异步模式”, Asynchronous Pattern)是一致的:

    参考: Asynchronous Programming Overview

    异步模式的优势是ASP.NET的worker thread不会等待BeginProcessRequest返回而是会掉头去接收其他的用户请求(当然你也可以要求worker thread等待,不过这样就等于变成了Synchronous Handler)。因为通常处理一个web request的后台时间需要比较长。假设你每秒只能处理10个用户请求,在同步模式下如果有11个人同时访问你的服务,就有一个人会看到500 Internal Server Error之类的错误消息了。但如果是异步模式,worker thread只要调用BeginProcessRequest,而根据Asynchronous Programming Overview,BeginProcessRequest应该立刻返回(“立刻”的含义是它不应该进行长时间的操作,而应该调用QueueUserWorkItem之类的API将耗时的任务放到新线程里执行),这样worker thread就可以腾出手去接收下一个user request了。

    注:ASP.NET的max worker thread上限可以通过processModel configuration element里的maxWorkerThreads属性来改变(参考:Improving ASP.NET Performance 以及 processModel element),但最大的值也只是100 (range from 5 to 100, default 20)。

    由此引出的问题自然是:当异步操作完成时, ASP.NET是如何知道并做相应处理的。这有一下几种选择:

    1) 当调用BeginProcessRequest的时候,ASP.NET可以同时传入一个AsyncCallback的delegate,而在你完成异步操作后,你应该调用这个回调函数来通知ASP.NET。

    2) ASP.NET可以不停地查看IAsyncResult (这个是BeginProcessRequest的返回值)IsCompleted属性来确认异步操作是否已经完成了,当然,当你完成异步操作时,你有义务将IsCompleted设成true。

    3) ASP.NET也可以等待AsyncWaitHandle的信号,AsyncWaitHandle是IAsyncResult的另一个属性,这个和经典的Win32里waiting on kernel object是类似的。

    4) ASP.NET可以直接调用EndProcessRequest。

    注意:3) 和 4) 是Asynchronous Programming Overview里规定的标准的blocking execution的方式,也就是说,如果你的主线程在异步操作完成前无法再做任何工作时,它可以通过3) 或者 4)来等待异步操作的完成。

    从理论上来说,你应该保证你的IHttpAsyncHandler能满足以上所有4种方式。但现实中,你未必一定如此做。那么哪些是我们必须实现以匹配ASP.NET的要求的呢?或者ASP.NET究竟是如何实现异步调用及返回的呢?

    事实上,ASP.NET采用了方法1,也就是说,在调用BeignProcessRequest的时候,ASP.NET传入了一个AsyncCallback,而你应该在完成异步操作后调用这个callback,而在这个AsyncCallback里,ASP.NET又调用了你的EndProcessRequest来做收尾工作。

    根据上面的讨论,我们可以如下设计我们的Asynchronous Http Hanlder:

    public class AsyncFakePHPHttpHandler : IHttpAsyncHandler
    {
            private void ProcessRequestCallback(object state)
            {
                AsyncResult async = (AsyncResult)state;
                // this is where the real work is done
                ProcessRequest(async.context);
                async.isCompleted = true;
                if (null != async.asyncCallback)
                    async.asyncCallback(async);
            }
            public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback asyncCallback, object state)
            {
                AsyncResult async = new AsyncResult(context, asyncCallback, state);
                // if the callback is null, we can return immediately and let EndProcessRequest do all the job
                // if callback is not null, we will use our thread pool to execute the necessary asynchronous operations
                // what happens in ASP.NET is that the callback in NOT null, so QueueUserWorkItem will be used
                if (null != async.asyncCallback)
                    threadPool.QueueUserWorkItem(ProcessRequestCallback, async);
                return async;
            }
            // this design also satisfies method 4), we implement it this way to follow the Asynchronous Pattern as much as we can
            public void EndProcessRequest(IAsyncResult result)
            {
                AsyncResult async = (AsyncResult)result;
                if (null == async.asyncCallback)
                    ProcessRequest(async.context);
            }

    总结一下实现异步Http Handler的要点:

    1) 所有在BeginProcessRequest中的耗时操作(比如IO什么的)都应该采用异步调用(比如BeginRead/EndRead)或者生成新的线程去执行。不错,你可以设计一个blocking BeginProcessRequest,没有人能阻止你这么做。But that's a BAD BAD idea.

    2) 实现BeginProcessRequest/EndProcessRequest的目的是允许ASP.NET来异步调用你的Http Handler

    3) 你应该创建一个实现IAsyncResult接口的类,在BeginProcessRequest中你会生成一个该类的实例并返回给ASP.NET(注意BeginProcessRequest的返回值类型)。而根据Asynchronous Pattern,ASP.NET在调用EndProcessRequest的时候会把这个实例再传回给你,你可以用这个实例来判断你所执行的任务的当前状态。

    4) 我个人感觉比较容易导致困惑的是这里“两段式”的异步调用操作。首先ASP.NET是通过BeginProcessRequest/EndProcessRequest来异步调用我们的Http Handler的。然后我们在BeginProcessRequest又再次用异步模式(用QueueUserWorkItem或者其他的Begin*/End*操作)去完成真正的工作。实际上第二步的异步调用才是真正生成另一个thread来处理工作的地方。ASP.NET调用我们的BeginProcessRequest只是一种形式上的协议通知,因为是我们告诉ASP.NET:Hey,我是一个异步的handler。ASP.NET说:那好吧,既然你这么说的,我就用你异步的接口来调用你。事实上,在HttpRuntime的源代码中,可以看到ASP.NET的操作如下:

                    if (app is IHttpAsyncHandler) {
                        // asynchronous handler
                        IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                        context.AsyncAppHandler = asyncHandler;
                        asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
                    }
                    else {
                        // synchronous handler
                        app.ProcessRequest(context);
                        FinishRequest(context.WorkerRequest, context, null);
                    }

    ====================================================================

    最后的题外话,关于IIS/ASP.NET处理请求的工作流也是一个很有趣的问题,下面的这篇文章很棒(but the author's English writing kinda sucks, : ) )。比如,从中我们可以知道,整个流程中其实牵涉了两个Queue,一个是Kernel mode下的,一个是User mode下的,而后者就是ASP.NET所使用的Application Queue,而ASP.NET的worker thread就是从这个Queue里去取下一个需要处理的请求的。


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/blackvii/archive/2008/01/05/2026903.aspx

  • 相关阅读:
    Selenium自动化测试-unittest单元测试框架
    Python 面向对象
    【新手总结】在.Net项目中使用Redis作为缓存服务
    asp.net性能优化之使用Redis缓存(入门)
    浅谈MVC、MVP、MVVM架构模式的区别和联系
    jquery uploadify在谷歌浏和火狐下无法上传的解决方案(.Net版)
    [翻译]NUnit---Action Attributes(八)
    [翻译]NUnit---String && Collection && File && Directory Assert (七)
    [翻译]NUnit---Exception && Utility Methods (六)
    [翻译]NUnit---Condition Asserts && Comparisons Asserts && Type Asserts (五)
  • 原文地址:https://www.cnblogs.com/twilight/p/1634935.html
Copyright © 2011-2022 走看看