• OutputCache 缓存key的创建 CreateOutputCachedItemKey


    有关OutputCache的相关资料大家可以查看 OutputCacheProvider OutputCache的一点点认识 ,我们还是复习一下OutputCache内容,OutputCache 的处理是在OutputCacheModule类中注册ResolveRequestCache、UpdateRequestCache这2个方法,一个 用于获取一个用于设置缓存。缓存内容分为两部分,一部分为缓存策略CachedVary,一部分为缓存数据CachedRawResponse,一个页面 缓存策略只有一个CachedVary,但是它却可以有多个缓存内容CachedRawResponse缓存内容的获取和设置主要是依赖于HttpResponse的GetSnapshot()  UseSnapshot(HttpRawResponse rawResponse, bool sendBody)方法。一般我们的缓 存都是要占用存储空间的尽量减少缓存内容的副本是非常重要的,那么我们现在就来看看缓存key是如何创建的,key的创建取决于 CreateOutputCachedItemKey方法。CreateOutputCachedItemKey方法的内容还是比较复杂的,现在让我们一 起来看看它的具体实现吧。

    CreateOutputCachedItemKey方法又是如何调用的了,创建缓存策 略key的代码:this.CreateOutputCachedItemKey(context, null);创建缓存key的代码:this.CreateOutputCachedItemKey(context, cachedVary)区别就在于参数CachedVary 的值一个为null,一个是真正的CachedVary 实例。我这里的代码是通过Reflector.exe反编译得到,感觉和真实的代码有点差别,不过逻辑上是一样的。

    我还是以一个实际的例子来变解析边说明,我这里是用asp.net mvc建立的一个demo。请求url:http://localhost:7503/Home/index 那么path就应该是:Home/index

    首先我们的可以需要区分我们的请求是Get还是Post,Post以a1打头,否则已a2打头,紧接着追加当前的Path:

     if (verb == HttpVerb.POST)
        {
            builder = new StringBuilder("a1", path.Length + "a1".Length);
        }
        else
        {
            builder = new StringBuilder("a2", path.Length + "a2".Length);
        }
        builder.Append(CultureInfo.InvariantCulture.TextInfo.ToLower(path));

    到这个时候我们的缓存策略key及确定的,我这里的策略key为:a2/home/index

    如果我们的cachedVary不为null则继续执行:

    for (int i = 0; i <= 2; i++)
            {
                int num;
                string[] array = null;
                NameValueCollection serverVarsWithoutDemand = null;
                bool flag = false;
                switch (i)
                {
                    case 0:
                        builder.Append("H");
                        array = cachedVary._headers;
                        if (array != null)
                        {
                            serverVarsWithoutDemand = request.GetServerVarsWithoutDemand();
                        }
                        break;

                    case 1:
                        builder.Append("Q");
                        array = cachedVary._params;

                        if (request.HasQueryString && ((array != null) || cachedVary._varyByAllParams))
                        {
                            serverVarsWithoutDemand = request.QueryString;
                            flag = cachedVary._varyByAllParams;
                        }
                        break;

                    default:
                        builder.Append("F");
                        if (verb == HttpVerb.POST)
                        {
                            array = cachedVary._params;
                            if (request.HasForm && ((array != null) || cachedVary._varyByAllParams))
                            {
                                serverVarsWithoutDemand = request.Form;
                                flag = cachedVary._varyByAllParams;

                            }
                        }
                        break;
                }

        if (flag && (serverVarsWithoutDemand.Count > 0))
                {
                    array = serverVarsWithoutDemand.AllKeys;
                    num = array.Length - 1;
                    while (num >= 0)
                    {
                        if (array[num] != null)
                        {
                            array[num] = CultureInfo.InvariantCulture.TextInfo.ToLower(array[num]);
                        }
                        num--;
                    }
                    Array.Sort(array, InvariantComparer.Default);
                }
                if (array != null)
                {
                    num = 0;
                    int length = array.Length;
                    while (num < length)
                    {
                        string str = array[num];
                        if (serverVarsWithoutDemand == null)
                        {
                            varyByCustomString = "+n+";
                        }
                        else
                        {
                            varyByCustomString = serverVarsWithoutDemand[str];
                            if (varyByCustomString == null)
                            {
                                varyByCustomString = "+n+";
                            }
                        }
                        builder.Append("N");
                        builder.Append(str);
                        builder.Append("V");
                        builder.Append(varyByCustomString);

                        num++;
                    }
                }
            }

    这段代码说白了就是给key值追加HQF3个字符,这个循环首先处理服务器的数据,

     array = cachedVary._headers;
     serverVarsWithoutDemand = request.GetServerVarsWithoutDemand();

    其次是处理QueryString数据:

     array = cachedVary._params;
    serverVarsWithoutDemand = request.QueryString;

    最后处理Form数据
     array = cachedVary._params;
    serverVarsWithoutDemand = request.Form;

    serverVarsWithoutDemand是NameValueCollection 类型的数据,这里循环serverVarsWithoutDemand里面的每个key,每个key对应的追加字符串为N+key+V+value,如果value是null则从新赋值为“+n+”,可以看见不同的请求这里的key及有所不同了。在QueryString和Form时这里的serverVarsWithoutDemand的取值与
    cachedVary._varyByAllParams有关,cachedVary的创建是在OutputCacheModule 的OnLeave方法中:

     vary = new CachedVary(varyByContentEncodings, varyByHeaders, varyByParams, varyByAllParams, currentSettings.VaryByCustom);

    varyByAllParams的取值如下:

    bool varyByAllParams = false;
    if (varyByParams != null)
       {
        varyByAllParams = (varyByParams.Length == 1) && (varyByParams[0] == "*");
       }
    可见varyByAllParams基本都是false,只有varyByParams有且紧有一个元素并且为*时,varyByAllParams才为true,varyByAllParams为false时这里的serverVarsWithoutDemand取值为GetServerVarsWithoutDemand方法,与我们的QueryString、Form3没什么关系。GetServerVarsWithoutDemand()方法大家可能都不怎么熟悉,我们来看看它的定义:

    internal NameValueCollection GetServerVarsWithoutDemand()
    {
        return this.GetServerVars();
    }
    对这个方法不了解不要紧,我们有一个ServerVariables(获取 Web 服务器变量的集合属性和他相似:

    public NameValueCollection ServerVariables
    {
        get
        {
            if (HttpRuntime.HasAspNetHostingPermission(AspNetHostingPermissionLevel.Low))
            {
                return this.GetServerVars();
            }
            return this.GetServerVarsWithDemand();
        }
    }
     其中GetServerVarsWithDemand方法也是调用GetServerVars方法。现在serverVarsWithoutDemand的数据我们也搞清楚了。

    builder.Append("C"); 接下来在追加字符C

    接下来我们该处理缓存_varyByCustom的配置了
            if (cachedVary._varyByCustom != null)
            {
                builder.Append("N");
                builder.Append(cachedVary._varyByCustom);
                builder.Append("V");

                try
                {
                    varyByCustomString = context.ApplicationInstance.GetVaryByCustomString(context, cachedVary._varyByCustom);
                    if (varyByCustomString == null)
                    {
                        varyByCustomString = "+n+";
                    }
                }
                catch (Exception exception)
                {
                    varyByCustomString = "+e+";
                    HttpApplicationFactory.RaiseError(exception);
                }
                builder.Append(varyByCustomString);
            }

    这个方法很好明白,如果_varyByCustom不为null那么我们就追加N+key+V+value格式的字符。其中key就是_varyByCustom字符串,value是调用context.ApplicationInstance.GetVaryByCustomString(context, cachedVary._varyByCustom)得到的value,如果value值为null,则设置为“+n+”

     builder.Append("D");
            if (((verb == HttpVerb.POST) && cachedVary._varyByAllParams) && (request.Form.Count == 0))
            {
                int contentLength = request.ContentLength;
                if ((contentLength > 0x3a98) || (contentLength < 0))
                {
                    return null;
                }
                if (contentLength > 0)
                {
                    byte[] asByteArray = ((HttpInputStream) request.InputStream).GetAsByteArray();
                    if (asByteArray == null)
                    {
                        return null;
                    }
                    varyByCustomString = Convert.ToBase64String(MachineKeySection.HashData(asByteArray, null, 0, asByteArray.Length));
                    builder.Append(varyByCustomString);

                }
            }

    这段代码主要是给key追加一个字符D,然后在处理Post的请求(非表单request.Form.Count == 0)把请求的内容(字节)转化为字符追加到key中,一般的http很少会发生此情况,典型的是HttpWebRequest发生的Post请求会触发。

    builder.Append("E");
            string[] strArray2 = cachedVary._contentEncodings;
            if (strArray2 != null)
            {
                string httpHeaderContentEncoding = context.Response.GetHttpHeaderContentEncoding();
                if (httpHeaderContentEncoding != null)
                {
                    for (int j = 0; j < strArray2.Length; j++)
                    {
                        if (strArray2[j] == httpHeaderContentEncoding)
                        {
                            builder.Append(httpHeaderContentEncoding);
                            break;
                        }
                    }
                }
            }

    这段代码首先给key追加一个字符E,然后最佳ContentEncoding,ContentEncoding的取值为 context.Response.GetHttpHeaderContentEncoding()并且在缓存策略中的 _contentEncodings存在才追加。

    到现在为止我们的CreateOutputCachedItemKey方法讲完了,缓存策略的key没什么说的,与Http请求方式Get和Post、Request的Path属性有关。但是缓存数据的key有关对象:

       (1)与我们的_headers有关,即配置中的 VaryByHeader属性有关,VaryByHeader取值不同,key则不同

    (2)与_varyByAllParams有关,当它为true时,实际上就是与 request.QueryString有关,如果此请求是Post则还与request.Form有关;_varyByAllParams默认为 false,为true的情况也很单一 varyByAllParams = (varyByParams.Length == 1) && (varyByParams[0] == "*")

    (3)与_varyByCustom有关,它会把 context.ApplicationInstance.GetVaryByCustomString(context, cachedVary._varyByCustom)方法返回值追加到key中,

    (4)与_contentEncodings有关,如果 context.Response.GetHttpHeaderContentEncoding()返回的值在_contentEncodings中则追加其返回值。

    注意:如果此Http处理是一个Post并且request.Form.Count ==0&& _varyByAllParams为rue的时候海域我们post过来的数据有关。

  • 相关阅读:
    优化慢执行或慢查询的方法
    Top K问题的两种解决思路
    优先队列实现 大小根堆 解决top k 问题
    进程间的八种通信方式----共享内存是最快的 IPC 方式
    二叉树基础之按层打印
    按层打印二叉树--每行打印一层
    给定一颗完全二叉树,给每一层添加上next的指针,从左边指向右边
    缓存与数据库一致性保证
    一致性哈希算法原理
    Linux复制指定目录下的文件夹结构
  • 原文地址:https://www.cnblogs.com/majiang/p/2788879.html
Copyright © 2020-2023  润新知