修改网站内容( ConfigManager缓存行为不受控制且缓存时间不够长久缓存起来!)

优采云 发布时间: 2022-03-19 12:00

  修改网站内容(

ConfigManager缓存行为不受控制且缓存时间不够长久缓存起来!)

  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 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 s_settings;

public static Dictionary Settings

{

get{

Exception exceptin = s_loadConfigException;

if( exceptin != null )

throw exceptin;

return s_settings;

}

}

public static void LoadConfig(string xmlFilePath)

{

Dictionary dict = null;

try {

OutputCacheConfig config = XmlHelper.XmlDeserializeFromFile(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响应header,明确告诉浏览器缓存文件多长时间。

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

  

  但是,在IIS7中,这个参数不容易找到,需要进行如下操作才能找到: 选择网站(或网站子目录)节点,双击【HTTP Response Headers】 ]

  

  点击右侧的【Set Common Header】链接,

  

  这将显示:

  

  注意:[Enable Content Expiration] 此设置可以基于整个网站,也可以针对子目录,或者特定文件。

  注意:如果在 IIS7 中为子目录或文件设置了[Enable Content Expiration],之前的对话框看起来完全一样,但是在 IIS6 中,我们可以从对话框的标题栏中清楚地看到我们在做什么:

  

  有时候真的感觉 IIS7 界面在倒退!

  最后我想说:可以直接为整个网站开启内容过期,ASPX页面不会被缓存!

  说到这里,可能有人会想:这个过期时间应该设置多久?十分钟、两小时、一天还是一个月?在我看来,这个时间越长越好。可能有人又要说:如果我要升级一个JS文件,时间设置了很久,用户怎么更新呢?如果你问我这个问题,我只能说你的代码不合理(毕竟不能解决升级问题),如果你想知道为什么,请继续阅读。

  回到顶部

  解决资源文件升级问题

  对于一些小的网站,通常将资源文件和程序文件一起部署到一个网站中。这时候可以通过以下方式引用JS或者CSS文件:

  在这种情况下,如果使用上面提到的【启用内容过期】方法,那么当有 JS 和 CSS 文件需要升级时,由于浏览器的缓存还没有过期,所以不会请求服务器。此时会使用缓存的版本,所以可能会出现各种奇怪的bug

  对于上面提到的BUG,我认为根本原因是引用JS和CSS文件的方式有缺陷。该方法根本没有考虑版本升级的问题。正确的方法有两种: 1. 给文件名加上版本号,如jquery,每个版本一个文件(jquery-1.4.4.min.js)。 2. 在 URL 后添加版本号,使原来的 URL 失效。

  第一种方法没有缓存问题,因为每次升级都会生成一个新文件,但是维护大量文件的成本可能比较大,所以我推荐第二种方法来解决。

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

  页面运行时会产生如下输出:

  这两个工具方法的实现代码如下(在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("\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("\r\n", path, version);

}

  以上获取文件版本号的方法是比较简单的解决方案。每个引用的地方在生成 HTML 代码时都会访问文件的最后修改时间,这会给磁盘带来一点读取开销。如果你担心这个实现可能会对性能产生影响,你也可以添加一个配置文件(请自行实现),比如如下结构:

  如果你觉得这个配置文件需要手动维护,自动化程度不够,也可以用程序在运行时自动维护一个列表。总之,直接引用资源文件的方式是直接耦合,会给文件升级带来很多。来吧,我们可以用外部方法解耦这种直接耦合(给FileVersion加一个属性也可以把内部地址改成CDN地址)。

  回到顶部

  启用压缩

  压缩响应结果也是一种常用的优化方法网站。由于现在所有浏览器都支持压缩功能,如果响应结果可以在服务器端进行压缩,对于网速较慢的用户来说,网络传输时间减少了很多,最终的体验就是网页的显示速度页面变得更快!

  虽然IIS6提供了压缩设置接口,但配置是基于服务器级别的:

  

  注意:这里的【应用文件】不包括aspx。如果需要压缩aspx的响应,需要手动修改x:\WINDOWS\system32\inetsrv\MetaBase.xml文件(参考放大字体部分):

  

  注意:要修改 MetaBase.xml,需要停止 IIS Admin Service 服务。

  在IIS7中,我们可以在服务器级别配置压缩参数:

  

  然后在每个 网站 中打开或关闭压缩:

  

  注意:在 IIS7 中不再使用 MetaBase.xml,所以我们找不到 IIS6 的那些设置。 IIS7 压缩的过滤条件不再针对扩展,而是使用 mimeType 规则(保存在 applicationHost.config 中)。根据IIS7的压缩规则,当我们开启动态压缩时,aspx的响应结果会被压缩。

  两种压缩方式的区别: 1. 静态内容压缩:当服务器第一次响应静态文件时,会生成一个压缩的结果,并保存到磁盘中以供重复使用。 2. 动态内容压缩:【每次】响应客户端之前,响应结果会被压缩并在内存中完成,因此会对 CPU 造成一定的负担。

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

  回到顶部

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线