為了增加網站的承載量,我們通常會在謹慎的在關鍵處加上Cache來避免高成本的開銷,(例如需要高CPU運算、大量的IO操作,但卻並不是隨時都需要動態獲得最新值之處),除了在程式碼中使用Cache之外,我們也常常會使用Asp.Net提供的Output Cache,它可以幫助我們將整個網頁包含Html做鏡像的Cache,來快速地回應使用者的Request,在使用Cache很重要的一點是所有的Cache都必須要能夠有效及時地進行清除或更新,才不會造成系統使用上的困擾。
前言
為了增加網站的承載量,我們通常會在謹慎的在關鍵處加上Cache來避免高成本的開銷,
(例如需要高CPU運算、大量的IO操作,但卻並不是隨時都需要動態獲得最新值之處),
除了在程式碼中使用Cache之外,我們也常常會使用Asp.Net提供的Output Cache,
它可以幫助我們將整個網頁包含Html做鏡像的Cache,來快速地回應使用者的Request,
在使用Cache很重要的一點是所有的Cache都必須要能夠有效及時地進行清除或更新,
才不會造成系統使用上的困擾。
實際演練
首先我們來很簡單設定一個頁面呈現目前的時間,並且加上OutputCache,
並建立一個方法用來清除OutputCache。
Step1. 先建立Asp.Net MVC的Routing規則
Global.asax
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Step2. 建立一個簡單的Action, 有一個參數id, 並且會顯示目前的時間
HomeController.cs
public class HomeController : Controller
{
public ActionResult Index(int id)
{
return View(id);
}
}
Index.cshtml
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@model int
<h2>Index</h2>
ID: @Model <br />
DateTime: @DateTime.Now.ToString()
Step3. 執行網址/Home/Index/1, 並重複更新網頁, 每次網頁上的時間都會不同
Step4. 加上Output Cache Attribute, 並設定根據id的不同, 分別做不同的Cache, Cache時間為60分鐘
HomeController.cs
[OutputCache(Duration=3600, VaryByParam="id")]
public ActionResult Index(int id)
{
return View(id);
}
Step5. 執行網址/Home/Index/1, 並重複更新網頁, 可以看到Cache有生效
Step6. 建立一個Action, 用來清除Output Cache, 已確保網站隨時能更新
HomeController.cs
public ActionResult RemoveCache(int id)
{
//// Outputcache root
var url = Url.Action("Index", "Home", new { id = id });
//// Clean output cache by root
HttpResponse.RemoveOutputCacheItem(url);
return Content(string.Format("Clear Output Cache by Url {0} Success!", url));
}
Step7. 執行一次之後, 再執行/Home/RemoveCache/1, 再次執行網址, 可以看到OutputCache成功的被清除
特殊情境
通過上面的例子, 我們了解了如何清除OutputCache,
但我在線上網站實際操作的時候, 卻發現沒有這麼簡單, 讓我們來看看下個例子
我們在實際環境中, 不可能每個網站都是提供一個參數id, 也符合預設的Routing
例如我們在撰寫部落格的時候, 常常會使用名字加文章序號來當作參數
Step1. 建立一個Action來當作部落格, 提供兩個參數author, postname
並建立一個用來清除BlogCache的Action
HomeController.cs
[OutputCache(Duration = 3600, VaryByParam = "author;postname")]
public ActionResult Blog(string author, string postname)
{
this.ViewBag.Author = author;
this.ViewBag.PostName = postname;
return View();
}
public ActionResult RemoveBlogCache(string author, string postname)
{
//// Outputcache root
var url = Url.Action("Blog", "Home", new { author = author, postname = postname });
//// Clean output cache by root
HttpResponse.RemoveOutputCacheItem(url);
return Content(string.Format("Clear Output Cache by Url {0} Success!", url));
}
Blog.cshtml
@{
ViewBag.Title = "Blog";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Blog</h2>
Author: @ViewBag.Author
PostName: @ViewBag.PostName
DateTime: @DateTime.Now.ToString()
Step2. 執行網址/Home/Blog?author=kirk&postname=test, 再執行/Home/RemoveBlogCache?author=kirk&postname=test,
卻發現OutputCache並沒有被清除
Step3. 原來事情並沒有那麼單純, 清除OutputCache要直接指定OutputCache的Root才行,
並且它會清除所有關聯的Cache,包括VaryByParam的參數
而Root的祕密就在Global.asax的Routing設定中可以發現...
透過網址/Home/Blog?author=kirk&postname=test, 我們可以發現這個網址其實是符合Default這個Routing的,
但因為我們沒有id這個參數, 所以可想而知我們的Root網址應該為/Home/Blog,
我們重新修改清除OutputCache的Action如下
public ActionResult RemoveBlogCache()
{
//// Outputcache root
var url = Url.Action("Blog", "Home");
//// Clean output cache by root
HttpResponse.RemoveOutputCacheItem(url);
return Content(string.Format("Clear Output Cache by Url {0} Success!", url));
}
Step4. 執行網址/Home/Blog?author=kirk&postname=test, 再執行/Home/RemoveBlogCache,
再次執行文章網址, 可以發現OutputCache確實的被清除了
聰明的你, 想必也在此時發現事情並不單純,
由於我們在清除OutputCache是直接針對/Home/Blog做清除,
所以不單單只是這篇文章, 而是系統中所有作者和所有文章的OutputCache都被清除了,
這跟我們原本想像的一點都不一樣, 我並不想一次清除所有文章的Cache, 只想要可以清除單篇文章的Cache就好
修改做法
重新在Global.asax中為我們的Action指定一組專屬的Routing,
來為每篇文章網址產生不同的Cache Root
Global.asax
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Blog",
url: "{controller}/{action}/{author}/{postname}",
defaults: new { controller = "Home", action = "Blog" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
重新改寫清除OutputCache的Action
HomeController.cs
public ActionResult RemoveBlogCache(string author, string postname)
{
//// Outputcache root
var url = Url.Action("Blog", "Home", new { author = author, postname = postname });
//// Clean output cache by root
HttpResponse.RemoveOutputCacheItem(url);
return Content(string.Format("Clear Output Cache by Url {0} Success!", url));
}
分別執行網址, 並清除OutputCache在重新執行
/Home/Blog/Kirk/Test1
/Home/Blog/Jason/Test2
/Home/RemoveBlogCache/Kirk/Test1
/Home/Blog/Kirk/Test1
/Home/Blog/Jason/Test2
可以看到能夠針對單篇文章進行OutputCache的清除了,
妥善的指定Routing的網址對OutputCache來說也是相當重要的!
感想
當初在對於清除OutputCache的機制不夠熟悉時,
一直無法理解為什麼OutputCache的清除有時可以成功, 有時卻又總是失敗,
花時間測試過後才恍然大悟, 原來跟Routing的設定有相關聯性,
要清除該網址的OutputCache還需要找到對應的Routing Root是哪一個才能對症下藥,
理解之後也就不用擔心會有無法清除OutputCache的問題囉!