阅读目录
本文将介绍一些方法用于优化ASP.NET网站性能,这些方法都是不需要修改程序代码的。 它们主要分为二个方面: 1. 利用ASP.NET自身的扩展性进行优化。 2. 优化IIS设置。
回到顶部用缓存来优化网站性能的方法,估计是无人不知的。ASP.NET提供了HttPRuntime.Cache对象来缓存数据,也提供了 OutputCache指令来缓存整个页面输出。 虽然OutputCache指令使用起来更方便,也有非常好的效果, 不过,它需要我们在那些页面中添加这样一个指令。
对于设置过OutputCache的页面来说,浏览器在收到这类页面的响应后,会将页面响应内容缓存起来。 只要在指定的缓存时间之内,且用户没有强制刷新的操作,那么就根本不会再次请求服务端, 而对于来自其它的浏览器发起的请求,如果缓存页已生成,那么就可以直接从缓存中响应请求,加快响应速度。 因此,OutputCache指令对于性能优化来说,是很有意义的(除非所有页面页面都在频繁更新)。
在网站的优化阶段,我们可以用Fiddler之类的工具找出一些内容几乎不会改变的页面,给它们设置OutputCache, 但是,按照传统的开发流程,我们需要针对每个页面文件执行以下操作: 1. 签出页面文件。 2. 添加OutputCache指令。 3. 重新发布页面。 4. 签入文件(如果遇到多分支并行,还可能需要合并操作)。 以上这些源代码管理制度会让一个简单的事情复杂化,那么,有没一种更简单的方法能解决这个问题呢?
接下来,本文将介绍一种方法,它利用ASP.NET自身的扩展性,以配置文件的方式为页面设置OutputCache参数。 配置文件其它就是一个xml文件,内容如下:
<?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.VEVb.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代码时,都会访问文件的最后修改时间,这会给磁盘带来一点读的开销
新闻热点
疑难解答