zoukankan      html  css  js  c++  java
  • HttpHandler开发的两个重点问题

    大家在用HttpHandler的时候,一般都会有两个大的疑问(当然,前提是你有钻研精神的话,呵呵)

    1. IsReusable到底什么意思?

    老实说,这个属性很多人都感兴趣,但搞懂的人确实不多。MSDN中的介绍也是不知所以然。

    获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。该属性默认为false

    我来这么说吧,首先我们为什么使用自定义的Handler呢?简单的说,我们是希望能接管掉某些请求,对吧?最常见的应用如下

    • 对图片进行处理。例如所有图片都输出一个水印。或者防止盗链到设计
    • 添加一些特殊的扩展名。例如,我的网站能不能有一个后缀名为chenxizhang的网页呢?(这当然是一个比喻,事实上一般没有必要这么做)

    知道上述的需求之后,我们再来看一下后台的设计。HttpHandler其实就是实现了IHttpHandler接口的一个类型,它要工作,就必须通过ASP.NET所提供的一些所谓的Factory去创建实例,然后调用它的ProcessRequest方法。其实就这么简单

    因为创建对象实例在服务器肯定是需要占用资源的,那么我们就势必要考虑这些请求能不能在一定程序上去复用。这就是IsReusable的初衷

    事实上,我们对这种复用并不会陌生。平常我们就知道对象池和连接池的技术。Handler的Reuse也是一个池的概念。

    好了,说了这么一堆的概念,我们来讲一讲该属性设置为true和设置为false的区别。

    • 设置为true,则通常情况下,就创建一次实例
    • 设置为false,则每次请求都需要创建实例

    我们来看一个例子。为了做测试,我写了如下代码

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.IO;
    using System.Threading;

    namespace WebApplication1
    {
        public class TestHandler:IHttpHandler
        {

            #region IHttpHandler 成员

            public bool IsReusable
            {
                get { return fasle; }
            }

            public void ProcessRequest(HttpContext context)
            {
                WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
                context.Response.Write("你请求的地址是:<b>" + context.Request.Url.ToString() + "</b>");
                WriteLogMessage("结束调用");
            }

            #endregion

            public TestHandler() {
                WriteLogMessage("对象实例被创建");
            }

            void WriteLogMessage(string msg)
            {
                string logFile = HttpContext.Current.Server.MapPath("Log.txt");
                File.AppendAllText(logFile, DateTime.Now.ToString()+ msg + Environment.NewLine);
            }
        }
    }

    代码很简单,我通过一个文本文件的方式记录对象什么创建的,什么时候发起请求和结束请求的。现在我先设置IsReusable为false

    在调试之前,我们还需要设置一下web.config,添加一个HttpHandler的注册

    <httpHandlers>
        <add path="*.chenxizhang" type="WebApplication1.TestHandler" verb="*"/>
    </httpHandlers>

    这里作为演示目的,我就牺牲一下自己,用这个Handler来监听所有后缀名为chenxizhang的请求

    打开浏览器,输入类似下面这样的网址http://localhost:6994/test.chenxizhang

    快速地刷新几次,然后关闭浏览器。找到网站根目录下面的一个Log.txt文件,会看到下面的文本

    2009/6/15 11:28:30对象实例被创建
    2009/6/15 11:28:30当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:30结束调用
    2009/6/15 11:28:31对象实例被创建
    2009/6/15 11:28:31当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:31结束调用
    2009/6/15 11:28:31对象实例被创建
    2009/6/15 11:28:31当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:31结束调用
    2009/6/15 11:28:32对象实例被创建
    2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:32结束调用
    2009/6/15 11:28:32对象实例被创建
    2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:32结束调用
    2009/6/15 11:28:32对象实例被创建
    2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:28:32结束调用
    2009/6/15 11:28:32对象实例被创建
    2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang

    我们可以看到,每次都请求都需要创建实例

    然后,我们去修改一下IsReusable属性为true,再运行,就可以看到下面这样的输出结果了

    2009/6/15 11:23:34对象实例被创建
    2009/6/15 11:23:34当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:23:34结束调用
    2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:40结束调用
    2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:40结束调用
    2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:40结束调用
    2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:40结束调用
    2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:40结束调用
    2009/6/15 11:24:41当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:41结束调用
    2009/6/15 11:24:41当前被调用的请求是:/test.chenxizhang
    2009/6/15 11:24:41结束调用

    也就是说,对象只被创建了一次。后面调用就重复利用了。这样看起来是不是还不错呢?

    通常意义上说,IsReusable设置为true可以提高性能,尤其是说这种Handler初始化的时候需要做很多事情的情况下。但为什么默认又设置为false呢?

    因为如果设置为true,也就是所有有用户,所有请求都共享一个对象实例,那么可能造成什么问题呢?

    • 最典型的问题就是你要注意这个类型中成员变量的线程安全性问题,例如某个请求上来之后,修改了某个变量;然后做其他的事情,紧接着其他请求也上来了,它又修改了变量,这可能就有问题。
    • 这一点我倒认为没有什么大不了的,我也算是写过一些Handler的,但其实比较少真的复杂到要搞一堆变量。如果是那样,是需要检讨的

    我同时还想到另外一个问题,假设IsReusable是设置为true的,同时又假设它的ProcessRequest却又需要比较长时间才能完成。那么会怎么样呢?

    就是说前一个请求还没有完成,这时候又有第二个请求过来。该如何处理?

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.IO;
    using System.Threading;
    using System.Web.SessionState;

    namespace WebApplication1
    {
        public class TestHandler:IHttpHandler
        {

            #region IHttpHandler 成员

            public bool IsReusable
            {
                get { return true; }
            }

            public void ProcessRequest(HttpContext context)
            {
                WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
                context.Response.Write("你请求的地址是:<b>" + context.Request.Url.ToString() + "</b>");
                Thread.Sleep(10000);
                WriteLogMessage("结束调用");
            }

            #endregion

            public TestHandler() {
                WriteLogMessage("对象实例被创建");
            }

            void WriteLogMessage(string msg)
            {
                string logFile = HttpContext.Current.Server.MapPath("Log.txt");
                File.AppendAllText(logFile, DateTime.Now.ToString()+ msg + Environment.NewLine);
            }
        }
    }

    上面的代码中,我加入一个让当前线程休眠的代码:让它休眠10秒钟。此时,我们来刷新浏览器看看会怎么样

    2009/6/15 12:05:59对象实例被创建
    2009/6/15 12:05:59当前被调用的请求是:/ie.chenxizhang
    2009/6/15 12:06:02对象实例被创建
    2009/6/15 12:06:02当前被调用的请求是:/google.chenxizhang
    2009/6/15 12:06:09结束调用 //这一句是ie.chenxizhang结束了
    2009/6/15 12:06:12结束调用 //这一句是google.chenxizhang结束了

    我用了两个浏览器来模拟两个用户的操作。你会惊奇地发现,虽然我们设置为true,但仍然创建了多个实例。这又是咋回事呢

    这个问题要这么理解了:因为现在是线程阻塞了,所以ASP.NET引擎会检测到这一点,他必须创建另外实例出来,否则很显然,第二个请求就必须等第一个请求结束之后才能开始工作,这显然是不可以接受的,这个现象,在大容量用户并发的时候可能是一个灾难

    好吧,再绕回来说,假如我们的操作时间比较长,但什么时候又不需要创建多个实例呢?答案就是说,如果异步的话。

    也正因为可能会有异步的情况,所以就出现了我们刚才所说的线程安全性问题。可以配合锁定机制避免一些问题。

    所以,针对IsReusable属性,我总结如下:

    • 这个属性默认为false(Visual studio提供的模板默认将其设置为false)
    • 如果设置为true,能提高性能,但要注意线程之间安全性问题
    • 如果设置为false,则线程是安全的

     

    2. 如何在Handler中使用会话状态(Session)?

    有些朋友在使用Handler的时候,念念不忘原先在页面编程中的会话状态(Session),一个典型的问题就是:在Handler里面能不能使用我的用户状态信息呀?

    例如,能不能添加下面这样的代码呢

    context.Response.Write("当前用户的状态值为:" + context.Session["Id"].ToString());

    答案是:当然可以。

    先别忙着乐,虽然可以添加这样的代码,但它默认却无法工作。你会收到下面这样的错误消息:未将对象设置到引用的实例

    其实,一个比较好的建议是:不要在Handler里面使用会话状态。甚至在整个网站都不要。

    但是,假设你就是认准了这个非用不可,那么你也可以通过下面的方式来实现

    为该类型实现一个接口:IRequiresSessionState

    这个接口不需要有任何方法实现,只需要定义一下就可以了。所以整个案例的代码,如下

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.IO;
    using System.Threading;
    using System.Web.SessionState;

    namespace WebApplication1
    {
        public class TestHandler:IHttpHandler,IRequiresSessionState
        {

            #region IHttpHandler 成员

            public bool IsReusable
            {
                get { return true; }
            }

            public void ProcessRequest(HttpContext context)
            {
                WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
                context.Response.Write("你请求的地址是:<b>" + context.Request.Url.ToString() + "</b>");
                context.Response.Write("当前用户的状态值为:" + context.Session["Id"].ToString());
                WriteLogMessage("结束调用");
            }

            #endregion

            public TestHandler() {
                WriteLogMessage("对象实例被创建");
            }

            void WriteLogMessage(string msg)
            {
                string logFile = HttpContext.Current.Server.MapPath("Log.txt");
                File.AppendAllText(logFile, DateTime.Now.ToString()+ msg + Environment.NewLine);
            }
        }
    }

    以上两点是编写HttpHandler中的难点和困惑点,大家可以参考领会一下

    本文由作者:陈希章 于 2009/6/15 17:40:23 发布在:http://www.cnblogs.com/chenxizhang/
    本文版权归作者所有,可以转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    更多博客文章,以及作者对于博客引用方面的完整声明以及合作方面的政策,请参考以下站点:陈希章的博客中心
  • 相关阅读:
    python使用thrift访问操作hbase
    js打开新页面
    设计模式
    c# dotfuscator 混淆后无法使用
    SQL server清空数据库日志脚本
    SQlserver 行转列
    SQLServer 脚本测试
    C# HttpWebRequest与HttpWebResponse详解
    反射
    SQl server master
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/1503779.html
Copyright © 2011-2022 走看看