hapi - caching

hapi Jul 16, 2016

HAPI 内置了緩存系統。緩存也分很多種。爲什麽用緩存。如果資料不需要實時更新,其實它可以緩存到内存、或者其它儲存設備。

例如電視節目表,可以緩存幾小時或者長達一天的資料,不需要每次訪問都詢問資料庫。

當然,速度來講非 redis、memcache 莫屬,内存目前還是最快的媒體,所以首推這兩款。詳細請看 catbox 的 API 文檔

TOC

  • 游覽器緩存
  • 伺服器緩存
  • 游覽器 + 伺服器

偽代碼之談

手動寫 cache 就是大概這樣……譬如用 redis。接下來的是偽代碼:

  1. redis.get('key:xxxx')
  2. 找到 key
  • return res(...)
  1. 沒找到 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 的存活時間。單位:ms
  • generateTimeout: 給伺服器多長時間生成這個緩存。單位:ms
    • 如果你需要下載遠程東西,或者 db 搞很長時間,考慮提高這個數值到三十秒或者一分鐘。否則 hapi 返回 504 timeout。

上面的例子來説,Hapi 自動創建 segment: '#sum' 這個 cache key。如果有參數,參數也會附加到這個 cache key。自動創建支持 String,Number,Boolean 這三個類型。其它類型的需要你自己寫 generateKey 生成自己的 cache key。

客戶端和服務器緩存

繼續上面的例子。如果調用 server.methods,提供了額外兩個參數 cachedreport

(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),重複無聊的緩存代碼是不需要自己寫,有用的功能都内置了,配置好就可以用了,花時間處理其它事吧。

via

擴展閲讀

Wai Tsang

💥8➕26.