Elton's Blog

Tag: nginx

Nginx使用Linux内存加速静态文件访问

by on 二.05, 2012, under Linux, Web

Nginx是一个非常出色的静态资源web服务器。如果你嫌它还不够快,可以把放在磁盘中的文件,映射到内存中,减少高并发下的磁盘IO。

先做几个假设。nginx.conf中所配置站点的路径是/home/wwwroot/res,站点所对应文件原始存储路径:/opt/web/res

shell脚本非常简单,思路就是拷贝资源文件到内存中,然后在把网站的静态文件链接指向到内存中即可。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/bash   
 
res_path="/opt/web/res"  
mem_path="/dev/shm/res"  
lk_path="/home/wwwroot/res"  
 
if [ ! -d "$mem_path" ]; then  
        cp -r "$res_path" "$mem_path"  
fi  
 
if [ ! -L "$lk_path" ]; then  
        ln -s "$mem_path" "$lk_path"  
fi
Leave a Comment :, , more...

使用ETag和Expires调优web服务器性能

by on 八.04, 2009, under Linux

正确使用Etag和Expires标识处理,可以使得页面更加有效被Cache。

在客户端通过浏览器发出第一次请求某一个URL时,根据 HTTP 协议的规定,浏览器会向服务器传送报头(Http Request Header),服务器端响应同时记录相关属性标记(Http Reponse Header),服务器端的返回状态会是200,格式类似如下:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Tue, 03 Mar 2009 04:58:40 GMT
Content-Type: image/jpeg
Content-Length: 83185
Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT
Cache-Control: max-age=2592000
 
Expires: Thu, 02 Apr 2009 05:14:08 GMT
Etag: “5d8c72a5edda8d6a:3239″

客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送报头(Http Request Header),服务器端响应并记录相关记录属性标记文件没有发生改动,服务器端返回304,直接从缓存中读取:

1
2
3
4
5
6
7
8
HTTP/1.x 304 Not Modified
Date: Tue, 03 Mar 2009 05:03:56 GMT
Content-Type: image/jpeg
Content-Length: 83185
Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT
Cache-Control: max-age=2592000
Expires: Thu, 02 Apr 2009 05:14:08 GMT
Etag: “5d8c72a5edda8d6a:3239″

其中Last-Modified、Expires和Etag是标记页面缓存标识

一、Last-Modified、Expires和Etag相关工作原理

1、Last-Modified
在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(Http Reponse Header)此文件在服务期端最后被修改的时间,格式类似这样:
Last-Modified: Tue, 24 Feb 2009 08:01:04 GMT
客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头(Http Request Header),询问该时间之后文件是否有被修改过:
If-Modified-Since: Tue, 24 Feb 2009 08:01:04 GMT
如果服务器端的资源没有变化,则自动返回 HTTP 304 (NotChanged.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
注:如果If-Modified-Since的时间比服务器当前时间(当前的请求时间request_time)还晚,会认为是个非法请求

2、Etag工作原理
HTTP 协议规格说明定义ETag为“被请求变量的实体标记” (参见14.19)。简单点即服务器响应时给请求URL标记,并在HTTP响应头中将其传送到客户端,类似服务器端返回的格式:
Etag: “5d8c72a5edda8d6a:3239″
客户端的查询更新格式是这样的:
If-None-Match: “5d8c72a5edda8d6a:3239″
如果ETag没改变,则返回状态304。
即:在客户端发出请求后,Http Reponse Header中包含 Etag: “5d8c72a5edda8d6a:3239″
标识,等于告诉Client端,你拿到的这个的资源有表示ID:5d8c72a5edda8d6a:3239。当下次需要发Request索要同一个 URI的时候,浏览器同时发出一个If-None-Match报头( Http RequestHeader)此时包头中信息包含上次访问得到的Etag: “5d8c72a5edda8d6a:3239″标识。
If-None-Match: “5d8c72a5edda8d6a:3239“
,这样,Client端等于Cache了两份,服务器端就会比对2者的etag。如果If-None-Match为False,不返回200,返回304 (Not Modified) Response。

3、Expires
给出的日期/时间后,被响应认为是过时。如Expires: Thu, 02 Apr 2009 05:14:08 GMT
需和Last-Modified结合使用。用于控制请求文件的有效时间,当请求数据在有效期内时客户端浏览器从缓存请求数据而不是服务器端. 当缓存中数据失效或过期,才决定从服务器更新数据。

4、Last-Modified和Expires
Last-Modified标识能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而且要和Expires一起用。而Expires标识却使得浏览器干脆连HTTP请求都不用发,比如当用户F5或者点击Refresh按钮的时候就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而 且要和Expires一起用。

5、Etag和Expires
如果服务器端同时设置了Etag和Expires时,Etag原理同样,即与Last-Modified/Etag对应的HttpRequest Header:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和WebServer发出的 Last-Modified,Etag值完全一样;在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和 Etag之后,服务器才能返回304.

6、Last-Modified和Etag
Last-Modified 和ETags请求的http报头一起使用,服务器首先产生 Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改,来决定文件是否继续缓存
过程如下:
1. 客户端请求一个页面(A)。
2. 服务器返回页面A,并在给A加上一个Last-Modified/ETag。
3. 客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
4. 客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
5. 服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。
注:
1、Last-Modified和Etag头都是由Web Server发出的Http Reponse Header,Web Server应该同时支持这两种头。
2、Web Server发送完Last-Modified/Etag头给客户端后,客户端会缓存这些头;
3、客户端再次发起相同页面的请求时,将分别发送与Last-Modified/Etag对应的Http RequestHeader:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和 WebServer发出的Last-Modified,Etag值完全一样;
4、通过上述值到服务器端检查,判断文件是否继续缓存;

二、Apache、Lighttpd和Nginx中针配置Etag和Expires,有效缓存纯静态如css/js/pic/页面/流媒体等文件。

A、Expires
A.1、Apache Etag
使用Apache的mod_expires 模块来设置,这包括控制应答时的Expires头内容和Cache-Control头的max-age指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ExpiresActive On
ExpiresByType image/gif “access plus 1 month”
ExpiresByType image/jpg “access plus 1 month”
ExpiresByType image/jpeg “access plus 1 month”
ExpiresByType image/x-icon “access plus 1 month”
ExpiresByType image/bmp “access plus 1 month”
ExpiresByType image/png “access plus 1 month”
ExpiresByType text/html “access plus 30 minutes”
ExpiresByType text/css  “access plus 30 minutes”
ExpiresByType text/txt  “access plus 30 minutes”
ExpiresByType text/js   ”access plus 30 minutes”
ExpiresByType application/x-javascript   ”access plus 30 minutes”
ExpiresByType application/x-shockwave-flash     ”access plus 30 minutes”
或
<ifmodule mod_expires.c>
<filesmatch “.(jpg|gif|png|css|js)$”>
ExpiresActive on
ExpiresDefault “access plus 1 year”
</filesmatch>
</ifmodule>

当设置了expires后,会自动输出Cache-Control 的max-age 信息
具体关于 Expires 详细内容可以查看Apache官方文档。
在这个时间段里,该文件的请求都将直接通过缓存服务器获取,
当然如果需要忽略浏览器的刷新请求(F5),缓存服务器squid还需要使用 refresh_pattern 选项来忽略该请求

1
2
3
4
5
6
7
8
9
10
11
12
refresh_pattern -i .gif$ 1440 100% 28800 ignore-reload
refresh_pattern -i .jpg$ 1440 100% 28800 ignore-reload
refresh_pattern -i .jpeg$ 1440 100% 28800 ignore-reload
refresh_pattern -i .png$ 1440 100% 28800 ignore-reload
refresh_pattern -i .bmp$ 1440 100% 28800 ignore-reload
refresh_pattern -i .htm$ 60 100% 100 ignore-reload
refresh_pattern -i .html$ 1440 50% 28800 ignore-reload
refresh_pattern -i .xml$ 1440 50% 28800 ignore-reload
refresh_pattern -i .txt$ 1440 50% 28800 ignore-reload
refresh_pattern -i .css$ 1440 50% 28800 reload-into-ims
refresh_pattern -i .js$ 60 50% 100 reload-into-ims
refresh_pattern . 10 50% 60

有关Squid中Expires的说明,请参考Squid官方中refresh_pattern介绍。

A.2、Lighttpd Expires
和Apache一样Lighttpd设置expire也要先查看是否支持了mod_expire模块,
下面的设置是让URI中所有images目录下的文件1小时后过期;

1
expire.url = ( “/images/” => “access 1 hours” )

下面是让作用于images目录及其子目录的文件;

1
2
3
$HTTP["url"] =~ “^/images/” {
expire.url = ( “” => “access 1 hours” )
}

也可以指定文件的类型;

1
2
3
$HTTP["url"] =~ “.(jpg|gif|png|css|js)$” {
expire.url = ( “” => “access 1 hours” )
}

具体参考Lighttpd官方Expires解释

A.3、Nginx中Expires

1
2
3
4
5
6
7
8
location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
}
location ~ .*.(js|css)?$
{
expires 1h;
}

这类文件并不常修改,通过 expires 指令来控制其在浏览器的缓存,以减少不必要的请求。 expires 指令可以控制 HTTP 应答中的“ Expires ”和“ Cache-Control ”的头标(起到控制页面缓存的作用)。其他请参考Nginx中Expires

B.1、Apache中Etag设置
在Apache中设置Etag的支持比较简单,只用在含有静态文件的目录中建立一个文件.htaccess, 里面加入:

1
FileETag MTime Size

这样就行了,详细的可以参考Apache的FileEtag文档页

B.2、Lighttpd Etag
在Lighttpd中设置Etag支持:
etag.use-inode: 是否使用inode作为Etag
etag.use-mtime: 是否使用文件修改时间作为Etag
etag.use-size: 是否使用文件大小作为Etag
static-file.etags: 是否启用Etag的功能
第四个参数肯定是要enable的, 前面三个就看实际的需要来选吧,推荐使用修改时间

B.3、 Nginx Etag
Nginx中默认没有添加对Etag标识.Igor Sysoev的观点”在对静态文件处理上看不出如何Etag好于Last-Modified标识。”
Note:
Yes, it’s addition,and it’s easy to add, however, I do not see howETag is better than Last-Modified for static files. -Igor Sysoev
A nice short description is here:

http://www.mnot.net/cache_docs/#WORK

It looks to me that it makes some caches out there to cache theresponse from the origin server more reliable as in rfc2616(ftp://ftp.rfc-editor.org/in-notes/rfc2616.txt) is written.
3.11 Entity Tags 13.3.2 Entity Tag Cache Validators 14.19 ETag
当然也有第三方nginx-static-etags 模块了,请参考

http://mikewest.org/2008/11/generating-etags-for-static-content-using-nginx

三、对于非实时交互动态页面中Expires和Etag处理

对数据更新并不频繁、如tag分类归档等等,可以考虑对其cache。简单点就是在非实时交互的动态程序中输出expires和etag标识,让其缓存。但需要注意关闭session,防止http response时http header包含session id标识;
3.1、Expires
如expires.php

1
2
3
4
5
<?php
header(’Cache-Control: max-age=86400,must-revalidate’);
header(’Last-Modified:.gmdate(’D, d M Y H:i:s’) . ‘ GMT’ );
header(”Expires:.gmdate (’D, d M Y H:i:s’, time() +86400). ‘ GMT’);
?>

以上信息表示该文件自请求后24小时后过期。
其他需要处理的动态页面直接调用即可。
3.2、Etag
根据Http返回状态来处理。当返回304直接从缓存中读取
如etag.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
cache();
echo date(”Y-m-d H:i:s”);
function cache()
{
$etag = “http://longrujun.name”;
if ($_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
{
header(’Etag:.$etag,true,304);
exit;
}
else header(’Etag:.$etag);
}
?>

来自: http://longrujun.name/index.php/2009/03/04/etag%E5%92%8Cexpires/

4 Comments :, , , , , , more...

Gentoo下Nginx+thin构建rails环境

by on 七.21, 2009, under Linux, Rails

本文前提是你已经配置好了ruby on rails

安装thin

thin是一个ruby的轻量级的web server

可以看到thin在100个并发连接的时候,性能还是不错的。

可以使用

1
sudo gem install thin

或者

1
emerage -av thin

使用emerage的话,需要在/etc/portage/package.keywords中加入

1
2
3
www-servers/thin ~amd64
dev-ruby/eventmachine ~amd64
dev-ruby/rack ~amd64

因为相关的包被gentoo的portage给mask了

创建thin集群rake脚本

进入你的rails应用目录,在lib/tasks下建立一个thin的任务,以.rake为后缀名,如thin.rake。这个是用来建立thin的集群的脚本
编辑内容如下:

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
32
namespace :thin do
  namespace :cluster do
 desc 'Start thin cluster'
    task :start =&gt; :environment do
      `cd #{RAILS_ROOT}`
      port_range = RAILS_ENV == 'development' ? 3 : 8
      (ENV['SIZE'] ? ENV['SIZE'].to_i : 4).times do |i|
        Thread.new do
          port = ENV['PORT'] ? ENV['PORT'].to_i + i : ("#{port_range}%03d" % i)
          str  = "thin start -d -p#{port} -Ptmp/pids/thin-#{port}.pid"
          str += " -e#{RAILS_ENV}"
          puts str
          puts "Starting server on port #{port}..."
          `#{str}`
        end
      end
    end
desc 'Stop all thin clusters'
    task :stop =&gt; :environment do
      `cd #{RAILS_ROOT}`
      Dir.new("#{RAILS_ROOT}/tmp/pids").each do |file|
        Thread.new do
          if file.starts_with?("thin-")
            str  = "thin stop -Ptmp/pids/#{file}"
            puts "Stopping server on port #{file[/d+/]}..."
            `#{str}`
          end
        end
      end
    end
  end
end

之后就可以使用

1
2
# rake thin:cluster:start RAILS_ENV=production SIZE=3 PORT=8000
# rake thin:cluster:stop

来启动和停止thin集群了。

编辑nginx的conf文件,加入rails虚拟主机

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
upstream thin {
    server 127.0.0.1:8000;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}
 
server {
        listen   80;
        server_name  localhost;
        access_log  /var/log/nginx/localhost.access.log;
        root /var/www/test/public;
 
        location / {
                proxy_set_header  X-Real-IP  $remote_addr;
                proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect false;
                if (-f $request_filename/index.html) {
                        rewrite (.*) $1/index.html break;
                }
                if (-f $request_filename.html) {
                        rewrite (.*) $1.html break;
                }
                 if (!-f $request_filename) {
                        proxy_pass http://thin;
                        break;
                }
        }
}

重启nginx就可以运行rails应用了。

参考:
http://code.macournoyer.com/thin/
http://glauche.de/2008/01/12/thin-nginx-with-rails/

2 Comments :, , , more...

Gentoo下安装Nginx+php

by on 七.19, 2009, under Linux, PHP

使用nginx(engin x)和spawn-fcgi来共同支持php

安装nginx

1
emerge -av nginx

安装spawn-fcgi

1
emerge -av spawn-fcgi

启动spawn-fcgi

1
spawn-fcgi -a 127.0.0.1 -p 9000 -f /usr/bin/php-cgi -C 10

a 表示绑定的ip地址
p 表示端口号
f 表示fcgi的应用程序,在这里是制定php的cgi版本的程序
C 表示spawn的child的个数

执行netstat检查spwan-fcgi是否正常启动,可以看到9000端口是否已经开始监听

1
netstat -tnpl

配置nginx

编辑nginx.conf文件

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
user nginx nginx;
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
 
error_log /var/log/nginx/error_log info;
 
#Specifies the value for maximum file descriptors that can be opened by this process.
worker_rlimit_nofile 51200;
 
events {
        worker_connections  8192;
        use epoll;
}
 
http {
        include         /etc/nginx/mime.types;
        default_type    application/octet-stream;
 
        log_format main
                '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $bytes_sent '
                '"$http_referer" "$http_user_agent" '
                '"$gzip_ratio"';
 
        client_header_timeout   10m;
        client_body_timeout     10m;
        client_max_body_size 50m; #最大允许上传50M的附件
        send_timeout            10m;
 
        connection_pool_size            512;
        client_header_buffer_size       8k;
        large_client_header_buffers     4 4k;
        request_pool_size               1024k;
 
        gzip on;
        gzip_min_length 1k;
        gzip_buffers    4 8k;
        gzip_http_version  1.1;
        gzip_types   text/plain application/x-javascript text/css application/xml;
 
        output_buffers  1 1024k;
        postpone_output 1460;
 
        sendfile        on;
        tcp_nopush      on;
        tcp_nodelay     on;
 
        keepalive_timeout       75 20;
        ignore_invalid_headers  on;
        index index.html;
 
        include /data/www/vhosts/*.conf;

其中,worker_processes表示worker进程的个数,一般跟CPU个数相同
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000
表示每个CPU处理一个worker

虚拟主机配置

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 {
        index           index.htm index.html index.php
        server_name     blog.domain.com;
        access_log      /var/log/nginx/domain.access_log main;
        error_log       /var/log/nginx/domain.error_log info;
        root            /data/www/domain/htdocs/blog;
 
        location / {
                if (-f $request_filename/index.html){
                        rewrite (.*) $1/index.html break;
                }
 
                if (-f $request_filename/index.php){
                        rewrite (.*) $1/index.php;
                }
 
                if (!-f $request_filename){
                        rewrite (.*) /index.php;
                }
        }
 
        location ~ .*.php$ {
                include /etc/nginx/fastcgi_params;
                fastcgi_index index.php;
                fastcgi_pass  127.0.0.1:9000;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;        }
 
         location ~ .(htm|html|gif|jpg|jpeg|png|bmp|ico|css|js)$ {
                expires 30d;
        }
}

上面的配置以wordpress的静态url为例
其中,location ~ .*.php$ 部分是配置php的fcgi
expires,是设置静态资源的缓存时间
rewrite部分是设置wordpress静态url时候需要用到的rewrite

Nginx日常维护

Nginx 支持下表中的信号:
信号名 作用描述
TERM, INT 快速关闭程序,中止当前正在处理的请求
QUIT 处理完当前请求后,关闭程序
HUP 重新加载配置,并开启新的工作进程,关闭就的进程,此操作不会中断请求
USR1 重新打开日志文件,用于切换日志,例如每天生成一个新的日志文件
USR2 平滑升级可执行程序
WINCH 从容关闭工作进程

如要重新加载配置文件就使用如下命令

1
#kill -HUP <pid>

pid是nginx的进称号,通过netstat -tnpl可以查到

Nginx 监控

通过在配置文件中加入:

1
2
3
4
location ~ ^/status/ {
	    stub_status on; #Nginx 状态监控配置
	    access_log off;
	 }

就可以使用http://yourdomain.com/stauts监控nginx的状态。
可以看到类似这样的信息

1
2
3
4
Active connections: 70
server accepts handled requests
 14553819 14553819 19239266
Reading: 0 Writing: 3 Waiting: 67
  • active connections – 当前 Nginx 正处理的活动连接数.
  • server accepts handled requests — 总共处理了 14553819 个连接 , 成功创建 14553819 次握手 ( 证明中间没有失败的 ), 总共处理了 19239266 个请求 ( 平均每次握手处理了 1.3 个数据请求 )
  • reading — nginx 读取到客户端的 Header 信息数
  • writing — nginx 返回给客户端的 Header 信息数。
  • waiting — 开启 keep-alive 的情况下,这个值等于 active – (reading + writing),意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接。

参考:
http://www.ibm.com/developerworks/cn/web/wa-lo-nginx/index.html
http://wiki.nginx.org/NginxChs
http://blog.chinaunix.net/u/12909/showart_1831422.html

Leave a Comment :, , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit my friends!

A few highly recommended friends...