我有一个 nginx 服务器,位于运行 django 的 apache 前面。
我的网站大部分都是静态内容:http://www.grovemade.com/
我的应用服务器可以很好地处理需要动态的部分(POST、购物车、订单状态、常见问题解答等)。
绝大多数点击都是针对静态页面,例如产品页面、关于页面、ajax 获取请求。
它处理得非常好,可以直接从 memcached 提供页面。我实际上请求 SF 帮助确定我的瓶颈:这是否证明网络带宽存在瓶颈?并且它受到我主机出站流量限制的限制。因此它 100% 能够满足我的任何需求。我对这些数字感到非常兴奋;但是当 40 页同时被“嘿,给我做一个新的缓存!”“嘿,我也是!”轰炸时,这些数字并不意味着废话。
唯一的问题是,在流量大的时候,一旦某个页面的缓存过期,我的应用服务器就会收到数千个请求,这会导致一切陷入停滞/可能导致服务器崩溃。我想象的情况更像是......
答:应用服务器会按照自己的节奏自动将内容推送到 memcached(因为它当然可以填充缓存……但不能同时填充数百个)。前端服务器使用其现有资源 - 从不尝试代理获取请求。见鬼,它甚至可以抛出一个空白页,我才不管呢;至少应用服务器将处于活动状态,接受订单,并能够在未来的某个时刻填充该损坏的页面,而不是陷入死亡螺旋,我甚至无法拿起我的盾牌(memcached)。问题是:我必须构建一个系统来确定每个应该从多个地方缓存的页面。Django 知道要缓存哪些页面;我想这很容易。但是 nginx -> django:我不希望它代理所有内容(否则我会处于同样的情况);所以我必须在单独的位置编写更多逻辑。嗯。
B:Nginx 可以限制与应用服务器的连接。但是它如何区分应该排队到应用服务器的请求与我只希望它建立单个连接的请求类型?毕竟,应用程序决定页面是否应该被缓存。如果它正在我的应用服务器上等待简单的动态内容(如拉取订单详细信息),我不希望它断开连接。我是否应该在缓存/应用程序之间建立一个请求响应周期,以传达需要构建页面的信息?并且后续请求应该被忽略?
废话。
因此,考虑到 99% 的流量是由 nginx 满足的,而我的应用服务器实际上只对转换并使其成为动态页面的 X% 感兴趣,我应该怎么做才能防止我的应用服务器在响应的几秒钟内完全被淹没:
嘿,你!让我把这个页面还给你。哦,还有 1000 个人想要它?好吧,如果可以的话,我也会这么做的。
现实问题/示例:今天,我们的点击量激增。我们通常不会有太多流量,但我们今天发布了我们的产品,预订客户如潮水般涌来。服务器现在以 20% 的容量处理流量,但有一些非常非常不稳定的时刻,当每小时缓存计时器到期时,它试图生成一个每个人都在点击的微不足道的页面时,我几乎失去了它。
我疯狂地挑选最重要的缓存页面,并试图将它们保存在缓存中。这可不是什么乐趣!我还手动从 :8080 上运行的 apache 中提取 HTML,并将它们放入 memcached 中。
如果我的 apache 进程内存耗尽并完全崩溃(比如 memcached)或者花费的时间太长导致密钥过期,那么就会出现“难以恢复”的情况,即更多的人会绕过缓存,我的服务器会比平时更加超载 / 因此通常无法恢复,除非阻止所有请求并开始手动填充缓存。
通常要做什么才能使其发挥作用?
抱歉,这篇文章思路有点混乱。我因为这个一直没睡觉。
# grove urls
# ----------
location / {
set $use_memcached no;
if ($request_method = GET) {
set $use_memcached yes;
}
if ($host ~ "^cached") {
set $use_memcached no;
}
if ($request_uri ~ '.{240,}') {
set $use_memcached no;
}
if ($args ~ nginx_bypass_cache=true) {
set $use_memcached no;
}
if ($use_memcached = yes) {
set $memcached_key "nginx.$request_uri";
memcached_pass localhost:11211;
}
default_type text/html;
client_max_body_size 50m;
error_page 404 502 = @cache_miss;
}
location @cache_miss {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 50m;
client_body_buffer_size 128k;
proxy_connect_timeout 60; # time to connect to upstream server
proxy_send_timeout 300; # time to wait for upstream to accept data
proxy_read_timeout 300; # time to wait for upstream to return data
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
答案1
如果您可以发布您的 nginx 配置,我可能会帮助您找到更好的方法!
一般情况下,我会使用 Nginx 的 fastcgi_cache / proxy_cache禁用 fastcgi_cache_use_stale/proxy_cache_use_stale
我之所以说两种选择是因为如果你可以使用 Nginx 的 fastcgi 或其他模块运行后端应用程序那么最好这样做。
如果无法删除 8080 上的 Apache,最好使用带proxy_cache_use_stale updating
线的 proxy_cache。
请提供您的配置,以便我们尝试改进它。
==
添加了基于您的示例配置(非常原始,很可能需要进行调整)
#IMPORTANT outside server{..} block
proxy_cache_path /var/run/nginx-cache levels=1:2 keys_zone=GROVE:500m inactive=60m;
proxy_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale updating;
server {
#other stuff
set $no_cache 0;
# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
set $no_cache 1;
}
# grove urls
# ----------
location / {
default_type text/html;
client_max_body_size 50m;
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_body_buffer_size 128k;
proxy_connect_timeout 60; # time to connect to upstream server
proxy_send_timeout 300; # time to wait for upstream to accept data
proxy_read_timeout 300; # time to wait for upstream to return data
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
proxy_cache_bypass $no_cache;
proxy_no_cache $no_cache;
proxy_cache GROVE;
proxy_cache_valid 60m;
}