[WEB API]關於快取那件事,使用Strathweb.CacheOutput
前言
其實要提升網站回應速度,有很多的環節,包括了db的選擇和硬體的配置,加上程式碼的寫法 etc...,但快取機制是非常重要的,此篇是針對只有一台ap的快取做法,如果有需要使用到load blanace的時候,就得使用其他做法了,或許redis會是您的好朋友,但是如果我們快取的機制並不到非常複雜,我們只希望程式碼簡單點,這種方式就非常的簡單和快速,這邊想要簡單記錄一下如何快速的實做web api的cache機制。
安裝Strathweb.CacheOutput
首先一樣使用nuget安裝吧,有興趣的人也可以直接去github看(https://github.com/filipw/Strathweb.CacheOutput)
開始使用web api cache
這裡使用之前oracle的例子來示例,程式碼如下
public class OracleController : ApiController
{
public IDbConnection GetConnection //回傳OracleConnection
{
get
{
string connString = "Data source=localhost/book;User id=C##ANSON;Password=7154;";
return new OracleConnection(connString);
}
}
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
public async Task<IHttpActionResult> Get(int Id)
{
using (var con = GetConnection)
{
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(await companyResult);
}
}
}
然後先測試一下swagger的部份,預設的狀況下,應該都可以看到是回傳200的狀態,不管我們重覆呼叫幾次
web api cache有分client cache和server cache,差異在於如果是client的話,想當然的就是儲存在使用者的電腦啦,這樣的好處是使用者在發送request的時候,不會再訪問到web api,當然就減輕了iis的壓力
[CacheOutput(ClientTimeSpan =5)]//這邊指定的是秒數
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
從圖示可以明顯看到,除了第一個請求之外,其餘都是(from disk cache),但是client cache有幾個缺點,第一個是如果使用者把瀏覽器快取給強制取消掉,這招就行不通了,所以其實我們也可以使用server cache
[CacheOutput(ServerTimeSpan = 5)] //這邊指定的是秒數
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
從圖示可看到,除了第一個請求回應200之外,其餘都是304代表快取的意思,但是如果使用server cache的話,雖然能強迫快取,不會訪問到db,但是使用者訪問一樣會造成iis的負擔,所以實務上我們會選擇兩種方式都快取,這樣子首先快取的會是使用者的電腦,如果使用者把瀏覽器的快取清掉了,或者關閉預設快取,至少也只會訪問到iis而不會直接到db。
[CacheOutput(ServerTimeSpan = 5,ClientTimeSpan =5)] //這邊指定的是秒數
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
但是其實他提供了更多的方式
//下面快取請擇其一,會全部列出來只是為了方便
[CacheOutput(ServerTimeSpan =5,ClientTimeSpan =5)]
[CacheOutputUntil(2017, 05, 25, 17, 00)] //快取直到2017/5/25 17:00
[CacheOutputUntilToday(23, 00)]//直到今天23點
[CacheOutputUntilThisMonth(28)]
[CacheOutputUntilThisYear(7, 31)]
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
當然我們也可以使用整個web api層級的快取,而不是只針對一個action
[CacheOutput(ServerTimeSpan = 5, ClientTimeSpan = 5)]
public class OracleController : ApiController
{
public IDbConnection GetConnection //回傳OracleConnection
{
get
{
string connString = "Data source=localhost/book;User id=C##ANSON;Password=7154;";
return new OracleConnection(connString);
}
}
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
public async Task<IHttpActionResult> Get(int Id)
{
using (var con = GetConnection)
{
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(await companyResult);
}
}
}
可以從圖中看到不管我是直接call Get或有傳入ID的Get都會快取,如果我要忽略有傳入ID的,只要在該action加上IgnoreCacheOutput
[IgnoreCacheOutput]
public async Task<IHttpActionResult> Get(int Id)
{
using (var con = GetConnection)
{
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(await companyResult);
}
}
}
接下來說明一下,其實web api cache會針對有參數的做快取,比如說我們取第一筆id的時候,他會針對傳入id為1的資料做快取,當我們傳2的時候會重取,然後針對2的部份也做一個快取。
[CacheOutput(ServerTimeSpan = 50,ClientTimeSpan =50)]
public async Task<IHttpActionResult> GetById(int Id)
{
using (var con = GetConnection)
{
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(await companyResult);
}
}
然後此package還有提供了,如果我們觸發了某個action的時候,可以把預設的快取清掉,這種例子可以做在如果我們新增某筆資料的時候,我們希望取資料的時候,能即時的取到最新資料庫裡的資料,就可以這樣子做。
public class OracleController : ApiController
{
public IDbConnection GetConnection //回傳OracleConnection
{
get
{
string connString = "Data source=localhost/book;User id=C##ANSON;Password=7154;";
return new OracleConnection(connString);
}
}
[CacheOutput(ServerTimeSpan = 50,ClientTimeSpan =50)]
public async Task<IHttpActionResult> Get() //取得資料
{
using (var con = GetConnection)
{
var dao = con.QueryAsync<EmployeeDto>("SELECT id,name,address,phone_number as phoneNumber FROM EMPLOYEE");
var companyResult = con.QueryAsync<CompanyDto>("SELECT id,companyName FROM company");
return Ok(new { Employee = await dao, Company = await companyResult });
}
}
[InvalidateCacheOutput("Get")] //當觸發這個action時,會清除掉Get的快取
public void Post(EmployeeDto employeeDto)
{
//add something
}
}
結論
其實這個pakcage提供了我們很多案例,所以如果我們沒有很複雜的快取條件或load blanace的狀況下,使用此package已經很夠用了,如果有興趣的讀者也可以去github看更詳細和進階的用法,如果有任何更好的建議,再請不吝告知。