系列導航地址http://www.companysz.com/fzrain/p/3490137.html
本文將使用一個開源框架CacheCow來實現針對Http請求資源緩存,本文主要介紹服務器端的緩存。
使用緩存技術可以很好的提高Web Api的性能,減小服務器的開銷。我們把這種緩存形式稱之為:條件化請求(Conditional Requests)。具體表現為:客戶端向服務器請求時會附加一個請求頭ETag,然后服務器會根據這個信息來決定是否需要把更新過的資源響應給客戶端,如果需要,則響應200狀態嗎以及資源內容,否則響應304狀態碼(Not modified)以及一個空的響應正文。
寫了好多,那么什么是ETag呢?ETag是服務器為特定資源生成的一個唯一標識(string類型)。你也可以理解為用來檢查服務器資源是否變化。
ETag分2種類型:強類型和弱類型。對于弱類型的ETag包含一個前綴W(例如:W/53fsfsd322),而強類型的ETag不包含任何前綴(例如:53fsfsd322)。通常來說,弱類型ETag代表緩存短時間資源(內存緩存),而強類型的ETag緩存是靠持久化的方式來實現的。
先上一張圖:
由上圖可知:在一開始,客戶端發起一個Http Get請求,請求的是id為4的course資源,由于這個資源是第一次被訪問,因此服務器在把資源返回的同時附加了一個響應頭(ETag)。
現在,客戶端發送Http Get請求想要再次請求相同的資源(Course id: 4),考慮到客戶端使用緩存,因此Get請求初始化的時候增加一個Header(If-None-Match),內容就是資源的ETag值。當服務器接受到請求的時候,就會讀取ETag的值并與服務器內的ETag值做比較,如果完全相同,服務器就會返回304狀態碼(Not modified)并且正文不含任何內容。這樣客戶端就知道資源是最新的。
對于Http Get和Delete請求,我們可以使用(If-None-Match)頭,但對于更新時我們要使用(If-Match)來匹配Put/Patch請求。請求到達的時候,服務器會校驗ETag值,如果不一樣,服務器就會響應一個412狀態碼(PRecondition Failed),因此客戶端就知道自己的版本不是最新的,在客戶端沒有獲取最新資源之前是不允許更新的。
經過了之前的一段簡單介紹,我們來實現所謂的“條件化請求”。
我們需要使用NuGet來安裝CacheCow,打開NuGet控制臺,輸入“Install-Package CacheCow.Server -Version 0.4.12”。會安裝2個dll:CacheCow.Server和CacheCow.Common。
配置CacheCow也是非常簡單的,我們所需要做的是創建一個Cache Handler并把它注入到web api的請求管道中。這個handler就是在請求到達和離開web api的時候檢查ETag和生成ETag的。
為了實現這一點,在“WebApiConfig.cs”里做如下配置:
//Configure HTTP Caching using Entity Tags (ETags)var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();config.MessageHandlers.Add(cacheCowCacheHandler);
到目前為止,我們的web api已經具有使用本機內存實現緩存的功能了,這也是CacheCow默認的配置,在單機狀態(只有一臺服務器)的時候可謂是比較完美了。然而,當應用程序走向分布式的時候就出現為題了——由于不同的web服務器需要共享緩存狀態,因此我們需要把緩存狀態持久化到一個單獨的地方(SQL Server, MongoDB, MemCache)。但是在實現持久化之前我們先測試一下內存緩存。
打開我們的測試客戶端PostMan(Chrome插件),發送Get請求到:http://localhost:{your_port}/api/courses/4
結果:
請求注釋:
1.響應Http狀態碼是200,意昧著服務器把資源一起響應過來了。
2.這次響應增加了2條頭信息:ETag和Last-Modified,目前我們只需關心ETag的值,因為下次請求會用到。
3.ETag的類型是弱類型的(帶有W前綴),說明這個緩存存在于服務器的內存中,如果重啟IIS或切斷服務進程的話,緩存就會失效。
對于接受到ETag值的客戶端,在下次請求相同資源的時候就需要附加一個“If-None-Match”的請求頭,服務器就會比較客戶端與自己內存中的ETag值,如果相同,返回304(Not modified),不相同則返回200加上資源內容。
測試:我們再次請求這個資源
結果:
對于這次請求來說:
1.http狀態碼是304,意味著客戶端的資源是最新的,因此響應body是空的
2.客戶端得到相同的ETag值
在SQL Server中做緩存同樣很簡單,首先我們要確定在哪個持久化介質中實現緩存,我們用的是SQL Server,因此打開NuGet控制臺,輸入如下命令:Install-Package CacheCow.Server.EntityTagStore.SqlServer -Version 0.4.11。
然后在“WebApiConfig”做如下配置:
//Configure HTTP Caching using Entity Tags (ETags)var connString = System.Configuration.ConfigurationManager.ConnectionStrings["eLearningConnection"].ConnectionString;var eTagStore = new CacheCow.Server.EntityTagStore.SqlServer.SqlServerEntityTagStore(connString);var cacheCowCacheHandler = new CacheCow.Server.CachingHandler(eTagStore);cacheCowCacheHandler.AddLastModifiedHeader = false;config.MessageHandlers.Add(cacheCowCacheHandler);
上面的實現很明顯,CacheCow需要把緩存信息存到數據庫中,因此我們需要制定我們api所用的數據庫。然后把eTagStore實例賦給Cache handler。
如果你現在直接請求api的話,等待你的不是資源而是一個500錯誤碼。這是因為之前我們介紹到CacheCow需要把緩存信息存入數據庫,那么數據庫中就應該有一張對應的表以及操作這張表的存儲過程,因此我們需要執行一個sql腳本。這個腳本通常在“{projectpath}/packages/CacheCow.Server.EntityTagStore.SqlServer.0.4.11/script”
執行完這個腳本后,你會發現數據庫多了一張表以及5個存儲過程:
ok,可以測試了,還是剛剛的例子:
正如上圖所示,ETag的值不再是弱類型的了,因此我們存到SQL Server中了,打開SQL Server中的CacheState表,你會發現:
現在,只要沒有客戶端來更新這個資源,之前訪問過這個資源的客戶端統統會得到304狀態碼以及空的body(前提是客戶端的請求中包含ETag值,呵呵)。
現在我們實現一下更新,客戶端要更新資源就需要包含一個“If-Match”的請求頭,如下圖所示:
結果:
ETag已經改變:
我們使用老的ETag再次請求服務器:
所以結果:
這個響應告訴客戶端:”你手里的資源不是最新的,先拿到最新的資源我才讓你修改”。
拖了好久的最后一篇終于和大家見面了,主要都是一些理論,代碼也就5行,不過感覺ETag還真的挺強大的。
本次系列到這里也要告一段落了,不過之后還打算介紹一些有其他內容(包括Web Api 2的新特性IHttpActionResult,CORS的支持以及OData的支持等),敬請期待。。。
源碼地址:https://github.com/fzrain/WebApi.eLearning
新聞熱點
疑難解答