不修改代码就能优化ASP.NET网站性能的一些方法

本文将介绍一些方法用于优化ASP.NET网站性能,这些方法都是不须要修改程序代码的。 它们主要分为二个方面: 1. 利用ASP.NET自身的扩展性进行优化。 2. 优化IIS设置。css

配置OutputCache

用缓存来优化网站性能的方法,估计是无人不知的。ASP.NET提供了HttpRuntime.Cache对象来缓存数据,也提供了OutputCache指令来缓存整个页面输出。 虽然OutputCache指令使用起来更方便,也有很是好的效果, 不过,它须要咱们在那些页面中添加这样一个指令。html

对于设置过OutputCache的页面来讲,浏览器在收到这类页面的响应后,会将页面响应内容缓存起来。 只要在指定的缓存时间以内,且用户没有强制刷新的操做,那么就根本不会再次请求服务端, 而对于来自其它的浏览器发起的请求,若是缓存页已生成,那么就能够直接从缓存中响应请求,加快响应速度。 所以,OutputCache指令对于性能优化来讲,是颇有意义的(除非全部页面页面都在频繁更新)。java

在网站的优化阶段,咱们能够用Fiddler之类的工具找出一些内容几乎不会改变的页面,给它们设置OutputCache, 可是,按照传统的开发流程,咱们须要针对每一个页面文件执行如下操做: 1. 签出页面文件。 2. 添加OutputCache指令。 3. 从新发布页面。 4. 签入文件(若是遇到多分支并行,还可能须要合并操做)。 以上这些源代码管理制度会让一个简单的事情复杂化,那么,有没一种更简单的方法能解决这个问题呢?jquery

接下来,本文将介绍一种方法,它利用ASP.NET自身的扩展性,以配置文件的方式为页面设置OutputCache参数。 配置文件其它就是一个XML文件,内容以下:web

<?xml version="1.0" encoding="utf-8"?>
<OutputCache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Settings>
        <Setting Duration="3" FilePath="/Pages/a3.aspx"  />
        <Setting Duration="10" FilePath="/Pages/a5.aspx"  />
    </Settings>
</OutputCache>

看了这段配置,我想您应该也能猜到它能有什么做用。浏览器

每一行配置参数为一个页面指定OutputCache所须要的参数, 示例文件为了简单只使用二个参数,其它能够支持的参数请参考OutputCache指令缓存

为了能让这个配置文件有效,须要在web.config中配置如下内容(适用于IIS7):性能优化

<system.webServer>
    <modules>
        <add name="SetOutputCacheModule" type="WebSiteOptimize.SetOutputCacheModule, WebSiteOptimize" />
    </modules>
</system.webServer>

在这里,我注册了一个HttpModule,它的所有代码以下:服务器

public class SetOutputCacheModule : IHttpModule
{
    static SetOutputCacheModule()
    {
        // 加载配置文件
        string xmlFilePath = Path.Combine(HttpRuntime.AppDomainAppPath, "OutputCache.config");
        ConfigManager.LoadConfig(xmlFilePath);
    }

    public void Init(HttpApplication app)
    {
        app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);
    }

    void app_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        Dictionary<string, OutputCacheSetting> settings = ConfigManager.Settings;
        if( settings == null )
            throw new ConfigurationErrorsException("SetOutputCacheModule加载配置文件失败。");

        // 实现方法:
        // 查找配置参数,若是找到匹配的请求,就设置OutputCache
        OutputCacheSetting setting = null;
        if( settings.TryGetValue(app.Request.FilePath, out setting) ) {
            setting.SetResponseCache(app.Context);
        }
    }

ConfigManager类用于读取配置文件,并启用了文件依赖技术,当配置文件更新后,程序会自动从新加载:

internal static class ConfigManager
{
    private static readonly string CacheKey = Guid.NewGuid().ToString();

    private static Exception s_loadConfigException;
    private static Dictionary<string, OutputCacheSetting> s_settings;

    public static Dictionary<string, OutputCacheSetting> Settings
    {
        get{
            Exception exceptin = s_loadConfigException;
            if( exceptin != null )
                throw exceptin;

            return s_settings;
        }
    }

    public static void LoadConfig(string xmlFilePath)
    {
        Dictionary<string, OutputCacheSetting> dict = null;

        try {
            OutputCacheConfig config = XmlHelper.XmlDeserializeFromFile<OutputCacheConfig>(xmlFilePath, Encoding.UTF8);
            dict = config.Settings.ToDictionary(x => x.FilePath, StringComparer.OrdinalIgnoreCase);
        }
        catch( Exception ex ) {
            s_loadConfigException = new System.Configuration.ConfigurationException(
                "初始化SetOutputCacheModule时发生异常,请检查" + xmlFilePath + "文件是否配置正确。", ex);
        }


        if( dict != null ) {
            // 注册缓存移除通知,以便在用户修改了配置文件后自动从新加载。

            // 参考:细说 ASP.NET Cache 及其高级用法
            //          http://www.cnblogs.com/fish-li/archive/2011/12/27/2304063.html
            CacheDependency dep = new CacheDependency(xmlFilePath);
            HttpRuntime.Cache.Insert(CacheKey, xmlFilePath, dep,
                Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, CacheRemovedCallback);
        }

        s_settings = dict;
    }


    private static void CacheRemovedCallback(string key, object value, CacheItemRemovedReason reason)
    {
        string xmlFilePath = (string)value;

        // 因为事件发生时,文件可能尚未彻底关闭,因此只好让程序稍等。
        System.Threading.Thread.Sleep(3000);

        // 从新加载配置文件
        LoadConfig(xmlFilePath);
    }
}

有了AutoSetOutputCacheModule,咱们就能够直接使用配置文件为页面设置OutputCache参数,而不须要修改任何页面,是否是很容易使用?

说明:MyMVC框架已支持这种功能,全部相关的能够从MyMVC框架的源码中获取。

建议:对于一些不多改变的页面,缓存页是一种颇有效的优化方法。

启用内容过时

每一个网站都会有一些资源文件(图片,JS,CSS),这些文件相对于ASPX页面来讲, 它们的输出内容极有可能在一段长时间以内不会有变化, 而IIS在响应这类资源文件时不会生成Cache-Control响应头。 在这种状况下,浏览器或许会缓存它们,也许会再次发起请求(好比重启后),总之就是缓存行为不受控制且缓存时间不够长久。

有没有想过能够把它们在浏览器中长久缓存起来呢?

为了告诉浏览器将这些文件长久缓存起来,减小一些无心义的请求(提升页面呈现速度),咱们能够在IIS中启用内容过时, 这样设置后,IIS就能生成Cache-Control响应头,明确告诉浏览器将文件缓存多久。

在IIS6中,这个参数很好找到:

然而,在IIS7中,这个参数不容易被发现,须要如下操做才能找到: 选择网站(或者网站子目录)节点,双击【HTTP响应标头】

再点击右边的【设置经常使用标头】连接,

此时将会显示:

说明:【启用内容过时】这个设置能够基于整个网站,也能够针对子目录,或者一个具体的文件。

注意:若是您在IIS7中针对某个子目录或者文件设置【启用内容过时】,前面的对话框看起来是如出一辙的, 然而,在IIS6中,咱们能够清楚地从对话框的标题栏中知道咱们在作什么:

有时真感受IIS7的界面在退步!

最后我想说一句:能够直接为整个网站启用内容过时,ASPX页面是不会被缓存的!

说到这里可能有人会想:这个过时时间我该设置多久呢? 十分钟,2个小时,一天,仍是一个月? 在我看来,这个时间越久越好。 可能有人又会说了:万一我要升级某个JS文件怎么办,时间设置久了,用户怎么更新呢? 若是你问我这个问题,我也只能说是你的代码不合理(毕竟你解决不了升级问题),想知道缘由的话,请继续阅读。

解决资源文件升级问题

对于一些规模不大的网站来讲,一般会将资源文件与程序文件一块儿部署到一个网站中。 这时可能会采用下面的方式来引用JS或者CSS文件:

<link type="text/css" href="aaaa.css" rel="Stylesheet" />
<script type="text/javascript" src="bbb.js"></script>

在这种状况下,若是使用了前面所说的【启用内容过时】方法,那么当有JS,CSS文件须要升级时, 因为浏览器的缓存尚未过时,因此就不会请求服务器,此时会使用已缓存的版本, 所以可能会出现各类奇怪的BUG

对于前面谈到的BUG,我认为根源在于引用JS,CSS文件的方式有缺陷, 那种方法彻底没有考虑到版本升级问题, 正确的方法有二种: 1. 给文件名添加版本号,像jquery那样,每一个版本一个文件(jquery-1.4.4.min.js)。 2. 在URL后面添加一个版本号,让原先的URL失效。

第一种方法因为每次升级都产生了一个新文件,因此不存在缓存问题,可是,维护一大堆文件的成本可能会比较大, 所以我建议采用第二种方法来解决。

MyMVC的示例代码中,我使用了下面的方法来引用这些资源文件:

<%= HtmlExtension.RefCssFileHtml("/css/StyleSheet.css")%>
<%= HtmlExtension.RefJsFileHtml("/js/MyPage/fish.js")%>

在页面运行时,会产生以下的输出结果:

<link type="text/css" rel="Stylesheet" href="/css/StyleSheet.css?_t=634642185820000000" />
<script type="text/javascript" src="/js/MyPage/fish.js?_t=634642154020000000"></script>

这二个工具方法的实现代码以下(在MyMVC的示例代码中):

private static readonly string s_root = HttpRuntime.AppDomainAppPath.TrimEnd('\\');

public static string RefJsFileHtml(string path)
{
    string filePath = s_root + path.Replace("/", "\\");
    string version = File.GetLastWriteTimeUtc(filePath).Ticks.ToString();
    return string.Format("<script type=\"text/javascript\" src=\"{0}?_t={1}\"></script>\r\n", path, version);
}

public static string RefCssFileHtml(string path)
{
    string filePath = s_root + path.Replace("/", "\\");
    string version = File.GetLastWriteTimeUtc(filePath).Ticks.ToString();
    return string.Format("<link type=\"text/css\" rel=\"Stylesheet\" href=\"{0}?_t={1}\" />\r\n", path, version);
}

上面这种获取文件版本号的方法,是一种比较简单的解决方案。 每一个引用的地方在生成HTML代码时,都会访问文件的最后修改时间,这会给磁盘带来一点读的开销, 若是您担忧这种实现方式可能会给性能带来影响,那么也能够增长一个配置文件的方式来解决(请自行实现), 例如如下结构:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfFileVersion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <FileVersion FilePath="/js/JScript.js" Version="255324" />
    <FileVersion FilePath="/css/StyleSheet.css" Version="2324235" />
</ArrayOfFileVersion>

若是您认为这种配置文件须要手工维护,不够自动化,还能够采用程序的方式自动在运行时维护一个列表, 总之,直接引用资源文件的方法是一种直接耦合,会给文件升级带来麻烦, 咱们能够经过一个外部方法来解开这个直接耦合(给FileVersion增长一个属性还还能够将内部地址改为一个CDN地址)。

启用压缩

压缩响应结果也是经常使用的网站优化方法,因为如今的浏览器都已支持压缩功能, 所以,若是在服务端能压缩响应结果,对于网速较慢的用户来讲,会减小不少网络传输时间,最终的体验就是网页显示速度变快了!

IIS6虽然提供压缩的设置界面,然而配置是基于服务器级别的:

注意:这里的【应用程序文件】不包括aspx,若是须要压缩aspx的响应, 须要手工修改x:\WINDOWS\system32\inetsrv\MetaBase.xml文件(参考加大字号部分):

<IIsCompressionScheme    Location ="/LM/W3SVC/Filters/Compression/gzip"
        HcCompressionDll="%windir%\system32\inetsrv\gzip.dll"
        HcCreateFlags="1"
        HcDoDynamicCompression="TRUE"
        HcDoOnDemandCompression="TRUE"
        HcDoStaticCompression="TRUE"
        HcDynamicCompressionLevel="9"
        HcFileExtensions="htm
            html
            txt
            js
            css
            htc"
        HcOnDemandCompLevel="10"
        HcPriority="1"
        HcScriptFileExtensions="asp
            exe
            aspx
            axd"
    >

说明:要修改MetaBase.xml,须要中止IIS Admin Service服务。

在IIS7中,咱们能够在服务器级别配置压缩参数:

而后在每一个网站中开启或者关闭压缩功能:

说明:IIS7中已经再也不使用MetaBase.xml,因此咱们找不到IIS6的那些设置了。IIS7压缩的过滤条件再也不针对扩展名,而是采用了mimeType规则(保存在applicationHost.config)。 根据IIS7的压缩规则,当咱们启用动态压缩后,会压缩aspx的响应结果。

二种压缩方法的差异: 1. 静态内容压缩:当服务器在第一次响应某个静态文件时,会生成一个压缩后的结果,并保存到磁盘中,以便重用。 2. 动态内容压缩:【每次】在响应客户端以前,压缩响应结果,在内存中完成,所以会给CPU带来一些负担。

注意:要不要【启用动态内容压缩】这个参数,须要评估服务器的CPU是否能以承受(观察任务管理器或者查看性能计数器)。

删除无用的HttpModule

对一个网站来讲,ASP.NET提供的有些HttpMoudle可能并非须要的, 然而,若是你不去手工禁用它们,它们其实会一直运行。 好比 会禁用下面这些HttpMoudle:

<httpModules>
    <remove name="Session"/>
    <remove name="RoleManager"/>
    <remove name="PassportAuthentication"/>
    <remove name="Profile"/>
    <remove name="ServiceModel"/>
</httpModules>

对于使用Forms身份认证的网站的来讲,下面这些HttpModule也是能够禁用的:

<httpModules>
    <remove name="WindowsAuthentication"/>
    <remove name="FileAuthorization"/>
</httpModules>

其它优化选项

优化ASP.NET网站是一个大的话题,除了博客中介绍的这些方法以外,还有如下方法也是能够参考的: 1. 升级服务器硬件配置。 2. 使用Windows Server 2008以上版本操做系统(网络性能比2003要好)。 3. 优化操做系统配置(例如禁用不须要的服务)。 4. 禁用调试模式。 5. 网站使用专用应用程序池。

相关文章
相关标签/搜索