缓存过滤器

本示例将展示如何使用 Envoy 的缓存过滤器处理 HTTP 缓存。 沙盒环境的安装需要基于 :ref: 前端代理沙盒 <install_sandboxes_front_proxy> 的设置。

所有传入请求都通过前端 Envoy 进行路由,该前端 Envoy 充当位于 envoymesh 网络边缘的反向代理。 docker compose 暴露两个端口 80008001 来分别处理服务的 HTTP 调用和对 /admin 的请求。(请参阅 :repo: /examples/cache/docker-compose.yaml)。 前端 Envoy 的后面部署了两个后端服务,每个后端服务都有一个 sidecar Envoy。

前端 Envoy 配置为运行缓存过滤器,该过滤器将可缓存的响应存储在内存缓存中,并将其提供给后续请求。 示例中,由部署的服务提供的响应信息已配置在 :repo: /examples/cache/responses.yaml 文件中。 该文件已安装到两个服务所在的容器中,因此在服务运行时对存储的响应信息所做的任何更改都将立即生效(无需重新构建项目或重启)。

为了演示的目的,响应的创建时间已经提前配置在它的响应体中。为了验证的目的,将为每个响应计算一个 Etag,该 Etag 仅取决于 yaml 文件中的响应体(即,不考虑附加时间)。 通过带有 age 的头信息来识别缓存的响应。比较生成时间是否早于响应头中包含的 date,可验证响应信息的有效性;一个通过验证的响应信息,它头信息中的 date 会更新,而响应体不会改变。 通过验证的响应头信息中不包含 age 信息。来自后端服务的响应头中不包含 age 信息,其头信息中的 date 信息与响应生成的时间一致。

运行沙盒环境

以下文档说明了上述对于 Envoy 容器编译环境进行设置的过程。

步骤 1:安装 Docker

确保你已安装较新版本的 dockerdocker-compose

一个最简单的方法来完成此事就是使用 Docker Desktop

步骤 2:克隆 Envoy 仓库

如果你还没有克隆 Envoy 仓库,用如下方式进行克隆:

git clone [email protected]:envoyproxy/envoy
git clone https://github.com/envoyproxy/envoy.git

第三步,启动我们的所有容器

$ pwd
envoy/examples/cache
$ docker-compose build --pull
$ docker-compose up -d
$ docker-compose ps

       名称                      命令                 状态                           端口
------------------------------------------------------------------------------------------------------------------------
cache_front-envoy_1   /docker-entrypoint.sh /bin ... Up      10000/tcp, 0.0.0.0:8000->8000/tcp, 0.0.0.0:8001->8001/tcp
cache_service1_1      /bin/sh -c /usr/local/bin/ ... Up      10000/tcp, 8000/tcp
cache_service2_1      /bin/sh -c /usr/local/bin/ ... Up      10000/tcp, 8000/tcp

第四步,测试 Envoy 的 HTTP 缓存能力

现在你可以通过 front-envoy 向后端发送一个请求。需注意,两个服务是不同的请求路由,相同的请求发送到不同服务时产生的缓存实体是不同的。(即,一个请求发送到服务 2 产生的缓存响应信息与发送到服务 1 所产生的缓存响应信息不是同一个对象)。

发送一个请求:

curl -i localhost:8000/service/<service_no>/<response>

service_no:请求发送到服务 1 或 2。

response:请求后的响应。 响应信息可在 /examples/cache/responses.yaml 文件中查看。

提供的示例响应信息如下:

  • 一分钟有效

    响应缓存仅保持一分钟。之后,响应将由后端服务验证后再从缓存中提供。如果缓存被更新,则返回新的响应(及缓存)。 否则,将缓存并响应缓存的响应。

  • 私有

    这种响应模式为私有的;响应不能被存储在共享缓存中(例如:代理中)。响应永远都需要由后端服务返回。

  • 无缓存

    这种模式的响应每次在返回前都需要被验证。

在运行沙盒测试的过程中,你可以改变响应头信息及响应体(或者新增一个响应)。

响应示例

1. 一分钟有效
$ curl -i localhost:8000/service/1/valid-for-minute
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 103
cache-control: max-age=60
custom-header: any value
etag: "172ae25df822c3299cf2248694b4ce23"
date: Fri, 11 Sep 2020 03:20:40 GMT
server: envoy
x-envoy-upstream-service-time: 11

该响应将在缓存中保持一分钟
响应体生成时间为:Fri, 11 Sep 2020 03:20:40 GMT

当然,该响应头信息中的 date 信息与生成时间是一致的。 30 秒后发送相同的请求会得到与之前相同的一个响应,且该响应的生成时间也是相同的。 不同的是,这个来自缓存中的响应,它的响应头中会带一个 age 信息:

$ curl -i localhost:8000/service/1/valid-for-minute
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 103
cache-control: max-age=60
custom-header: any value
etag: "172ae25df822c3299cf2248694b4ce23"
date: Fri, 11 Sep 2020 03:20:40 GMT
server: envoy
x-envoy-upstream-service-time: 11
age: 30

该响应将在缓存中保持一分钟
响应体生成时间为:Fri, 11 Sep 2020 03:20:40 GMT

一分零一秒后:

$ curl -i localhost:8000/service/1/valid-for-minute
HTTP/1.1 200 OK
cache-control: max-age=60
custom-header: any value
etag: "172ae25df822c3299cf2248694b4ce23"
date: Fri, 11 Sep 2020 03:21:41 GMT
server: envoy
x-envoy-upstream-service-time: 8
content-length: 103
content-type: text/html; charset=utf-8

该响应将在缓存中保持一分钟
响应体生成时间为:Fri, 11 Sep 2020 03:20:40 GMT

在后端服务验证过后会返回一个相同的响应。 你可以发现这个返回的响应的生成时间与之前的是一样的,但是响应头中的 date 已被更新为验证响应的时间。 同时,在响应头中不包含 age 信息。

这种模式的响应每次被验证后,响应信息都会在缓存中保持一分钟。 如果缓存中的响应依然处于有效期,即使响应体发生了变化,Envoy 依然会返回缓存中的响应信息。缓存中的响应信息仅当缓存失效后才会被更新。

2. 私有
$ curl -i localhost:8000/service/1/private
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 117
cache-control: private
etag: "6bd80b59b2722606abf2b8d83ed2126d"
date: Fri, 11 Sep 2020 03:22:28 GMT
server: envoy
x-envoy-upstream-service-time: 7

这是个私有的响应,它不会被缓存在 Envoy 中。
响应体生成时间为:Fri, 11 Sep 2020 03:22:28 GMT

无论你请求多少次,你每次都会得到一个新的响应信息;新的生成时间、新的 date 头信息,且响应头中不存在 age 信息。

3. 无缓存
$ curl -i localhost:8000/service/1/no-cache
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 130
cache-control: max-age=0, no-cache
etag: "ce39a53bd6bb8abdb2488a5a375397e4"
date: Fri, 11 Sep 2020 03:23:07 GMT
server: envoy
x-envoy-upstream-service-time: 7

这个响应会被缓存,但是每次请求时都会被校验。
响应体生成时间为:Fri, 11 Sep 2020 03:23:07 GMT

几秒后:

$ curl -i localhost:8000/service/1/no-cache
HTTP/1.1 200 OK
cache-control: max-age=0, no-cache
etag: "ce39a53bd6bb8abdb2488a5a375397e4"
date: Fri, 11 Sep 2020 03:23:12 GMT
server: envoy
x-envoy-upstream-service-time: 7
content-length: 130
content-type: text/html; charset=utf-8

这个响应会被缓存,但是每次请求时都会被校验。
响应体生成时间为:Fri, 11 Sep 2020 03:23:07 GMT

你会收到一个生成时间相同的缓存响应。 但是,响应头中的 date 信息永远是新的,因为这类响应在返回前始终会先被验证。 另外,响应头中不存在 age 信息。

如果你修改了 yaml 文件中的响应信息:

$ curl -i localhost:8000/service/1/no-cache
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 133
cache-control: max-age=0, no-cache
etag: "f4768af0ac9f6f54f88169a1f3ecc9f3"
date: Fri, 11 Sep 2020 03:24:10 GMT
server: envoy
x-envoy-upstream-service-time: 7

这个响应会被缓存,但是每次请求时都会被校验!!!
响应体生成时间为:Fri, 11 Sep 2020 03:24:10 GMT

后端服务会返回一个新的响应。 新的响应会被缓存,并用于接下来的请求。

当然,你也可以在 yaml 文件中加一个 cache-control 信息不同的响应信息去测试! 欲了解更多缓存和 cache-control 相关的信息,请访问 MDN Web Docs.