nginx的问题(续)

昨天说了nginx的几个问题,

由于我现在支持的主要是通用CDN,也就是小图片和CSS以及JS这些东西,并没有做大文件的cache。而问了我们视频部门他们也根本不用nginx作为cache使用,所以有些问题还是没考虑周到。

一个问题是之前发现的,在做tmpfs的时候,当你设置的cache大小为tmpfs的90%的时候,一旦要缓存的内容大于这个90%的时候,nginx会自己崩溃掉。这个我明天做个测试再重现一下,现在有点忘记原因了,好像是tmpfs会占用到实际的磁盘才导致的。

另外一个其实是upstream的问题,这个是一个新浪的老兄提的,基本现在是无解。问题是:比如平均一个文件10M,你在磁盘上存3000个文件,然后模拟多个客户端去访问,因为shm一开始是空的,所以都会去后端拽文件,我们放慢这个拽的过程,比如说网络不好吧 呵呵,那么第一个请求发现不在lookup失败,就会去upstream取数据,边取边向downsteam发,并且存在一个临时文件中,这时第二个请求来了也请求这个文件,因为第一个请求没有处理完,所以第二个请求找不到目标文件只好继续去后端拽,又生成临时文件,并发量大的时候,一个大文件会对应N多的临时文件,最终这些文件rename到一个目标文件,大大浪费了磁盘IO。这个rename是内核做的,nginx调完之后就返回了。这种问题往往会导致内部网络流量非常大,而实际却没有根本没有大。

当有多个连接来获取同一个文件的时候,squid中却没有跟nginx cache一样的问题。我们看看squid是怎么解决的吧。我们在squid有这样一个参数collapsed_forwarding on(http://wiki.squid-cache.org/Features/CollapsedForwarding),一旦设置了参数了,当发现有连接请求同一个文件的时候,squid会合并多个连接,但是它会以最慢速的那个连接去取,然后同步地传送给客户端。这样就不会重复请求后端了。但是squid也有问题,因为首先它是同步的。当用户请求的是动态的内容的话会导致客户端错误。而且当一个用户连接速度很快,一个用户连接速度很慢,那就会以最慢的速度进行获取和传输。所以在squid3.0以后取消了这个参数。

这个问题的解决办法暂时看起来只能在nginx cache前充一下热点的大文件。还好一般内网都是千兆连接,这个速度往往大于用户的连接速度。只有当文件特别大,并且热点文件特别多的情况下才会发生。而且nginx这种处理方式也有一定的好处。

nginx upstream流程我们可以参考http://bollaxu.javaeye.com/blog/900424http://bollaxu.javaeye.com/blog/900357这2篇文章。基本是由于nginx upstream为了防止锁所以进程间是不共享的。

###########################################

Best regards
Timo Seven
blog:http://www.timoseven.com
twitter: http://twitter.com/zauc
Linux System Admin  & MySQL DBA

Advertisements

nginx cache现有的几个问题

在我这边现实环境中主要是有三个问题。

第一个问题是当cache过期的时候,nginx会把cache状态设置为updating,但是这个updating却没有设置一个超时,所以导致的情况当一个cache过期的时候好多nginx会连接到源站取数据,而如果源站并发量有限的话,会导致源站突发负载过高,而这个时候导致nginx一直处于updating过程中,如果导致源站挂掉的话,那完蛋了。当然我们也可以设置proxy_cache_use_stale updating;但是这样会导致cache的内容不准确。

解决的方法有2种,一个是我们现在更改nginx源码,使之可以updating设置有超时时间,这样不会导致长时间连接源站造成拥塞。另外一种就是nginx cache分层,分为父节点和子节点,只有父节点才会去源站更新,子节点只从父节点上更新。

第二个问题是upstream中server的状态,我们知道upstream中可以设置proxy_next_upstream当一个服务器挂掉后自动转到其它的服务器上,但是这里有个问题就是让用户访问的时候proxy会每次都会先访问下这个坏的服务器,连接不通了再转到其它服务器上,这每次都要测试会浪费大量的时间。我们需要得到的是proxy会独立一个线程去检查后端服务器,一旦发现后端服务器挂掉的话,那直接从upstream中剔除掉这个服务器就可。而等检测后端服务器又存活了再加进来。

这个现在有ngx_http_healthcheck_module这个第三方模块。具体可以参考http://wiki.nginx.org/HttpHealthcheckModule

第三个问题是nginx proxy连接nginx cache的时候居然使用的是http1.0的,而不是http1.1,就没法使用keepalive连接后端,导致我们小文件的cache服务器连接数过高,而没法进行复用链接。

这个暂时还没有解决,不过看到http://wiki.nginx.org/HttpUpstreamKeepaliveModule这个第三方模块,但是实际还没有测试过,没法做什么结论。

其实大家可以完全查看http://wiki.nginx.org/3rdPartyModules 来看看是否针对自己的应用,有些还是我们国人自己开发的,但是针对第三方模块还是要谨慎使用,上面的healthcheck模块还是经过我们自己的开发重新修改后并进行压力测试后再上线的。开源就是这点好啊。看来我要得学习学习C语言编程来,以后自己也改改增加增加功能啥的。

###########################################

Best regards
Timo Seven
blog:http://www.timoseven.com
twitter: http://twitter.com/zauc
Linux System Admin  & MySQL DBA

关于nginx cache的命中率

在squid中可以很简单的查看squid的命中率,但是在nginx需要在日志中添加$upstream_cache_status这个参数,这样就可以显示它的cache状态,有

  1. MISS
  2. EXPIRED – expired, request was passed to backend
  3. UPDATING – expired, stale response was used due to proxy/fastcgi_cache_use_stale updating
  4. STALE – expired, stale response was used due to proxy/fastcgi_cache_use_stale
  5. HIT

共这样5种状态。
我的日志文件的格式为:

log_format cache '$remote_addr - $remote_user [$time_local] - $request ' 'upstream_response_time $upstream_response_time ' 'msec $msec request_time $request_time - $upstream_cache_status';

在我的日志文件中计算了upstream的响应时间和命中状态。

这样很容易就能从日志中计算出nginx cache的命中率了。

###########################################

Best regards
Timo Seven
blog: http://www.timoseven.com
twitter: http://twitter.com/zauc
Linux System Admin & MySQL DBA

nginx cache试用

nginx cache现在已经在0.8以上的版本默认启用了,其实是作为proxy部分的一部分。而且现在nginx cache已经支持expires, gzip,内存cache管理等等,已经可以作为线上可以使用了。

使用nginx cache一直是有这个想法的,因为squid实在是太过臃肿了,而连接数到达400已经很吃力了,而nginx的优势恰恰是在连接数上。下面就开始试用吧。

首先是安装,我还特地下载了一个purge工具,专门用来进行推送,但是还是要单独安装squidclient。

./configure --prefix=/usr/local/nginx --with-http_realip_module
--with-http_sub_module --with-http_gzip_static_module
--with-http_stub_status_module --with-file-aio
--add-module=/home/timo/download/ngx_cache_purge-1.2
make
make install

下面这个是我自己的一个Nginx.conf文件,用来做最简单的cache功能

user www-data;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;

#pid logs/nginx.pid;

events {
worker_connections 1024;
use	epoll;
}

http {
include mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';

access_log logs/access.log main;

sendfile on;
tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;

gzip on;
gzip_http_version 1.0;
gzip_comp_level 9;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
#gzip_min_length 1100;
gzip_buffers 16 8k;
#gzip_disable "MSIE [1-6]\.(?!.*SV1)";

proxy_cache_path /var/www/nginx_cache levels=1:2
keys_zone=cache_one:200m inactive=1d max_size=200m;
# proxy_temp_path /var/www/nginx_cache/temp;

server {
listen 10.1.41.81:81;
# expires 2d;

location / {
proxy_cache cache_one;
# proxy_cache_valid 200 301 302 304 30d;
proxy_cache_key $host$uri;
proxy_hide_header Vary;
proxy_set_header Host $host;
proxy_set_header Accept-Encoding '';
proxy_set_header X-Forwarded-For $remote_addr; add_header X-Cache "HIT from cache_test";
proxy_pass http://10.1.41.81;
if ( $request_method = "PURGE" ) {
rewrite ^(.*)$ /purge$1 last;
}
}

location ~ /purge(/.*) {
allow 10.0.0.0/8;
deny all;
error_page 405 =200 /purge$1;
proxy_cache_purge cache_one $host$1$is_args$args;
}
}

}

现在对proxy cache进行下说明:

proxy_cache_path /var/www/nginx_cache levels=1:2
keys_zone=cache_one:200m inactive=1d max_size=200m;

levels1:2表示有2层目录,第一层是一个字母或数字,第二层为2个字母或数字
keys_zones=cache_one:200m 其中cache_one为自己定义的名字,200m为这个cache目录的大小 inactive=1d 表示当这个文件在一天没有请求后会进行清理
max_size=200m 能够缓存的最大空间为200m

proxy_cache_valid 200 301 302 304 30d;
这个看wiki按我的理解是200,301,302,304这些Http code可以缓存有效30天,也可以把这些http code替换成any, 但是这样会导致404,502,503也会被缓存。

add_header X-Cache “HIT from cache_test”;
这个是在http头中加入自定义的头,比如像我加如一个跟squid一样的Hit头

if purge这个部分是直接抄的http://sudone.com/nginx/nginx_cache_purge.html 这个为了做推送用的。

推送的方法就是参考ayou的

squidclient -h192.168.0.123 -p80 -mPURGE -v http://www.sudone.com/index.html 

###########################################

Best regards
Timo Seven
blog: http://www.timoseven.com
twitter: http://twitter.com/zauc
Linux System Admin & MySQL DBA