Asp.net 服务端缓存引发的一点思考

我们知道asp.net要经历以下事件

BeginRequest
AuthenticateRequest
PostAuthenticateRequest
AuthorizeRequest
PostAuthorizeRequest
ResolveRequestCache
PostResolveRequestCache
PostMapRequestHandler
AcquireRequestState
PostAcquireRequestState
PreRequestHandlerExecute
....IHttpHandler 类的 ProcessRequest 方法,真正处理请求的地方时一个比较耗时的处理
PostRequestHandlerExecute
eleaseRequestState
PostReleaseRequestState 事件。
UpdateRequestCache
PostUpdateRequestCache
EndRequest

  而在服务端我们缓存经常会用到OutputCache,之所以能用它只要是因为OutputCacheModule中有如下代码:

  void IHttpModule.Init(HttpApplication app)
    {
        if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
        {
            app.ResolveRequestCache += new EventHandler(this.OnEnter);
            app.UpdateRequestCache += new EventHandler(this.OnLeave);
        }
    }
同样只要看过OutputCacheModule源码的人都知道这里面面的代码比较难懂,为了提高性能,我们能否把读取缓存的时间往前推推到 BeginRequest,一旦有缓存这个时候我们就返回数据结束输出流。那么什么时候设置缓存了?我们可以选着在 ReleaseRequestState事件中处理,在这个处理中我们需要截获输出流的内容。

在本例中只是提出这种缓存的思想,在项目中运用需要注意的事情还很多。

首先我们需要实现缓存机制,这里我们用字典来存储数据永不过期。

    public class Cache
    {
        static ConcurrentDictionary<string, string> dict = new ConcurrentDictionary<string, string>();
        public static void Add(string key, string value)
        {
            if (dict.ContainsKey(key))
            {
                 dict[key] = value;
                 
            }
            else
            {
                 dict.TryAdd(key, value);
            }
        }
        public static string Get(string key)
        {
            string result = string.Empty;
            dict.TryGetValue(key, out result);
            return result;
        }
    }

代码很简单吧,同样需要截获输出流的代码


    public class PageFilter : Stream
    {
        Stream responseStream;
        long position;
        StringBuilder responseHtml;

        public PageFilter(Stream inputStream)
        {
            responseStream = inputStream;
            responseHtml = new StringBuilder();
        }

        #region Filter overrides
        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Close()
        {
            responseStream.Close();
        }

        public override void Flush()
        {
            responseStream.Flush();
        }

        public override long Length
        {
            get { return 0; }
        }

        public override long Position
        {
            get { return position; }
            set { position = value; }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return responseStream.Seek(offset, origin);
        }

        public override void SetLength(long length)
        {
            responseStream.SetLength(length);
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return responseStream.Read(buffer, offset, count);
        }
        #endregion

        #region Dirty work
        public override void Write(byte[] buffer, int offset, int count)
        {
            HttpResponse response = HttpContext.Current.Response;
            string charset = response.Charset ?? "utf-8";
            string finalHtml = Encoding.GetEncoding(charset).GetString(buffer, offset, count);
          

            int index = finalHtml.IndexOf("</html>");
            if (index<1)
            {
                responseHtml.Append(finalHtml);
            }
            else
            {
                responseHtml.Append(finalHtml);
                finalHtml = responseHtml.ToString(); 
                byte[] data = Encoding.GetEncoding(charset).GetBytes(finalHtml);
                /*缓存处理*/
                string key = HttpContext.Current.Request.Url.ToString();
                Cache.Add(key, finalHtml);
                /*缓存处理*/
                responseStream.Write(data, 0, data.Length);
            }

        }

        #endregion
    }

那么如何运用了?在Global.asax.cs文件中:

  public override void Init()
        {
            base.Init();
            this.BeginRequest += new EventHandler(MvcApplication_BeginRequest);
            this.ReleaseRequestState += new EventHandler(MvcApplication_ReleaseRequestState);
        }

        void MvcApplication_BeginRequest(object sender, EventArgs e)
        {
            string key = HttpContext.Current.Request.Url.ToString();
            HttpResponse response = HttpContext.Current.Response;
            string result = Cache.Get(key);
            if (!string.IsNullOrEmpty(result))
            {
                response.Write(result);
                ((HttpApplication)sender).CompleteRequest();
            }

        }

        void MvcApplication_ReleaseRequestState(object sender, EventArgs e)
        {

            HttpResponse response = HttpContext.Current.Response;
            if (response.ContentType == "text/html")
            {
                response.Filter = new PageFilter(response.Filter);
            }
        }
运行结果,无论你运行多少次结果都如下

这种处理方式和OutputCache一样不用区分你的程序是否是mvc模式。在实际项目中我们可以用Memcached、Redis、AppFabric 来做分布式缓存。比如像凡客、京东某些页面就可以采用这种思想来实现,比你在页面或action任何地方读取缓存的效率要高出很多,当然效率高的代价就是它的适用范围比较窄,实际中往往不是缓存一个完整的整个页面内容,而只是缓存一部分。

欢迎大家拍砖
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值