iT邦幫忙

DAY 24
7

使用Asp.Net MVC打造Web Api系列 第 24

使用Asp.Net MVC打造Web Api (24) - 使用Azure Cache將資料進行Cache

在大型網站應用上,Cache的使用絕對是讓人又愛又恨,若是用的好,可以讓網站的Performance大大提升,但若不謹慎使用Cache的話,最後可能會發現自己的網站怎麼更新都是舊資料。因此Cache的使用絕對必須審慎拿捏,用在關鍵點,而且一但用了Cache一定要有對應的資料更新機制,這樣才能讓Cache發揮最大的功效。
※新增Cache Role
在之前的文章中,我們已經新增了Web Role用來發行網站,而今天我們要在同樣的專案中新增一個Cache Role用來提供我們的Cache服務。

  1. 在Deploy專案的角色點擊滑鼠右鍵,新增背景工作角色專案
  2. 選擇新增快取角色服務
  3. 將網站發行至雲端,就可以看到快取服務的實體已經正常運作中,進入設定頁面可以看到有基本的設定內容

※替網站加入Cache
接下來我們將實作一個模擬長時間執行的資料查詢,並且替這個服務加上Cache

  1. 使用Nuget加入Azure cache的Library

  2. 修改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>    
    
  3. 撰寫一個模擬長時間查詢的服務

        public interface ILongTimeService
        {
            string GetLongTimeData();
        }
    
        public class LongTimeService : ILongTimeService
        {
            public string GetLongTimeData()
            {
                System.Threading.Thread.Sleep(10 * 1000);
    
                return DateTime.Now.ToString();
            }
        }
    
  4. 增加一個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資料。

  1. 部署程式到雲端,準備開始測試,由於是長時間查詢,所以第一次的執行時間應該會比較久,我們連結至/Sample/GetLongTimeData測試
  2. 再次執行查詢,由於第一次查詢之後結果應該會儲存在Cache之中,所以查詢時間應該較短!

測試成功!我們的Cache服務生效了,大家還可以依據自己的需求進一步的調整Cache的時間長短。

延伸閱讀:
* How to cache service

※使用AOP實作CacheInterceptor
除了直接在程式碼直接撰寫Cache的讀取程式碼之外,其實我們也可以將Cache抽出作為Interceptor,這麼一來我們只要在需要的Class上加上Attribute就可以完成Cache的新增,並且讓Cache統一並分離於商業邏輯之外,也增加了重複使用的容易性!

  1. 在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;
                }
            }
        }
    
  2. 修改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));
            }
        }
    
  3. 修改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);
        }
    
  4. 發行上雲端,測試一切正常,如此一來不但讓我們的程式碼更加的乾淨,也讓要增加Cache變成一件輕鬆的事情囉!

※本日小結
透過Cache的幫助,我們可以讓需要花費大量資源才能產生資料的服務成本降低,也讓網站整體的效能變好,但還是必須謹慎的使用以避免Cache過於繁雜而難於管理,Azure提供的分散式Cache服務也讓Cloud Service可以共用Cache服務,更甚至透過多個Cache Instance來實現高可用性,相當的方便,大家可以多加利用!關於今天的內容,歡迎大家一起討論喔^_^


上一篇
使用Asp.Net MVC打造Web Api (23) - 建置Stage環境及組態轉換
下一篇
使用Asp.Net MVC打造Web Api (25) - 使用Azure Storage儲存圖片
系列文
使用Asp.Net MVC打造Web Api30

1 則留言

0

我要留言

立即登入留言