AhDung

/*追求代码质量的非职业码农*/

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
  95 Posts :: 0 Stories :: 401 Comments :: 0 Trackbacks

在asp.net项目中,添加一个【一般处理程序】来处理请求是很自然的事,这样会得到一个实现自IHttpHandler的类,然后只需在ProcessRequest方法中写上处理逻辑就行了。但是这样的一个请求处理程序(下称ashx)是同步的,就是接待该次请求的线程会一直等待处理完才能解脱,后果就是,如果这个ashx比较耗时,并且同时对它的请求又多的话,服务器需要开启若干个线程来跑这个ashx,并且这些线程都要各自跑很久才能被收回或挪作它用,如果这样的ashx还有不少的话,那么对整个服务器资源的开销是很大的,所以有必要采用IHttpAsyncHandler来实现这种ashx,即异步请求处理程序,异步化以后,线程把请求接进来就完事了,反手就可以去处理其它请求,然后由别的线程硬件来处理具体的任务~取决于任务是CPU消耗型(密集运算,如图片处理)还是I/O型(数据库读写、网络访问等),老实说如果耗时任务总是CPU消耗型,那同步异步在资源消耗上没什么区别,因为总得有个线程来跑任务,换不换线程意义不大。但总的来说异步化没坏处,而且万一对任务类型评估错误呢。

改用IHttpAsyncHandler后,多了两个方法BeginProcessRequest和EndProcessRequest,原有的ProcessRequest事实上已经废弃,请求不会进入里面,而是改为在BeginProcessRequest中处理请求,原IsReusable属性功能不变。说回BeginProcessRequest,这是一个典型的传统异步方法(相对于.net 4.5后的async/await新式异步方法来说),逻辑相比原来的同步方法ProcessRequest有点绕,首先入参除了熟悉的HttpContext外还有两个,然后还有个IAsyncResult类型的返回值。熟悉APM(异步编程模型)套路的朋友知道该怎么搞,不熟悉的可参看MSDN,要点就是实例化一个实现IAsyncResult的类,在其中异步或起线程执行逻辑,然后返回这个对象。现成的实现IAsyncResult的类在.net 4.0后有Task,但如果项目不到4.0,你还找不到一个可以拿来就用的类,如果要为每个ashx实现一个IAsyncResult,想想都蛋疼,哪怕总共只需实现一个IAsyncResult我都不情愿,好在委托这个东西编译器会为它自动生成异步模型,于是有了下面这个简单的封装:

/// <summary>
/// 异步请求处理基类
/// <para>- 子类实现ProcessRequest方法并在其中处理请求</para>
/// <para>- 默认允许实例重用(IsReusable=true),子类可重写为false</para>
/// </summary>
public abstract class HttpAsyncHandler : IHttpAsyncHandler
{
    readonly Action<HttpContext> _processRequestDel;

    protected HttpAsyncHandler() => _processRequestDel = ProcessRequest;

    /// <summary>
    /// 处理请求
    /// </summary>
    //总是要有个让子类处理业务逻辑的地方,既然原来的ProcessRequest已废弃,不如废物利用
    public abstract void ProcessRequest(HttpContext context);

    /// <summary>
    /// 多次请求是否可以重用实例。默认true
    /// </summary>
    public virtual bool IsReusable => true;

    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) =>
        _processRequestDel.BeginInvoke(context, cb, extraData); //利用ProcessRequest委托的异步能力

    //虽然不End也不会导致异步还没跑完就返回响应(HttpApplication的实现似乎保证这一点),但异步中抛出的异常会被忽略,所以需要End暴露问题
    public void EndProcessRequest(IAsyncResult result) => _processRequestDel.EndInvoke(result);
}

有了这个封装好的基类,在写新的ashx时就可以把IHttpHandler改为HttpAsyncHandler,完了把ProcessRequest方法标成override就行,老ashx也可以经过简单修改异步化。举例:

public class FooHandler : HttpAsyncHandler // 替掉IHttpHandler
{
    //加上override
    public override void ProcessRequest(HttpContext context)
    {
        //在这里写逻辑
        context.Response.Write("OK");
    }
}

需要注意的是IsReusable在HttpAsyncHandler中已改为true,所以如果你的ashx明确需要false,请override该属性,如:

public override bool IsReusable => false;

对于.net 4.5及以上版本,微软已经写好了个HttpTaskAsyncHandler,性质一样,只不过形式上符合新式的async/await用法,总之目的都是让开发者可以优雅的使用异步ashx,不必繁琐的从IHttpAsyncHandler开始。

EOF

posted on 2018-05-18 16:18 ahdung 阅读(...) 评论(...) 编辑 收藏