Skip to content

Files

Latest commit

 

History

History
234 lines (161 loc) · 8.93 KB

Caching.md

File metadata and controls

234 lines (161 loc) · 8.93 KB

缓存

客户端缓存

这份教程兼容hapi v11.x.x版本

HTTP协议提供几种不同的HTTP头来控制浏览器怎么缓存资源。通过查看Google开发者指南或者其他资料来选择合适你场景的一种。本教程仅提供预览怎么使用这些技术在hapi中。

缓存控制

缓存控制头告诉浏览器和其他中间缓存资源是否可缓存以及缓存多久时间。例如,Cache-Control:max-age=30, must-revalidate, private表示浏览器可以缓存相关资源30秒,private意味着不能被中间缓存资源缓存,只能被浏览器缓存。must-revalidate表示有效期过期之后必须要再次从服务器请求资源。

来让我们看看在hapi中怎么设置这些头:

server.route({
    path: '/hapi/{ttl?}',
    method: 'GET',
    handler: function (request, reply) {

        const response = reply({ be: 'hapi' });
        if (request.params.ttl) {
            response.ttl(request.params.ttl);
        }
    },
    config: {
        cache: {
            expiresIn: 30 * 1000,
            privacy: 'private'
        }
    }
});

实例一 设置Cache-Control

示例一已经举例说明,可以通过response对象提供的ttl(msec)进行重写expiresIn属性。

查看route-options获取更多信息关于通用缓存配置选项。

最后修改Last-Modified

在某些场景中服务器能提供信息,当资源被最后修改后。当使用inert插件来分发静态内容,在每次反馈载体中都会添加Last-Modified头。

当response中设置了Last-Modified的头,hapi会和客户端传过来的If-Modified-Since头进行对比,从而确定是否应当返回状态码304(远程资源未修改)。

假设lastModified是一个Date对象,你可以设置头通过response对象接口,从下边的实例二可见。

   reply(result).header('Last-Modified', lastModified.toUTCString());

实例二 设置 Last-Modified header

在本教程的最后一个章节有更多的使用Last-Modified的例子。

ETag

ETag是Last-Modified的替代,服务器提供一个token(通常是资源的校验和)而不是最后修改的时间戳。浏览器会在下一次请求的时候使用这个token设置if-None-Match头。服务器把header的值和新的ETag校验和进行比较,如果一样的话response会返回一个304状态码。

你仅仅需要在hadler中通过etag(tag, options)函数设置ETag。

   reply(result).etag('xxxxxxxxx');

实例三 设置ETag头

查看文档了解更多response对象下关于etag更多参数和可用配置的详细信息。

服务器端缓存

hapi提供强大的服务器端缓存,通过catbox来很方便的使用缓存。本教程章节将会帮助你理解catbox是怎样被使用到hapi中的和怎样才能使用它。

Catbox 有两个接口:clientpolicy

Client

Client是一个低版本的接口允许你设置/获取键值对。可以使用以下可用适配器来初始化:(Memory, Redis, mongoDB, Memcached, 或者 Riak)。

Client is a low-level interface that allows you set/get key-value pairs. It is initialized with one of the available adapters: (Memory, Redis, mongoDB, Memcached, or Riak).

hapi always initialize one default client with memory adapter. Let's see how we can define more clients.

'use strict';

const Hapi = require('hapi');

const server = new Hapi.Server({
    cache: [
        {
            name: 'mongoCache',
            engine: require('catbox-mongodb'),
            host: '127.0.0.1',
            partition: 'cache'
        },
        {
            name: 'redisCache',
            engine: require('catbox-redis'),
            host: '127.0.0.1',
            partition: 'cache'
        }
    ]
});

server.connection({
    port: 8000
});

实例四声明catbox客服端

在实例四种,我们声明了两个catbox客户端:mongoCacheredisCache。包括hapi创建的默认内存缓存,共有三种可用的缓存客户端。在注册一个新的缓存客户端的时候可以通过删除name属性来修改默认的客户端。partition告诉适配器缓存被怎么命名('catbox'是默认值)。当使用mongoDB的时候是数据库的名词,当使用redis的时候是使用的的键前缀。

Policy

Policy是一个比Client更高版本的接口。假设我们正在处理一些比添加两个数字更加复杂的事情,并且我们希望缓存这些结果。server.cache创建了一个新的policy,稍后会被使用到路由的handler中。

const add = function (a, b, next) {

    return next(null, Number(a) + Number(b));
};

const sumCache = server.cache({
    cache: 'mongoCache',
    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);
        });
    }
});

实例五 使用 server.cache

cache告诉hapi那个客户端将会被使用到。

sumCache.get函数的第一个参数是一个带有强制id键的对象,是一个唯一标识符字符串。

为了分区,segment允许你分割你统一客户端的缓存。如果想要缓存结果通过不同的方法,通常不希望将结果混合在一起。在mongoDB适配器中,segment表现为一个collection;在redis中是在partition选项前加一个前缀。

server.cache在插件中调用时segment的默认值将会是!pluginName。当创建server methods的时候,segment的值将会是#methodName。如果你使用多个政策(policies)共享同一个segment,有一个shared配置参数可以使用。

Server methods

但它可以得到比这更好的效果!(But it can get better than that!)在95%的情景下你都将使用server methods来缓存,因为这样减少引用到最低。让我们使用server methods来编写实例五:

const add = function (a, b, next) {

    return next(null, Number(a) + Number(b));
};

server.method('sum', add, {
    cache: {
        cache: 'mongoCache',
        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);
        });
    }
});

实例六 通过server methods使用缓存

server.method()自动为我们创建一个新的policy通过segment: '#sum'。通常唯一的对象id(缓存键)被自动的产生从参数中。对于更复杂的参数来说,你应当提供自己的generateKey函数依据参数创建唯一的id。查看server methods教程获取更多信息。

接下来是什么?

  • 浏览catbox policy 选项,并且额外注意staleIn,staleIngenerateTimeout,利用这些挖掘catbox缓存的所有潜在能力。

  • 查看更多的server methods教程实例。

Client and Server caching

Catbox Policy 提供了两个参数cachedreport来提供一些详情当一个对象被缓存。

在这种情景下我们使用 cached.stored 邮戳来设置 last-Modified header。

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());
        });
    }
});

实例七服务器和客户端缓存一起使用