HAPI 内置了緩存系統。緩存也分很多種。爲什麽用緩存。如果資料不需要實時更新,其實它可以緩存到内存、或者其它儲存設備。
例如電視節目表,可以緩存幾小時或者長達一天的資料,不需要每次訪問都詢問資料庫。
當然,速度來講非 redis、memcache 莫屬,内存目前還是最快的媒體,所以首推這兩款。詳細請看 catbox 的 API 文檔。
偽代碼#
手動寫 cache 就是大概這樣……譬如用 redis。接下來的是偽代碼:
const key = redis.get('key:xxxx')
- 找到 key,
return res(...)
- 沒找到 key
redis.set('key:xxxx') redis.expire('key:xxxx') // 不會緩存幾年吧 return res(...)
當然有 error 錯誤也好處理一下。
可以讀讀,這篇文章 Caching a MongoDB Database with Redis
客戶端緩存#
這個很明顯,客戶端單方面做簡單的緩存。伺服器加了 Cache-Control 這個 header。
HTTP Header#
server.route({
path: "/hapi",
method: "GET",
handler: function (request, reply) {
reply({ be: "hapi" });
},
config: {
cache: {
expiresIn: 30 * 1000, // 30 seconds
privacy: "private",
},
},
});
相等于:Cache-Control:max-age=30, private
除了用 expiresIn
之外,也可以用 expiresAt
:每天固定时间过期( HH:MM
格式)。
Last-Modified#
reply(result).header("Last-Modified", lastModified.toUTCString());
游覽器的 request 順便帶著 If-Modified-Since
這個 HTTP header,伺服器收到請求后比較時間,再作出回應是否 304.
Etag#
Etag 其實跟 Last-Modified 差不多~~,不是用時間來對比而已~~ 。Etag 需要校驗(checksum)計算,有點浪費資源。不推薦這方法。
reply(result).etag("xxxxxxxxx");
伺服器緩存#
配置緩存政策,暫且叫做 redisCache。
// server.js
const Hapi = require("hapi");
const server = new Hapi.Server({
cache: [
{
name: "redisCache",
engine: require("catbox-redis"),
host: "127.0.0.1",
partition: "cache",
},
],
});
server.connection({
port: 8000,
});
配置 cache 時,如果沒寫用什麽 engine,catbox 就會用 catbox-memory。默認限制 100MB。用來本地測試、dev 伺服器就算了,production 還是用 redis 或者 memcache。
// route.js
const add = function (a, b, next) {
return next(null, Number(a) + Number(b));
};
const sumCache = server.cache({
cache: "redisCache",
expiresIn: 20 * 1000,
segment: "customSegment",
generateFunc: function (id, next) {
add(id.a, id.b, next);
},
generateTimeout: 100,
});
server.route({
path: "/add/{a}/{b}",
method: "GET",
handler: function (request, reply) {
const id = request.params.a + ":" + request.params.b;
sumCache.get(
{ id: id, a: request.params.a, b: request.params.b },
(err, result) => {
if (err) {
return reply(err);
}
reply(result);
}
);
},
});
sumCache.get(xxx)
嘗試讀取這個緩存。如果沒有緩存,generateFunc
也提供了,它就會自動生成新的緩存。
segment
是命名空間。用來隔離緩存的 cache key,不會搞亂其它資料。
- 如果在插件裏面調用這個 cache。Segment 值就是
!pluginName
。 - Server Method 調用則是:
#methodName
。
Server method#
Server method 是 HAPI 很重要的 API。用來分享函數。
const add = function (a, b, next) {
return next(null, Number(a) + Number(b));
};
server.method("sum", add, {
cache: {
cache: "redisCache",
expiresIn: 30 * 1000,
generateTimeout: 100,
},
});
server.route({
path: "/add/{a}/{b}",
method: "GET",
handler: function (request, reply) {
server.methods.sum(request.params.a, request.params.b, (err, result) => {
if (err) {
return reply(err);
}
reply(result);
});
},
});
expiresIn
:cache 的存活時間。單位:msgenerateTimeout
: 給伺服器多長時間生成這個緩存。單位:ms- 如果你需要下載遠程東西,或者 db 搞很長時間,考慮提高這個數值到三十秒或者一分鐘。否則 hapi 返回 504 timeout。
上面的例子來説,Hapi 自動創建 segment: '#sum'
這個 cache key。如果有參數,參數也會附加到這個 cache key。自動創建支持 String,Number,Boolean 這三個類型。其它類型的需要你自己寫 generateKey 生成自己的 cache key。
客戶端和服務器緩存#
繼續上面的例子。如果調用 server.methods,提供了額外兩個參數 cached
和 report
。
(err, result, cached, report) => xxx
// some-route.js
server.route({
path: "/add/{a}/{b}",
method: "GET",
handler: function (request, reply) {
server.methods.sum(
request.params.a,
request.params.b,
(err, result, cached, report) => {
if (err) {
return reply(err);
}
const lastModified = cached ? new Date(cached.stored) : new Date();
return reply(result).header(
"last-modified",
lastModified.toUTCString()
);
}
);
},
});
cached.stored
是時間戳,這樣就可以在 reply 添加 last-modified
這個 header。
HAPI 緩存是智能的,錯誤當然是不會保存的。
題外話
HAPI 是配置形的伺服器(config-based),重複無聊的緩存代碼是不需要自己寫,有用的功能都内置了,配置好就可以用了,花時間處理其它事吧。