Nginx fastcgi_cache 的坑

这两天迁移完后发现一个奇怪的问题:我nginx里配置了两个vhost,分别是博客(www.repfaststart.com)和图床(tu.repfaststart.com),然后有时候,当我访问图床时,打开来的结果是博客。

遇到这种问题,第一印象是我nginx配置文件写错了?检查了下,没错啊,server_name 配置的明明白白,清清楚楚的。看日志,抓包,两个结果都是相同的,显示直接返回的200状态码。而nginx的日志,我两个vhosts的日志是分开记录的,当出现上诉情况时,tu.log里会记录一条200,然后在www.log里记录了其他页面元素的记录。

在排除了CDN配置和php-fpm错误的可能性后,我又想到了日志里那条诡异的200日志,之前没注意看,现在看发现这条日志没有upstream,也就是说没经过php-fpm处理,那可以肯定,还是nginx自己的问题了。

又重新审视了下nginx的配置文件,终于找到了原因,下面是图床(tu.repfaststart.com)配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fastcgi_cache_path /data/cache/tu levels=1:2 keys_zone=tu:10m;
#省略无关配置
server {
listen 443 ssl http2;
server_name tu.repfaststart.com;
#省略无关配置
location ~ \.php$ {
fastcgi_pass unix:/dev/shm/php-cgi.sock;
include /app/nginx/conf/fastcgi.conf;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
try_files $uri =404;

expires -1;
#下面这行就是根源所在
fastcgi_cache www;
fastcgi_cache_key $request_uri;
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;
fastcgi_cache_valid 200;
add_header X-Cache $upstream_cache_status;
}
}

可以看到,我这里配置了fastcgi_cache,然后我这个配置文件是拷贝博客(www.repfaststart.com)的配置文件修改的,下面的fastcgi_cache的keys_zone没有更改过来,因此两个网站使用了共同的缓存,而缓存是用request_uri做主键的,所以当直访问/这个两者共有的请求时,fastcgi_cache会直接把缓存的内容返回给客户端,就出现了这次的症状。

知道了问题根源,那就很好办了,修改完配置文件,重载nginx,至此问题解决。这次还是粗心大意导致的问题,还好是自己的网站,如果是公司业务,那麻烦可就大咯。

记一次Nginx负载均衡

前因

今天收到客服反馈很多用户说播放视频卡,甚至无法播放,检查后发现分布式存储系统里的某台服务器带宽满了,而导致带宽突然跑满的原因是由于某视频爆火,带宽一下子增加了3G。

过程

架构图

简单说下这块的访问流程:当用户访问 http://file.example.com/a.mp4 ,根据DNS解析,这个请求会先到达调度器(100.0.0.1),调度器经过数据库查询,得知这个文件存储在存储服务器1(200.0.0.1)上,然后会给用户返回一个302,把地址指向到 http://200.0.0.1/file.example.com/a.mp4 ,用户请求302后的地址,即得到了资源。

服务器物理带宽跑满,那没其他办法了,只能把带宽调度均摊到其他服务器上。这就有一个问题了,调度器给的地址,是以IP开头的地址,而要修改调度器的话,就要去改数据库了,这个不是一个规范的操作,而且是个危险的操作,所以这个方案直接排除。调度器这块不能更改,那只能从存储服务器1上动手了,当用户访问302后的地址也就是 http://200.0.0.1/file.example.com/a.mp4 时,实际上访问的是Nginx的80端口,这就有文章可以做了,下面是具体思路。

存储1(高流量):当收到用户请求时,先在Nginx里对请求做一个处理,将60%的流量302到存储2和存储3的8080端口,同时新开放一个81端口,用于存储2以及存储3服务器的回源。
存储2、存储3(低流量):安装OCT软件(一个内容缓存服务,类似squid服务),从存储1的81端口进行缓存资源,OCT开放8080端口。

下面是存储1的Nginx配置文件,在对应的location里添加了lua代码,这个是这个架构的核心所在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
server {
listen 80 default_server;
#新增81端口用于存储2以及存储3用于回源
listen 81 default_server;
charset utf-8;
server_name 200.0.0.1;

location ~ "^/\d{1,2}/" {

#下面这段lua代码是核心,将用户请求中的60%流量进行302重定向,返回一个存储2以及存储3服务器上带8080端口的URL

rewrite_by_lua '
if ngx.var.server_port == "81" then
return
end

x = math.random()
if x > 0.4 and x <= 0.7 then
ngx.redirect("http://200.0.0.2:8080" .. ngx.var.request_uri)
return
end

if x > 0.7 then
ngx.redirect("http://200.0.0.3:8080" .. ngx.var.request_uri)
return
end
';
}

}

存储2以及存储3上OCT的配置文件则如下:

1
2
3
4
5
6
7
8
9
10
11
threads 4 #4线程
store ssd #存储组名叫ssd
path /dev/sdb #ssd对应的快设备(硬盘)

listen 8080 #监听8080端口
description stf #备注
upstream_host 200.0.0.1:81 #从存储1的81端口进行回源
expires_default 6000000 #默认过期时间,单位秒
order_of_store ssd #缓存存在名叫ssd的存储组上
check_consistency on #开启一致性检查
mp4 mp4|flv start end second #对视频文件开启拖拽支持

配置文件配置好后,分别重载服务,即可生效。此时用户再来访问 http://file.example.com/a.mp4 ,那过程就是先到调度器,调度器302到 http://200.0.0.1/file.example.com/a.mp4 ,而后面有60%的概率再返回一个302,将用户302到 http://200.0.0.2:8080/file.example.com/a.mp4

结果

原来流量跑满的服务器,流量成功降了下来,而低流量的服务器,流量瞬间上升,客服回访用户,均恢复正常。