使用 Nginx 缓存代理使您的后端更可靠


我们大多数人都熟悉 Nginx——它是一个非常流行的 Web 服务器和反向代理。但是您知道您也可以将它用作缓存代理吗?
现在,您可能想知道为什么有人想做这样的事情——您不能更新您的服务以在 Redis 或 Memcached 中缓存数据吗?将缓存外部化到服务之外的单独层有什么好处?
以下是一些可能有用的场景:

  • 即使您的服务出现故障,您也希望提供缓存数据
  • 当您的服务响应时间过长时,您希望提供缓存数据
  • 当负载很大时,您想保护您的服务
  • 您有一个遗留系统,您希望使其更加可靠和高性能,但您无法更改代码
  • 您想让外部 3rd 方服务更可靠、更高效
  • 您正在使用多语言微服务架构并想要一种缓存请求的标准方式

现在您已经感兴趣了,让我们一步一步地完成实现。

简单实现

events {
    worker_connections 1024;
}

http {
+   proxy_cache_path /var/cache/nginx keys_zone=my_cache:10m;

    server {
        listen 3000;
+       proxy_cache my_cache;

        location / {
            proxy_set_header Host $host;
            proxy_pass http://my-backend-service/;
+           proxy_cache_key $scheme:
//$host$uri$is_args$query_string;
+           proxy_cache_valid 200 10m;
        }
    }
}

增加了一些新的指令,让我们逐一来看看。
Nginx将响应缓存在磁盘中,proxy_cache_path指定了存储响应的路径。
proxy_cache定义了用于存储缓存密钥和其他元数据的共享内存区。
proxy_cache_key定义了缓存密钥。
proxy_cache_valid指定了缓存过期,这也可以通过从后端服务发送缓存头信息来动态配置。一旦缓存过期,响应就不再被认为是 "新鲜的",而成为 "陈旧的stale"。

Nginx并没有立即删除陈旧的响应,而是将它们保留在磁盘中。为什么不删除它们的原因将在接下来的几节中变得更加明显。我们可以使用proxy_cache_path指令中的参数inactive来控制在删除陈旧的响应之前在磁盘中保留多长时间。起初可能会对proxy_cache_valid和inactive配置如何协同工作感到困惑,因为它们似乎在做类似的事情,让我们用下面的例子来清楚地了解。

如果proxy_cache_valid被设置为5m,inactive被设置为10m。如果第一个请求是在T0分钟时提出的,下一个请求是在T6分钟时提出的,那么第二个请求就需要从后端服务中获取,尽管数据仍然在磁盘中,因为缓存已经过期。相反,如果proxy_cache_valid被设置为10分钟,inactive被设置为5分钟,并且第一个请求在T0分钟到来,下一个请求在T6分钟到来,即使缓存没有过期,数据也会从磁盘中删除,所以需要再次从后端服务获取。

当后端关闭或缓慢时提供缓存数据
这是使用单独缓存代理的杀手级应用程序。当后端服务关闭或响应时间过长时,我们可以将 Nginx 配置为提供陈旧的响应。

events {
    worker_connections 1024;
}

http {
-   proxy_cache_path /var/cache/nginx keys_zone=my_cache:10m;
+   proxy_cache_path /var/cache/nginx keys_zone=my_cache:10m inactive=1w;

    server {
        listen 3000;
        proxy_cache my_cache;

        location / {
            proxy_set_header Host $host;
            proxy_pass http://my-backend-service/;
            proxy_cache_key $scheme:
//$host$uri$is_args$query_string;
            proxy_cache_valid 200 10m;
            proxy_cache_bypass $arg_should_bypass_cache;
+           proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 http_429;
        }
    }
}

我们更新了proxy_cache_path,加入了之前提到的非活动参数,并加入了proxy_cache_use_stale指令,参数如下。

error - 如果后端无法到达,则使用stale。

timeout - 如果后端响应时间过长,则使用stale。

http_xxx - 如果后端返回这些状态代码,则使用stale。

默认情况下,超时被设置为60秒,可以使用proxy_connect_timeout指令进行配置。

正如你所看到的,只需几行代码,我们就可以很容易地将缓存添加到你的服务中。虽然缓存只在公共端点层面上起作用,但它应该是一个很好的起点,以后可以添加Redis等来缓存你服务中的中间计算。由于它是一个独立的代理,它也可以被添加到你的后端和传统的或外部服务之间。