在大型網站應用上,Cache的使用絕對是讓人又愛又恨,若是用的好,可以讓網站的Performance大大提升,但若不謹慎使用Cache的話,最後可能會發現自己的網站怎麼更新都是舊資料。因此Cache的使用絕對必須審慎拿捏,用在關鍵點,而且一但用了Cache一定要有對應的資料更新機制,這樣才能讓Cache發揮最大的功效。
※新增Cache Role
在之前的文章中,我們已經新增了Web Role用來發行網站,而今天我們要在同樣的專案中新增一個Cache Role用來提供我們的Cache服務。
※替網站加入Cache
接下來我們將實作一個模擬長時間執行的資料查詢,並且替這個服務加上Cache
使用Nuget加入Azure cache的Library
修改config,將[cache cluster role name]改為我們剛剛新增的Role名稱
<dataCacheClients>
<dataCacheClient name="default">
<!--To use the in-role flavor of Windows Azure Caching, set identifier to be the cache cluster role name -->
<!--To use the Windows Azure Caching Service, set identifier to be the endpoint of the cache cluster -->
<autoDiscover isEnabled="true" identifier="ApiSample.Cache" />
<!--<localCache isEnabled="true" sync="TimeoutBased" objectCount="100000" ttlValue="300" />-->
<!--Use this section to specify security settings for connecting to your cache. This section is not required if your cache is hosted on a role that is a part of your cloud service. -->
<!--<securityProperties mode="Message" sslEnabled="false">
<messageSecurity authorizationInfo="[Authentication Key]" />
</securityProperties>-->
</dataCacheClient>
</dataCacheClients>
撰寫一個模擬長時間查詢的服務
public interface ILongTimeService
{
string GetLongTimeData();
}
public class LongTimeService : ILongTimeService
{
public string GetLongTimeData()
{
System.Threading.Thread.Sleep(10 * 1000);
return DateTime.Now.ToString();
}
}
增加一個Controller提供資料,並且加上Cache,同時我們加上StopWatch觀察查詢資料需要的時間
public class SampleController : Controller
{
public SampleController(ISampleService sampleService, ILongTimeService longTimeService)
{
this.SampleService = sampleService;
this.LongTimeService = longTimeService;
}
public ActionResult GetLongTimeData()
{
//// Calculate execute time
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
//// Emulate long time processing data
DataCacheFactory cacheFactory = new DataCacheFactory();
DataCache cache = cacheFactory.GetDefaultCache();
object result = cache.Get("longTimeData");
if (result == null)
{
result = this.LongTimeService.GetLongTimeData();
cache.Add("longTimeData", result);
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
return Json(
new
{
Result = result,
ExecuteTime = elapsedTime
}, JsonRequestBehavior.AllowGet);
}
}
其實cacheFactory.GetDefaultCache();代表取得default這組cache,也可以使用cacheFactory.GetCache("cache name");取得其它Cache資料。
測試成功!我們的Cache服務生效了,大家還可以依據自己的需求進一步的調整Cache的時間長短。
延伸閱讀:
* How to cache service
※使用AOP實作CacheInterceptor
除了直接在程式碼直接撰寫Cache的讀取程式碼之外,其實我們也可以將Cache抽出作為Interceptor,這麼一來我們只要在需要的Class上加上Attribute就可以完成Cache的新增,並且讓Cache統一並分離於商業邏輯之外,也增加了重複使用的容易性!
在Extensions專案新增CacheInterceptor
public class CacheInterceptor : IInterceptor
{
private string section;
public CacheInterceptor()
: this(string.Empty)
{
}
public CacheInterceptor(string section)
{
this.section = section;
}
public void Intercept(IInvocation invocation)
{
DataCacheFactory cacheFactory = new DataCacheFactory();
//// Get cache by section
DataCache cache;
if (string.IsNullOrWhiteSpace(this.section))
{
cache = cacheFactory.GetDefaultCache();
}
else
{
cache = cacheFactory.GetCache(this.section);
}
//// Get cache or set by proceed method
var typeName = invocation.TargetType.FullName;
var methodName = invocation.Method.Name;
var cacheKey = string.Format("{0}-{1}", typeName, methodName);
var result = cache.Get(cacheKey);
if (result == null)
{
invocation.Proceed();
result = invocation.ReturnValue;
cache.Add(cacheKey, result);
}
else
{
invocation.ReturnValue = result;
}
}
}
修改BL的ServiceModule,讓LongTimeService使用CacheInterceptor
public class ServiceModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
var service = Assembly.Load("ApiSample.BL.Services");
builder.RegisterAssemblyTypes(service)
.AsImplementedInterfaces()
.EnableInterfaceInterceptors();
builder.RegisterType<ProductService>()
.As<IProductService>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(LogInterceptor), typeof(AuthInterceptor));
builder.RegisterType<LongTimeService>()
.As<ILongTimeService>()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(CacheInterceptor));
}
}
修改Controller,將原本手動新增的Cache程式碼移除
public ActionResult GetLongTimeData()
{
//// Calculate execute time
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var result = this.LongTimeService.GetLongTimeData();
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
ts.Hours, ts.Minutes, ts.Seconds,
ts.Milliseconds / 10);
return Json(
new
{
Result = result,
ExecuteTime = elapsedTime
}, JsonRequestBehavior.AllowGet);
}
發行上雲端,測試一切正常,如此一來不但讓我們的程式碼更加的乾淨,也讓要增加Cache變成一件輕鬆的事情囉!
※本日小結
透過Cache的幫助,我們可以讓需要花費大量資源才能產生資料的服務成本降低,也讓網站整體的效能變好,但還是必須謹慎的使用以避免Cache過於繁雜而難於管理,Azure提供的分散式Cache服務也讓Cloud Service可以共用Cache服務,更甚至透過多個Cache Instance來實現高可用性,相當的方便,大家可以多加利用!關於今天的內容,歡迎大家一起討論喔^_^