ASP.NET cache de saída (ou seja, HTML estático) era baseado em memória até o .NET 4.0. Isso significa que, se nosso site contiver muito cache, é fácil consumir memória local. Agora, com a ajuda de . Em OutputCacheProvider no .NET 4.0, temos várias opções para criar nosso próprio cache. Por exemplo, podemos armazenar o cache de saída HTML em um servidor de cluster distribuído com memcache ou em MongoDB (um banco de dados orientado a documentos comumente usado, leia http://msdn.microsoft.com/zh-cn/magazine/gg650661.aspx). Claro, também podemos armazenar o cache como um arquivo no disco rígido, que é a forma mais barata de fazer isso considerando a escalabilidade, e este artigo é sobre como construir um cache de arquivos personalizado.
1:FornecedorCacheSaída OutputCacheProvider é uma classe base abstrata que precisamos sobrescrever quatro de seus métodos, que são: Método Add para inserir o item especificado no cache de saída. Get, que retorna uma referência ao item especificado no cache de saída. Método Remove para remover o item especificado do cache de saída. set, insere o item especificado no cache de saída e sobrescreve o item se ele estiver em cache.
2: Crie sua própria classe de tratamento de cache de arquivos O tipo é FileCacheProvider, e o código é o seguinte:
- public class FileCacheProvider : OutputCacheProvider
- {
- private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
-
- public override void Initialize(string name, NameValueCollection attributes)
- {
- base.Initialize(name, attributes);
- CachePath = HttpContext.Current.Server.MapPath(attributes["cachePath"]);
- }
-
- public override object Add(string key, object entry, DateTime utcExpiry)
- {
- Object obj = Get(key);
- if (obj != null) //这一步很重要
- {
- return obj;
- }
- Set(key,entry,utcExpiry);
- return entry;
- }
-
- public override object Get(string key)
- {
- string path = ConvertKeyToPath(key);
- if (!File.Exists(path))
- {
- return null;
- }
- CacheItem item = null;
- using (FileStream file = File.OpenRead(path))
- {
- var formatter = new BinaryFormatter();
- item = (CacheItem)formatter.Deserialize(file);
- }
-
- if (item.ExpiryDate <= DateTime.Now.ToUniversalTime())
- {
- log.Info(item.ExpiryDate + "*" + key);
- Remove(key);
- return null;
- }
- return item.Item;
- }
-
-
- public override void Set(string key, object entry, DateTime utcExpiry)
- {
- CacheItem item = new CacheItem(entry, utcExpiry);
- string path = ConvertKeyToPath(key);
- using (FileStream file = File.OpenWrite(path))
- {
- BinaryFormatter formatter = new BinaryFormatter();
- formatter.Serialize(file, item);
- }
- }
-
- public override void Remove(string key)
- {
- string path = ConvertKeyToPath(key);
- if (File.Exists(path))
- File.Delete(path);
- }
-
- public string CachePath
- {
- get;
- set;
- }
-
- private string ConvertKeyToPath(string key)
- {
- string file = key.Replace('/', '-');
- file += ".txt";
- return Path.Combine(CachePath, file);
- }
- }
-
- [Serializable]
- public class CacheItem
- {
- public DateTime ExpiryDate;
- public object Item;
-
- public CacheItem(object entry, DateTime utcExpiry)
- {
- Item = entry;
- ExpiryDate = utcExpiry;
- }
- }
Copiar código Existem dois lugares que precisam de atenção especial: No método Add, há um julgamento condicional que deve ser tratado dessa forma, caso contrário o mecanismo de cache irá armazenar o primeiro resultado em cache, e o cache expirará após a data de expiração e não será reconstruído. No programa de exemplo, simplesmente colocamos o cache no diretório cache, e na prática prática do projeto, considerando que as páginas em cache serão milhares, devemos fazer a classificação de diretórios, caso contrário encontrar e ler arquivos de cache se tornará um gargalo de eficiência, o que drenará a CPU.
3: Arquivo de configuração
Precisamos configurar no Web.config para que o handler de cache seja um FileCacheProvider personalizado, ou seja, adicionar um nó em FileCacheProvider:- <caching>
- <outputCache defaultProvider="FileCache">
- <providers>
- <add name="FileCache" type="MvcApplication2.Common.FileCacheProvider" cachePath="~/Cache" />
- </providers>
- </outputCache>
- </caching>
Copiar código
4: Uso do cache
Assumimos que usar isso sob controle do MVC (se quiser usá-lo em uma página de ASP.NET, inclua <% no page@OutputCache VaryByParam="none" Duration="10" %>), e você pode ver que o Index não está em cache de saída, enquanto o Index2 fica em cache por 10 segundos.
- public class HomeController : Controller
- {
- private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
- static string s_conn = "Data Source=192.168.0.77;Initial Catalog=luminjidb;User Id=sa;Password=sa;";
- public ActionResult Index()
- {
- using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
- {
- ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
- }
- return View();
- }
-
- [OutputCache(Duration = 10, VaryByParam = "none")]
- public ActionResult Index2()
- {
- using (DataSet ds = Common.SqlHelper.ExecuteDataset(s_conn, CommandType.Text, "select top 1* from NameTb a, DepTb b where a.DepID = b.ID ORDER BY NEWID()"))
- {
- ViewBag.Message = ds.Tables[0].Rows[0]["name"].ToString();
- }
- return View();
- }
- }
Copiar código 5: Verifique o efeito O código acima, após acessar o Index2, gerará um arquivo de cache na pasta Cache, da seguinte forma:
Agora, vamos avaliar a comparação de desempenho entre o cache de saída e o cache de saída, simulando 100 solicitações simultâneas de 100 usuários da seguinte forma:
|