企业级Nginx Web服务优化-Nginx基本安全优化

1.1 调整参数隐藏Nginx软件版本号信息

  • 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的。因此,我们应尽量隐藏或消除Web服务对访问用户显示各类敏感信息(例如Web软件名称及版本号等信息),这样恶意的用户就很难猜到他攻击的服务器所用的是否有特定漏洞的软件,或者是否有对应漏洞的某一特定版本,从而加强Web服务的安全性。这在武侠小说里,就相当于隐身术,你隐身了,对手就很难打着你了。
  • 想要隐身,首先要了解所使用软件的版本号,对于Linux客户端,可通过命令行查看Nginx版本号,最简单的方法就是在Linux客户端系统命令行执行如下curl命令:
[root@LNMP html]# curl -I 192.168.0.220
HTTP/1.1 200 OK
Server: nginx/1.6.2             #这里清晰的暴露了Web版本号(1.6.2)及软件名称(nginx)
Date: Wed, 23 Aug 2017 10:45:47 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.3.28
Link: <http://192.168.0.220/wp-json/>; rel="https://api.w.org/"

在Windows客户端上,通过浏览器访问Web服务时,若找不到页面,默认报错的信息如下图所示:

QQ截图20170825205242.png-31.5kB

以上虽然是不同的客户端,但是都获得了Nginx软件名称,而且查到了Nginx的版本号,这就使得Nginx Web服务的安全存在一定的风险,因此,应隐藏掉这些敏感信息或用一个其他的名字将其替代。例如,下面是百度搜索引擎网站Web软件的更名做法:

[root@LNMP html]# curl -I baidu.com
HTTP/1.1 200 OK
Date: Fri, 25 Aug 2017 12:22:29 GMT
Server: Apache          #将Web服务软件更名为了Apache,并且版本号也去掉了
[root@LNMP html]# curl -I -s www.baidu.com       
HTTP/1.1 200 OK
Server: bfe/1.0.8.18        #将Web服务软件更名为了bfe,并且版本号改为1.0.8.18(闭源软件名称和版本就无所谓了)

门户网站尚且如此,我们也学着隐藏或改掉应用服务软件名和版本号把!事实上,还可以通过配置文件加参数来隐藏Nginx版本号。编辑nginx.conf配置文件增加参数,实现隐藏Nginx版本号的方式如下:

#在Nginx配置文件nginx.conf中的http标签段内加入“server_tokens off”

http
{
...............
server_tokens off;
...............
}

此参数放置在http标签内,作用是控制http response header内的Web服务版本信息的显示,以及错误信息中Web服务版本信息的显示。

server_tokens参数的官方说明如下:
syntax:     server_tokens on|off;   #此行为参数语法,on为开启状态,off为关闭状态
default:    server_tokens on;       #此行意思是不配置该参数,软件默认情况的结果
context:    http,server,location    #此行为server_tokens参数可以放置的位置参数作用:激活或禁止Nginx的版本信息显示在报错信息和Server的响应首部位置中。

官方资料地址:http://nginx.org/en/docs/http/ngx_http_core_module.html

配置完毕后保存,重新加载配置文件,再次通过curl查看,结果如下:

[root@LNMP nginx]# curl -I 192.168.0.220
HTTP/1.1 200 OK
Server: nginx                   #版本号已经消失
Date: Wed, 23 Aug 2017 11:22:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.3.28
Link: <http://192.168.0.220/wp-json/>; rel="https://api.w.org/"

此时,浏览器的报错提示中没有了版本号,如下图所示,修改成功。

QQ截图20170825205203.png-34.3kB

1.2 更改源码隐藏Nginx软件名及版本号

隐藏了Nginx版本号后,更进一步,可以通过一些手段把Web服务软件的名称也隐藏起来,或者更改为其他Web服务软件名以迷惑黑客。但软件名字的隐藏修改,一般情况下不会有配置参数和入口,Nginx也不例外,这可能是由于商业及品牌展示等原因,软件提供商不希望使用者把软件名字隐藏起来。因此,此处需要更改Nginx源代码,具体的解决方法如下:

1.2.1 第一步:依次修改3个Nginx源码文件。

修改的第一个文件为nginx-1.6.3/src/core/nginx.h,如下:

[root@LNMP ~]# cd /usr/src/nginx-1.6.2/src/core/
[root@LNMP core]# ls -l nginx.h
-rw-r--r--. 1 1001 1001 351 Sep 16  2014 nginx.h
[root@LNMP core]# sed -n '13,17p' nginx.h
#define NGINX_VERSION      "1.6.2"      #修改为想要显示的版本号
#define NGINX_VER          "nginx/" NGINX_VERSION
#将nginx修改为想要修改的软件名称。
#define NGINX_VAR          "NGINX"      #将nginx修改为想要修改的软件名称
#define NGX_OLDPID_EXT     ".oldbin"

修改后的结果如下:

[root@LNMP core]# sed -n '13,17p' nginx.h
#define NGINX_VERSION      "0.0.0.0"
#define NGINX_VER          "yunjisuan/" NGINX_VERSION

#define NGINX_VAR          "YUNJISUAN"
#define NGX_OLDPID_EXT     ".oldbin"

修改的第二个文件是nginx-1.6.3/src/http/ngx_http_header_filter_module.c的第49行,需要修改的字符串内容如下:


ls -l /usr/src/nginx-1.6.2/src/http/ngx_http_header_filter_module.c 
-rw-r--r--. 1 1001 1001 19321 Sep 16  2014 /usr/src/nginx-1.6.2/src/http/ngx_http_header_filter_module.c
[root@LNMP http]# grep -n 'Server: nginx' ngx_http_header_filter_module.c 
49:static char ngx_http_server_string[] = "Server: nginx" CRLF;           #修改本行结尾的nginx

通过sed替换修改,后如下:

[root@LNMP http]# grep -n 'Server: nginx' ngx_http_header_filter_module.c 
49:static char ngx_http_server_string[] = "Server: nginx" CRLF;
[root@LNMP http]# sed -i 's#Server: nginx#Server: yunjisuan#g' ngx_http_header_filter_module.c 
[root@LNMP http]# grep -n 'Server: yunjisuan' ngx_http_header_filter_module.c 
49:static char ngx_http_server_string[] = "Server: yunjisuan" CRLF;

修改的第三个文件是nginx-1.6.3/src/http/nginx_http_special_response.c,对面页面报错时,它会控制是否展开敏感信息。这里输出修改前的信息ngx_http_special_response.c中的第21~30行,如下:

[root@LNMP http]# sed -n '21,30p' ngx_http_special_response.c 
static u_char ngx_http_error_full_tail[] =
"<hr><center>" NGINX_VER "</center>" CRLF       #此行需要修改
"</body>" CRLF
"</html>" CRLF
;

static u_char ngx_http_error_tail[] =
"<hr><center>nginx</center>" CRLF               #此行需要修改
"</body>" CRLF

修改后的结果如下:

[root@LNMP nginx-1.6.2]# sed -n '21,32p' src/http/ngx_http_special_response.c 
static u_char ngx_http_error_full_tail[] =
"<hr><center>" NGINX_VER "  (Mr.chen 2018-08-26)</center>"  CRLF    #此行是定义对外展示的内容
"</body>" CRLF
"</html>" CRLF
;


static u_char ngx_http_error_tail[] =
"<hr><center>yunjisuan</center>" CRLF       #此行将对外展示的Nginx名字更改为yunjisuan
"</body>" CRLF
"</html>" CRLF
;

1.2.2 第二步是修改后编辑软件,使其生效

修改后再编译安装软件,如果是已经安装好的服务,需要重新编译Nginx,配好配置,启动服务。
再次使浏览器出现404错误,然后看访问结果,如下图所示:

QQ截图20170826205843.png-33.2kB

如上面所示:Nginx的软件和版本名都被改掉了,并且加上了本人的大名。
再看看Linux curl命令响应头部信息,如下:

[root@LNMP conf]# curl -I localhost/xxx/
HTTP/1.1 404 Not Found
Server: yunjisuan/0.0.0.0           #也更改了
Date: Wed, 23 Aug 2017 15:33:54 GMT
Content-Type: text/html
Content-Length: 196
Connection: keep-alive

1.3 更改Nginx服务的默认用户

  • 为了让Web服务更安全,要尽可能地改掉软件默认的所有配置,包括端口,用户等。
  • 下面就来更改Nginx服务的默认用户
  • 首先,查看Nginx服务对应的默认用户。一般情况下,Nginx服务启动后,默认使用的用户是nobody,查看默认的配置文件,如下:
[root@LNMP conf]# cd /usr/local/nginx/conf/
[root@LNMP conf]# grep "#user" nginx.conf.default
#user  nobody;

为了防止黑客猜到这个Web服务的用户,我们需要更改成特殊的用户名,例如nginx或特殊点的inca,但是这个用户必须是系统里事先存在的,下面以nginx用户为例进行说明。

(1)为Nginx服务建立新用户

useradd nginx -s /sbin/nologin -M
#不需要有系统登录权限,应当禁止登陆。

(2)配置Nginx服务,让其使用刚建立的nginx用户

更改Nginx服务默认使用用户,方法有二:

第一种:直接更改配置文件参数,将默认的#user nobody;改为如下内容:

user nginx nginx;

如果注释或不设置上述参数,默认为nobody用户,不推荐使用nobody用户名,最好采用一个普通用户,此处用大家习惯的,前面建立好的nginx用户。

第二种:直接在编译nginx软件时指定编译的用户和组,命令如下:

./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module

#提示:
#前文在编译Nginx服务时,就是这样带着参数的,因此无论配置文件中是否加参数,默认都是nginx用户。

(3)检查更改用户的效果

重新加载配置后,检查Nginx服务进程的对应用户,如下:

[root@LNMP conf]# ps -ef | grep nginx | grep -v grep
root      52023      1  0 11:30 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx     52024  52023  0 11:30 ?        00:00:00 nginx: worker process      

通过查看上述更改后的Nginx进程,可以看到worker processes进程对应的用户都变成了nginx。所以,我们有理由得出结论,上述的两种方法都可设置Nginx的worker进程运行的用户。当然,Nginx的主进程还是以root身份运行的。

二,根据参数优化Nginx服务性能

2.1 优化Nginx服务的worker进程个数

  • 在高并发,高访问量的Web服务场景,需要事先启动好更多的Nginx进程,以保证快速响应并处理大量并发用户的请求。
  • 这类似于开饭店,在营业前,需要事先招聘一定数量的服务员准备接待顾客,但这里就有一个问题,如果饭店对客流没有正确预估,就会导致一些问题发生,例如:服务员人数招聘多了,但是客流很少,那么服务员就可能很闲,没事干,饭店的成本也高了;如果客流很大,而服务员人数少了,可能就接待不过来顾客,导致顾客吃饭体验差。因此,饭店要根据客户的流量及并发量来调整接待的服务人员数量,然后根据检测顾客量变化及时调整到最佳配置。
  • Nginx服务就相当于饭店,网站用户就相当于顾客,Nginx的进程就相当于服务员,下面就来优化Nginx进程的个数。

2.1.1 优化Nginx进程对应的配置

#优化Nginx进程对应Nginx服务的配置参数如下:
worker_processes 1;     #指定了Nginx要开启的进程数,结尾数字就是进程个数

上述参数调整的是Nginx服务的worker进程数,Nginx有Master进程和worker进程之分,Master为管理进程,真正接待“顾客”的是worker进程。

2.1.2 优化Nginx进程个数的策略

  • 前面已经讲解过,worker_processes参数大小的设置最好和网站的用户数量相关联,可如果是新配置,不知道网站的用户数量该怎么办呢?
  • 搭建服务器时,worker进程数最开始的设置可以等于CPU的核数,且worker进程数要多一些,这样起始提供服务时就不会出现因为访问量快速增加而临时启动新进程提供服务的问题,缩短了系统的瞬时开销和提供服务的时间,提升了服务用户的速度。高流量高并发场合也可以考虑将进程数提高至CPU核数*2,具体情况要根据实际的业务来选择,因为这个参数除了要和CPU核数匹配外,也和硬盘存储的数据及系统的负载有关,设置为CPU的核数是一个好的起始配置,这也是官方的建议。

2.1.3 查看Web服务器CPU硬件资源信息

下面介绍查看Linux服务器CPU总核数的方法:

(1)通过/proc/cpuinfo可查看CPU个数及总核数。查看CPU总核数的示例如下:

[root@LNMP ~]# grep processor /proc/cpuinfo 
processor	: 0
processor	: 1
processor	: 2
processor	: 3
[root@LNMP ~]# grep processor /proc/cpuinfo | wc -l
4               #表示为1颗CPU四核
[root@LNMP ~]# grep -c processor /proc/cpuinfo
4               #表示为1颗CPU四核

#查看CPU总颗数示例如下:
[root@LNMP ~]# grep "physical id" /proc/cpuinfo 
physical id	: 0     #物理ID一致,同一颗CPU
physical id	: 0     #物理ID一致,同一颗CPU
physical id	: 0     #物理ID一致,同一颗CPU
physical id	: 0     #物理ID一致,同一颗CPU
[root@LNMP ~]# grep "physical id" /proc/cpuinfo | sort | uniq | wc -l
1               #去重复,表示1颗CPU

(2)通过执行top命令,然后按数字1,即可显示所有的CPU核数,如下:

QQ截图20170827152136.png-52.9kB

2.1.4 实践修改Nginx配置

假设服务器的CPU颗数为1颗,核数为4核,则初始的配置可通过查看默认的nginx.conf里的worker_processes数来了解,命令如下:

[root@LNMP ~]# grep worker_processes /usr/local/nginx/conf/nginx.conf
worker_processes  1;
[root@LNMP ~]# sed -i 's#worker_processes  1#worker_processes  4#' /usr/local/nginx/conf/nginx.conf
[root@LNMP ~]# grep worker_processes /usr/local/nginx/conf/nginx.conf
worker_processes  4;        #提示可以通过vi修改

#优雅重启Nginx,使修改生效,如下:
[root@LNMP ~]# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@LNMP ~]# /usr/local/nginx/sbin/nginx -s reload

#现在检查修改后的worker进程数量,如下:
[root@LNMP ~]# ps -ef | grep "nginx" | grep -v grep
root       1110      1  0 11:12 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nginx      1429   1110  0 11:33 ?        00:00:00 nginx: worker process      
nginx      1430   1110  0 11:33 ?        00:00:00 nginx: worker process      
nginx      1431   1110  0 11:33 ?        00:00:00 nginx: worker process      
nginx      1432   1110  0 11:33 ?        00:00:00 nginx: worker process      

从“worker_processes 4”可知,worker进程数为4个。Nginx Master主进程不包含在这个参数内,Nginx Master的主进程为管理进程,负责调度和管理worker进程。

有关worker_processes参数的官方说明如下:

syntax:         worker_processes number;    #此行为参数语法,number为数量
default:        worker_processes 1;     #此行意思是不匹配该参数,软件默认情况数量为1
context:        main;   #此行为worker_processes参数可以放置的位置

worker_processes为定义worker进程数的数量,建议设置为CPU的核数或CPU核数*2,具体情况要根据实际的业务来选择,因为这个参数,除了要和CPU核数匹配外,和硬盘存储的数据以系统的负载也有关,设置为CPU的个数或核数是一个好的起始配置。From:http://nginx.org/en/docs/ngx_core_module.html

2.2 优化绑定不同的Nginx进程到不同的CPU上

  • 默认情况下,Nginx的多个进程有可能跑在某一个CPU或CPU的某一核上,导致Nginx进程使用硬件的资源不均,本节的优化是尽可能地分配不同的Nginx进程给不同的CPU处理,达到充分有效利用硬件的多CPU多核资源的目的。
  • 在优化不同的Nginx进程对应不同的CPU配置时,四核CPU服务器的参数配置参考如下:
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;

#worker_cpu_affinity就是配置Nginx进程与CPU亲和力的参数,即把不同的进程分给不同的CPU处理。这里0001 0010 0100 1000是掩码,分别代表第1,2,3,4核CPU,由于worker_processes进程数为4,因此,上述配置会把每个进程分配一核CPU处理,默认情况下进程不会绑定任何CPU,参数位置为main段。

四核和八核CPU服务器的参数配置参考如下:

#八核掩码
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

#四核掩码
worker_cpu_affinity 0001 0010 0100 1000;

worker_cpu_affinity 的作用是绑定不同的worker进程数到一组CPU上。通过设置bitmask控制进程允许使用的CPU,默认worker进程不会绑定到任何CPU(自动平均分配。)

2.2.1 实验环境准备

主机名IP地址备注
Nginx 192.168.0.220 nginxWeb
测试机1 192.168.0.240 Webbench压力测试
测试机2 192.168.0.245 Webbench压力测试
#安装webbench
tar xf webbench-1.5.tar.gz 
cd webbench-1.5
mkdir /usr/local/man
make install clean
which webbench.

虚拟机开启4核心

QQ截图20170827184049.png-7.5kB

2.2.2 第一步:不绑定worker进程进行压力测试

#配置文件如下:(未绑定worker进程)
[root@LNMP nginx]# cat conf/nginx.conf
worker_processes  4;
#worker_cpu_affinity 0001 0010 0100 1000;
events {
    worker_connections  10240;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  bbs.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

#在NginxWeb上执行如下命令:
[root@LNMP nginx]# top -u nginx
Tasks: 120 total,   1 running, 119 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.2%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1004412k total,   911632k used,    92780k free,     6952k buffers
Swap:  2031608k total,        0k used,  2031608k free,   749976k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                 
  1454 nginx     20   0 49240 5640  468 S  0.0  0.6   0:00.00 nginx                                                                                    
  1455 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx                                                                                    
  1456 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx                                                                                    
  1457 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx  
  

#在以上界面时按键盘的数值1键,出现如下界面:
top - 14:44:46 up 36 min,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 120 total,   1 running, 119 sleeping,   0 stopped,   0 zombie
Cpu0  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1004412k total,   911384k used,    93028k free,     6960k buffers
Swap:  2031608k total,        0k used,  2031608k free,   749976k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                                 
  1454 nginx     20   0 49240 5640  468 S  0.0  0.6   0:00.00 nginx                                                                                    
  1455 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx                                                                                    
  1456 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx                                                                                    
  1457 nginx     20   0 49240 5672  500 S  0.0  0.6   0:00.00 nginx      

在另外的两台测试机器上同时进行压力测试,命令如下:
webbench -c 2000 -t 60 http://192.168.0.220/

结果如下:

QQ截图20170827185601.png-36.8kB

2.2.3 第二步:绑定worker进程进行压力测试

#配置文件如下(绑定worker进程)
[root@LNMP nginx]# cat conf/nginx.conf
worker_processes  4;
worker_cpu_affinity 0001 0010 0100 1000;    #修改本行
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  bbs.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

在另外的两台测试机器上同时进行压力测试,命令如下:
webbench -c 2000 -t 60 http://192.168.0.220/

结果如下:

QQ截图20170827190217.png-32.6kB

根据图示,我们基本可以看出,平均绑定worker进程和不绑定的实验效果基本是一致的(CPU0是默认会被使用的)。原因在nginx在经过不断的优化后,会自动对worker进程进行动态的平均分配。

2.2.4 第三步:修改nginx配置,将所有worker进程绑定到CPU3上

#配置文件如下所示:
[root@LNMP nginx]# cat conf/nginx.conf
worker_processes  4;
worker_cpu_affinity 1000 1000 1000 1000;    #修改本行
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  bbs.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

在另外的两台测试机器上同时进行压力测试,命令如下:
webbench -c 2000 -t 60 http://192.168.0.220/

结果如下:

QQ截图20170827190657.png-20.5kB

从上图我们可以得知,worker进程的压力被集中分配到了CPU3上。(CPU0是默认被使用的)

2.3 Nginx事件处理模型优化

  • Nginx的连接处理机制在不同的操作系统会采用不同的额I/O模型,在Linux下,Nginx使用epoll的I/O多路复用模型,在Freebsd中使用kqueue的I/O多路复用模型,在Solaris中使用/dev/poll方式的I/O多路复用模型,在Windows中使用的是icop,等等。
  • 要根据系统类型选择不同的事件处理模型,可供使用的选择有“use [kqueue|rtsig|epoll|/dev/poll|select|poll];”。因为教学使用的是CentOS 6.5 Linux,因此将Nginx的事件处理模型调整为epoll模型。
#具体的配置参数如下:
events      #events指令是设定Nginx的工作模式及连接数上限
{
    use epoll;     #use是一个事件模块指令,用来指定Nginx的工作模式。Nginx支持的工作模式有select,poll,kqueue,epoll,rtsig和/dev/poll。其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,而kqueue用在BSD系统中。对于Linux系统Linux2.6+内核,推荐选择epoll工作模式,这是高性能高并发的设置
}

根据Nginx官方文档建议,也可以不指定事件处理模型,Nginx会自动选择最佳的事件处理模型服务。
对于使用连接进程的方法,通常不需要进行任何设置,Nginx会自动选择最有效办法。

2.4 调整Nginx单个进程允许的客户端最大连接数

接下来,调整Nginx单个进程允许的客户端最大连接数,这个控制连接数的参数为work_connections。
worker_connections的值要根据具体服务器性能和程序的内存使用量来指定(一个进程启动使用的内存根据程序确定),如下:

events  #events指令是设定Nginx的工作模式和连接数上线
{
    worker_connections 20480;
    #worker_connections也是个事件模块指令,用于定义Nginx每个进程的最大连接数,默认是1024.最大客户端连接数由worker_processes和worker_connections决定,即Max_client=  worker_processes*worker_connections。进程的最大连接数受Linux系统进程的最大打开文件数限制,在执行操作系统命令“ulimit   -HSn    65535”或配置相应文件后,worker_connections的设置才能生效。
}

下面是worker_connections的官方说明

参数语法:worker_connections number
默认配置:worker_connections 512
放置位置:events

说明:

worker_connections用来设置一个worker process支持的最大并发连接数,这个连接数包括了所有链接,例如:代理服务器的连接,客户端的连接等,实际的并发连接数除了受worker_connections参数控制外,还和最大打开文件数worker_rlimit_nofile有关(见下文),Nginx总并发连接=worker数量*worker_connections。
参考资料:http://nginx.org/en/docs/ngx_core_module.html

2.5 配置Nginx worker进程最大打开文件数

接下来,调整配置Nginx worker进程的最大打开文件数,这个控制连接数的参数为worker_rlimit_nofile。该参数的实际配置如下:

worker_rlimit_nofile 65535;
#最大打开文件数,可设置为系统优化后的ulimit     -HSn的结果

下面是worker_rlimit_nofile number的官方说明:

参数语法:worker_rlimit_nofile number
默认配置:无
放置位置:主标签段

说明:此参数的作用是改变worker processes能打开的最大文件数
参考资料:http://nginx.org/en/docs/ngx_core_module.html

备注:
Linux系统文件最大打开数设置:ulimit -n 65535

2.5.1 实验环境准备

主机名IP地址备注
Nginx 192.168.0.220 nginxWeb
测试机1 192.168.0.240 Webbench压力测试
测试机2 192.168.0.245 Webbench压力测试

2.5.2 修改nginx.conf配置文件

[root@LNMP nginx]# cat conf/nginx.conf
worker_processes  1;
#worker_cpu_affinity 0000 0010 0100 1000;
#worker_rlimit_nofile 65535;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  bbs.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

先定1核1024连接数。同学们注意多开几个虚拟机进行压力测试。不然的话,web服务器还没出问题,你测试服务器先down掉了。

2.5.2 测试nginx服务连接数的极值

#使用这个命令可以抓取nginx的连接数
[root@LNMP nginx]# netstat -antp | grep nginx | wc -l
554
[root@LNMP nginx]# netstat -antp | grep nginx | wc -l
471

逐渐提高压力,抓连接数,看看nginx啥时候down

2.6 开启高效文件传输模式

(1)设置参数:sendfile on;

sendfile参数用于开启文件的高效传输模式。同时将tcp_nopush和tcp_nodelay两个指令设置为on,可防止网络及磁盘I/O阻塞,提升Nginx工作效率。

#sendfile参数的官方说明如下:
syntax:     sendfile on | off;   #参数语法
default:    sendfile off;        #参数默认大小
context:    http,server,location,if in location #可以放置的标签段

参数作用:
激活或禁用sendfile()功能功能。sendfile()是作用于两个文件描述符之间的数据拷贝函数,这个拷贝操作是在内核之中的,被称为“零拷贝”,sendfile()比read和write函数要高效很多,因为,read和write函数要把数据拷贝到应用层再进行操作。相关控制参数还有sendfile_max_chunk,同学们可以执行查询。细节见http://nginx.org/en/docs/http/ngx_http_core_module.html#sendfile

(2)设置参数:tcp_nopush on;

#tcp_nopush参数的官方说明如下:
syntax:     tcp_nopush on | off;    #参数语法
default:    tcp_nopush off;         #参数默认大小
context:    http,server,location    #可以放置的标签段

参数作用:
激活或禁用Linux上的TCP_CORK socket选项,此选项仅仅当开启sendfile时才生效,激活这个tcp_nopush参数可以允许把http response header和文件的开始部分放在一个文件里发布,其积极的作用是减少网络报文段的数量。细节见http://nginx.org/en/docs/http/ngx_http_core_module.html。

2.7 优化Nginx连接参数,调整连接超时时间

2.7.1 什么是连接超时

  • 先来个比喻吧,某饭店请了服务员招待顾客,但是现在饭店不景气,此时,为多余的服务员发工资使得成本被提高,想减少饭店开支成本就得解雇服务员。
  • 这里的服务员就相当于Nginx服务建立的连接,当服务器建立的连接没有接收处理请求时,可在指定的时间内就让它超时自动退出。还有当Nginx和FastCGI服务建立连接请求PHP时,如果因为一些原因(负载高,停止响应),FastCGI服务无法给Nginx返回数据,此时可以通过配置Nginx服务参数使其不会死等,因为前面用过户还等着它返回数据呢,例如,可设置为如果请求FastCGI 10秒内不能返回数据,那么Nginx就中断本次请求,向用户汇报取不到数据的错误。

2.7.2 连接超时的作用

  • 将无用的连接设置为尽快超时,可以保护服务器的系统资源(CPU,内存,磁盘)。
  • 当连接很多时,及时断掉那些已经建立好的但又长时间不做事的连接,以减少其占用的服务器资源,因为服务器维护连接也是消耗资源的。
  • 有时黑客或恶意用户攻击网站,就会不断地和服务器建立多个连接,消耗连接数,但是啥也不干,大量消耗服务器的资源,此时就应该及时断掉这些恶意占用资源的连接。
  • LNMP环境中,如果用户请求了动态服务,则Nginx就会建立连接,请求FastCGI服务以及后端MySQL服务,此时这个Nginx连接就要设定一个超时时间,在用户容忍的时间内返回数据,或者再多等一会儿后端服务返回数据,具体的策略要根据具体业务进行具体分析。当然了,后端的FastCGI服务及MySQL服务也有对连接的超时控制。

简单的说,连接超时是服务的一种自我管理,自我保护的重要机制。

2.7.3 连接超时带来的问题,以及不同程序连接设定知识

  • 服务器建立新连接也是要消耗资源的,因此,超时设置得太短而并发很大,就会导致服务器瞬间无法响应用户的请求,导致用户体验下降。
  • 企业生产有些PHP程序站点会希望设置成短连接,因为PHP程序建立连接消耗的资源和时间相对要少些。而对于Java程序站点来说,一般建议设置长连接,因为Java程序建立连接消耗的资源和时间更多,这是语言运行机制决定的。

2.7.4 Nginx连接超时的参数设置

(1)设置参数:keepalive_timeout 60;

用于设置客户端连接保持会话的超时时间为60秒。超过这个时间,服务器会关闭该连接,此数值为参考值。

keepalive_timeout参数的官方说明如下:
syntax: keepalive_timeout  timeout [header_timeout] #参数语法
default: keepalive_timeout 75s;     #参数默认大小
context: http,serverr,location      #可以放置的标签段

参数作用:

  • keep-alive可以使客户端到服务器端已经建立的连接一直工作不退出,当服务器有持续请求时,keep-alive会使用已经建立的连接提供服务,从而避免服务器重新建立新连接处理请求。
  • 此参数设置一个keep-alive(客户端连接在服务器端保持多久后退出),其单位是秒,和HTTP响应header域的“Keep-Alive:timeout=time”参数有关,这些header信息也会被客户端浏览器识别并处理,不过有些客户端并不能按照服务器端的设置来处理,例如:MSIE大约60秒后会关闭keep-alive连接。细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html

(2)设置参数:tcp_nodelay on;

用于激活tcp_ondelay功能,提高I/O性能。

#tcp_nodelay参数的官方说明如下:
syntax:     tcp_nodelay on | off    #参数语法
default:    tcp_nodelay on;         #参数默认大小
context:    http,server,location    #可以放置的标签段

参数作用:
默认情况下当数据发送时,内核并不会马上发送,可能会等待更多的字节组成一个数据包,这样可以提高I/O性能。但是,在每次只发送很少字节的业务场景中,使用tcp_nodelay功能,等待时间会比较长。
参数生效条件:
激活或禁用TCP_NODELAY选项,当一个连接进入keep-alive状态时生效。细节见http://nginx.org/en/docs/http/ngx_http_core_module.html。

(3)设置参数:client_header_timeout 15;

用于设置读取客户端请求头数据的超时时间。此处的数值15,其单位是秒,为经验参考值。

#client_header_timeout参数的官方说明如下:
syntax:     client_header_timeout time; #参数语法
default:    client_header_timeout 60s;  #参数默认大小
context:    http,server         #可以放置的标签段

参数作用:
设置读取客户端请求头数据的超时时间。如果超过这个时间,客户端还没有发送完整的header数据,服务器端将返回“Request time out (408)”错误,可指定一个超时时间,防止客户端利用http协议进行攻击。细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html。

(4)设置参数:client_body_timeout 15;

用于设置读取客户端请求主体的超时时间,默认值60

#client_body_timeout参数的官方说明如下:
syntax:     client_body_timeout time;   #参数语法
default:    client_body_timeout 60s;    #默认60
context:    http,server,location    #可以放置的标签段

参数作用:
设置读取客户端请求主体的超时时间。这个超时仅仅为两次成功的读取操作之间的一个超时,非请求整个主体数据的超时时间,如果在这个超时时间内,客户端没有发送任何数据,Nginx将返回“Request time out(408)”错误,默认值60,细节见:http://nginx.org/en/docs/http/ngx_http_core_module.html

(5)设置参数:send_timeout 25;

用于指定响应客户端的超时时间。这个超时仅限于两个连接活动之间的时间,如果超过这个时间,客户端没有任何活动,Nginx将会关闭连接,默认值为60秒,可以改为参考值25秒。

#send_timeout参数的官方说明如下:
syntax:     send_timeout    time;   #参数语法
default:    send_timeout    60s;    #默认值60
context:    http,server,location    #可以放置的标签段

参数作用:
设置服务器端传送HTTP响应信息到客户端的超时时间,这个超时仅仅为两次成功握手后的一个超时,非请求整个响应数据的超时时间,如在这个超时时间内,客户端没有接收任何数据,连接将被关闭。细节见http://nginx.org/en/docs/http/ngx_http_module.html。

QQ截图20170827232141.png-614kB

备注:结合http原理画图讲解超时参数

2.8 上传文件大小的限制(动态应用)

下面我们学习如何调整上传文件的大小(http Request body size)限制。

#首先,我们可以在Nginx主配置文件里加入如下参数:
client_max_body_size 8m;

#具体大小根据公司的业务做调整,如果不清楚就先设置为8m把,有关客户端请求主体的解释在HTTP原理一节已经解释过了,一般情况下,HTTP的post方法在提交数据时才会携带请求主体信息。
#client_max_body_size参数的官方说明如下:
syntax:     client_max_body_size size;  #参数语法
default:    client_max_body_size 1m;    #默认值1m
context:   http,server,location        #可以放置的1标签段

参数作用:
设置最大的允许的客户端请求主体大小,在请求头域有“Content-Length”,如果超过了此配置值,客户端会受到413错误,意思是请求的条目过大,有可能浏览器不能正确显示。设置为0表示禁止检查客户端请求主体大小。此参数对提高服务器端的安全性有一定作用。细节见http://nginx.org/en/docs/http/ngx_http_core_module.html

2.9 FastCGI相关参数调优(配合PHP引擎动态服务)

FastCGI参数是配合Nginx向后请求PHP动态引擎服务的相关参数。Nginx FastCGI工作的逻辑图如下图所示:

QQ截图20170828004930.png-77.1kB

此处讲解的参数均为Nginx FastCGI客户端向后请求PHP动态引擎服务(php-fpm(FastCGI服务器端))的相关参数,属于Nginx的配置参数。下表是Nginx FastCGi常见参数的说明。

QQ截图20170828005612.png-445.9kB

QQ截图20170828005628.png-208.8kB

FastCGI Cache资料见http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_cache。

FastCGI常见参数的Nginx配置示例如下:

[root@LNMP nginx]# cat /usr/local/nginx/conf/nginx.conf
worker_processes  4;
worker_cpu_affinity 0001 0010 0100 1000;
worker_rlimit_nofile 65535;
user nginx;
events {
    use epoll;
    worker_connections  10240;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    tcp_nopush	    on;
    keepalive_timeout  65;
    tcp_nodelay     on;
    client_header_timeout 15;
    client_body_timeout   15;
    send_timeout          15;
    log_format main '$remote_addr - $remote_user [$time_local] "$request"'
    '$status $body_bytes_sent "$http_referer"'
    '"$http_user_agent""$http_x_forwarded_for"';
    server_tokens off;
    fastcgi_connect_timeout 240;        #Nginx允许fcgi连接超时时间
    fastcgi_send_timeout 240;           #Nginx允许fcgi返回数据的超时时间
    fastcgi_read_timeout 240;           #Nginx读取fcgi响应信息的超时时间
    fastcgi_buffer_size 64k;            #Nginx读取响应信息的缓冲区大小
    fastcgi_buffers 4 64k;              #指定Nginx缓冲区的数量和大小
    fastcgi_busy_buffers_size 128k;     #当系统繁忙时buffer的大小
    fastcgi_temp_file_write_size 128k;  #Nginx临时文件的大小
#   fastcgi_temp_path 	/data/ngx_fcgi_tmp; #指定Nginx临时文件放置路径
    fastcgi_cache_path 	/data/ngx_fcgi_cache	levels=2:2	keys_zone=ngx_fcgi_cache:512m	inactive=1d;    #指定Nginx缓存放置路径
    
# web

    server {
        listen       80;
        server_name  www.yunjisuan.com;
	location / {
		root html;
		index index.php index.html index.htm;
		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;
		}
	}
	access_log	logs/web_www_access.log 	main;
        location ~ .*\.(php|php5)?$ {
            	root   html;
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		include fastcgi.conf;
		fastcgi_cache ngx_fcgi_cache;       #开启fcgi缓存并起名叫ngx_fcgi_cache,很重要,有效降低CPU负载,并且防止502错误发生。
		fastcgi_cache_valid 200 302 1h; #指定应答代码的缓存时间,1h=1小时
		fastcgi_cache_valid 301 1d;     #1d=1天
		fastcgi_cache_valid any 1m;     #and 1m:将其他应答缓存1分钟
		fastcgi_cache_min_uses 1;       #待缓存内容至少要被用户请求过1次
		fastcgi_cache_use_stale error timeout invalid_header http_500;  #当遇到error,timeout,或者返回码500时,启用过期缓存返回用户(返回过期也比返回错误强)
#		fastcgi_cache_key 	http://$host$request_uri;   
		
        }
    }
    upstream	www_yunjisuan {

	server 192.168.0.225:8000 weight=1;

    }
    server {
		
	listen 8000;
	server_name www.yunjisuan.com;
	location / {
#		root	html;
		proxy_pass 	 	http://www_yunjisuan;
		proxy_set_header	host	$host;
		proxy_set_header	x-forwarded-for	$remote_addr;

	}
	access_log	logs/proxy_www_access.log	main;
    }
}

QQ截图20170829203733.png-161.9kB

Nginx的FastCGI的相关参数和反向代理proxy的相关参数非常接近,同学们可以拿来比对,一起理解。

2.10 配置Nginx gzip压缩实现性能优化

2.10.1 Nginx gzip压缩功能介绍

Nginx gzip压缩模块提供了压缩文件内容的功能,用户请求的内容在发送到用户客户端之前,Nginx服务器会根据一些具体的策略实施压缩,以节约网站出口带宽,同时加快数据传输效率,来提升用户访问体验。

2.10.2 Nginx gzip压缩的优点

  • 提升网站用户体验:发送给用户的内容小了,用户访问单位大小的页面就加快了,用户体验提升了,网站口碑就好了。
  • 节约网站带宽成本:数据是压缩传输的,因此节省了网站的带宽流量成本,不过压缩时会稍微消耗一些CPU资源,这个一般可以忽略。

此功能既能提升用户体验,又能使公司少花钱,一举多得。对于几乎所有的Web服务来说,这是一个非常重要的功能,Apache服务也有此功能。

2.10.3 需要和不需要压缩的对象

  • 纯文本内容压缩比很高,因此,纯文本的内容最好进行压缩,例如:html,js,css,xml,shtml等格式的文件。
  • 被压缩的纯文本文件必须要大于1KB,由于压缩算法的特殊原因,极小的文件压缩后可能反而变大。
  • 图片,视频(流媒体)等文件尽量不要压缩,因为这些文件大多都是经过压缩的,如果再压缩很可能不会减少或减少很少,或者有可能增大,同时压缩时还会消耗大量的CPU,内存资源。

2.10.4 参数介绍及配置说明

此压缩功能与早期Apache服务的mod_deflate压缩功能很相似,Nginx的gzip压缩功能依赖于ngx_http_gzip_module模块,默认已安装。

对应的压缩参数说明如下:

#######压缩的配置介绍######
gzip on;
#开启gzip压缩功能
gzip_min_length 1k;
#设置允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值0,表示不管页面多大都进行压缩。建议设置成大于1K,如果小于1K可能会越压越大。
gzip_buffers 4 16K;
#压缩缓冲区大小。表示申请4个单位为16K的内存作为压缩结果流缓存,默认值是申请与原始数据大小相同的内存空间来存储gzip压缩结果。
gzip_http_version 1.1;
#压缩版本(默认1.1,前端为squid2.5时使用1.0),用于设置识别HTTP协议版本,默认是1.1,目前大部分浏览器已经支持GZIP解压,使用默认即可。
gzip_comp_level 2;
#压缩比率。用来指定gzip压缩比,1压缩比最小,处理速度最快;9压缩比最大,传输速度快,但处理最慢,也比较消耗CPU资源。
gzip_types text/plain application/x-javascript text/css application/xml;
#用来指定压缩的类型,“text/html”类型总是会被压缩,这个就是HTTP原理部分讲的媒体类型。
gzip_vary on;
#vary header支持。该选项可以让前端的缓存服务器缓存经过gzip压缩的页面,例如用Squid缓存经过Nginx压缩的数据。

不同的Nginx版本中,gzip_types的配置可能会有不同,上述配置示例适合Nginx 1.6.3。对应的文件类型,请查看安装目录下的mime.types文件。

更多官方资源请看http://nginx.org/en/docs/http/ngx_http_gzip_module.html

2.10.5 Nginx压缩配置效果检查

可通过火狐浏览器+firebug插件+yslow插件查看gzip压缩及expires缓存结果。提前安装好yslow插件,开启监控,然后打开LNMP时安装的博客地址,就可以看到如下图所示的压缩结果:

QQ截图20170830002722.png-456kB

2.10.6 重要的前端网站调试工具介绍

常见的前端网站调试工具有如下几种:

  • Google浏览器(Chrome):通过该浏览器直接按F12键即可查看压缩及缓存结果,另外,谷歌浏览器(Chrome)上也可以直接安装yslow插件
  • 火狐浏览器:在该浏览器上安装firebug,yslow,即可进行调试(火狐要用老版本比如V28)
  • IE浏览器:在该浏览器上安装httpwatch即可进行调试(省略)

2.11 配置Nginx expires缓存实现性能优化

2.11.1 Nginx expires功能介绍

  • 简单说,Nginx expires的功能就是为用户访问的网站内容设定一个过期时间,当用户第一次访问这些内容时,会把这些内容存储在用户浏览器本地,这样用户第二次及以后继续访问该网站时,浏览器会检查加载已经缓存在用户浏览器本地的内容,就不会去服务器下载了,直到缓存的内容过期或被清除位置。
  • 更深入的理解:expires的功能就是允许通过Nginx配置文件控制HTTP的“Expires”和“Cache-Control”响应头部内容,告诉客户端浏览器是否缓存和缓存多久以内访问的内容。这个expires模块控制Nginx服务器应答时的expires头内容和Cache-Control头的max-age指令。缓存的有效期可以设置为相对于源文件的最后修改时刻或客户端的访问时刻。
  • 这些HTTP头向客户端表明了额内容的有效性和持久性。如果客户端本地有内容缓存,则内容就可以从缓存而不是从服务器中读取,然后客户端会检查缓存中的副本,看其是否过期或失效,以决定是否重新从服务器获得内容更新。

2.11.2 Nginx expires作用介绍

在网站的开发和运营中,视频,图片,CSS,JS等网站元素的更改机会较少,特别是图片,这时可以将图片设置在客户浏览器本地缓存365天或3650天,而将CSS,JS,html等代码缓存10~30天。这样用户第一次打开页面后,会在本地的浏览器按照过期日期缓存相应的内容,下次用户再打开类似的页面时,重复的元素就无需下载了,从而加快用户访问速度。用户的访问请求和数据减少了,也可节省大量的服务器端带宽。此功能同Apache的expires功能类似。

2.11.3 Nginx expires功能优点

  • expires可以降低网站的带宽,节约成本。
  • 加快用户访问网站的速度,提升用户访问体验。
  • 服务器访问量降低了,服务器压力就减轻了,服务器成本也会降低,甚至可以节约人力成本。
  • 对于几乎所有的Web服务来说,这是非常重要的功能之一,Apache服务也有此功能。

2.11.4 Nginx expires配置详解

前面已经介绍了expires的功能原理,接下来就来配置Nginx expires的功能。这里以location标签为例进行讲解,通过location URI规则将需要缓存的扩展名列出来,然后指定缓存时间。如果针对所有内容设置缓存,也可以不用location。Nginx默认安装了expires功能。

(1)根据文件扩展名进行判断,添加expires功能范例

范例1:

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
    expires 3650d;
}

该范例的意思是当用户访问网站URL结尾的文件扩展名为上述指定类型的额图片时,设置缓存3650天,即10年。

范例2:

location ~ .*\.(js|css)$
 {
    expires 30d;
 }

该范例的意思是当用户访问网站URL结尾的文件扩展名为js,css类型的元素时,设置缓存30天,即1个月。

(2)根据URL中的路径(目录)进行判断,添加expires功能范例

范例3:

location ~ ^/(images|javascript|js|css|flash|media|static)/
{
    expires 360d;
}

该范例的意思是当用过户访问网站URL中包含上述路径(例:images,js,css,这些在服务器端是程序目录)时,把访问的内容设置缓存360天,即1年。

2.11.5 Nginx expires配置效果检查

检查Nginx expires的方法和检查Nginx gzip的方法相同。
通过火狐浏览器加yslow插件查看gzip压缩及expires缓存结果时,要提前安装好火狐浏览器,并且要安装好yslow插件,开启监控,然后打开LNMP时安装的博客地址(带有图片,JS,CSS),就可以看到如下图所示的缓存结果了。

QQ截图20170830191330.png-11.7kB

在Linux客户端可通过如下curl命令查看图片URL的缓存header信息:

[root@localhost ~]# curl -I 192.168.0.220:8000
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 30 Aug 2017 02:15:54 GMT
Content-Type: text/html
Content-Length: 18
Connection: keep-alive
Last-Modified: Tue, 29 Aug 2017 19:37:44 GMT
ETag: "59a5c288-12"
Expires: Sat, 09 Sep 2017 02:15:54 GMT      #缓存的过期时间
Cache-Control: max-age=864000               #缓存的总时间,单位秒
Accept-Ranges: bytes

2.11.6 Nginx expires功能缺点及解决方法

  • 几乎所有的事物都是有两面性,没有十全十美的人和事。Nginx expires功能也不例外,虽然这个功能很好,但是也会给企业带来一些困惑。
  • 当网站被缓存的页面或数据更新了,此时用户端看到的可能还是旧的已经缓存的内容,这样就会影响用户体验,那么如何解决这个问题呢?解决方法如下。
  • 第一,对于经常需要变动的图片等文件,可以缩短对象缓存时间,例如:谷歌和百度的首页图片经常根据不同的日期换成一些节日的图,所以这里可以将这个图片设置为缓存期1天。
  • 第二,当网站改版或更新时,可以在服务器将缓存的对象改名(网站代码程序)。
  • 对于网站的图片,附件,一般不会被用户直接修改,用户层面上的修改图片,实际上是重新传到服务器,虽然内容一样但是是一个新的图片名了。
  • 网站改版升级会修改JS,CSS元素,若改版时对这些元素改了名,会使得前端的CDN及用户端需要重新缓存内容。

2.11.7 企业网站缓存日期曾经的案例参考

若企业的业务和网站访问量不同,那么网站的缓存期时间设置也是不同的,比如,如下企业所用的缓存日期就是不一样的。

  • 51CTO:1周
  • 新浪:15天
  • 京东:25年
  • 淘宝:10年

2.11.8 企业网站有可能不希望被缓存的内容

  • 广告图片,用于广告服务,都缓存了就不好控制展示了。
  • 网站流量统计工具(JS代码),都缓存了流量统计就不准了。
  • 更新很频繁的文件(google的logo),这个如果按天,缓存效果还是显著的。

三,Nginx日志相关优化与安全

3.1 编写脚本实现Nginx access日志轮询

当用户请求一个软件时,绝大多数软件都会记录用户的访问情况,Nginx服务也不例外。Nginx软件目前还没有类似Apache的通过cronolog或rotatelog对日志分割处理的功能,但是,运维人员可以利用脚本开发,Nginx的信号控制功能或reload重新加载,来实现日志的自动切割,轮询。

日志切割脚本如下:

[root@localhost nginx]# cat /server/scripts/cut_nginx_log.sh 
#!/bin/bash
#日志切割脚本可挂定时任务,每天00点整执行

Dateformat=`date +%Y%m%d`
Basedir="/usr/local/nginx"
Nginxlogdir="$Basedir/logs"
Logname="access"

[ -d $Nginxlogdir ] && cd $Nginxlogdir || exit 1
[ -f ${Logname}.log ] || exit 1
/bin/mv ${Logname}.log ${Dateformat}_${Logname}.log
$Basedir/sbin/nginx -s reload

[root@localhost nginx]# cat >>/var/spool/cron/root << KOF
#cut nginx access log by Mr.chen
00 00 * * * /bin/bash /server/scripts/cut_nginx_log.sh >/dev/null 2>&1

3.2 不记录不需要的访问日志

在实际工作中,对于负载均衡器健康节点检查或某些特定文件(比如图片,JS,CSS)的日志,一般不需要记录下来,因为在统计PV时是按照页面计算的,而且日志写入太频繁会消耗大量磁盘I/O,降低服务的性能。

具体配置方法如下:

location ~ .*\.(js|jpg|JPG|jpeg|JPEG|css|bmp|gif|GIF)$
{
    access_log  off;
}
#这里用location标签匹配不记录日志的元素扩展名,然后关闭日志

3.3 访问日志的权限设置

假如日志目录为/app/logs,则授权方法如下:

chown -R root.root /app/logs
chmod -R 700 /app/logs

不需要在日志目录上给Nginx用户读或写许可,但很多网友都没注意这个问题,他们把该权限直接给了Nginx或Apache用户,这就成为

四,Nginx站点目录及文件URL访问控制

4.1 根据扩展名限制程序和文件访问

  • Web2.0时代,绝大多数网站都是以用户为中心多的,例如:bbs,blog,sns产品,这几个产品都有一个共同特点,就是不但允许用户发布内容到服务器,还允许用户发图片甚至上传附件到服务器上,由于为用户开了上传功能,因此给服务器带来了很大的安全风险。虽然很多程序在上传前会着一定的控制,例如:文件大小,类型等,但是,一不小心就会被黑客钻了控制,上传了木马程序。
  • 下面将利用Nginx配置禁止访问上传资源目录下的PHP,Shell,Perl,Python程序文件,这样用户即使上传了木马文件也没法执行,从而加强了网站的安全。

范例1:配置Nginx,禁止解析指定目录下的指定程序。

location ~ ^/images/.*\.(php|php5|sh|pl|py)$
{
    deny all;
}

location ~ ^/static/.*\.(php|php5|sh|pl|py)$
{
    deny all;
}
location ~* ^/data/(attachment|avatar)/.*\.(php|php5)$
{
    deny all;
}
#对上述目录的限制必须写在Nginx处理PHP服务配置的前面,如下:
location ~ .*\.(php|php5)$
{
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fcgi.conf;
}

范例2:Nginx下配置禁止访问*.txt和*.doc文件。

实际配置信息如下:

location ~* \.(txt|doc)$
{
    if (-f $request_filename)
    {
        root /data/www/www;
        #rewrite ...可以重定向到某个URL
        break;
    }
}
location ~* \.(txt|doc)$
{
    root /data/www/www;
    deny all;
}

4.2 禁止访问指定目录下的所有文件和目录

范例1:配置禁止访问指定的单个或多个目录

#禁止访问单个目录的命令如下:
location ~ ^/static
{
    deny all;
}

#禁止访问多个目录的命令如下:
location ~ ^/(static|js)
{
    deny all;
}

范例2:禁止访问目录并返回指定的HTTP状态码,命令如下:

server 
{
    listen 80;
    server_name www.yunjisuan.com yunjisuan.com;
    root /data/www/www;
    index index.html index.htm;
    access_log logs/www_access.log commonlog;
    location /admin/
    {
        return 404;
    }
    location /tmplates/
    {
        return 403;
    }
}

作用:
禁止访问目录下的指定文件1,或者禁止访问指定目录下的所有内容。
最佳应用场景:
对于集群的共享存储,一般是存放静态资源文件,所以,可禁止执行指定扩展名的程序,例:.php,.sh,.pl,.py

4.3 限制网站来源IP访问

下面介绍如何使用ngx_http_access_module限制网站来源IP访问

案例环境:phpmyadmin数据库的Web客户端,内部开发人员用的。

范例1:禁止某目录让外界访问,但允许某IP访问该目录,且支持PHP解析,命令如下:

location ~ ^/yunjisuan/ 
{
    allow 202.111.12.211;
    deny all;
}

location ~ .*\.(php|php5)$
{
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index   index.php;
    include         fastcgi.conf;
}

范例2:限制指定IP或IP段访问,命令如下:

location / 
{
    deny    192.168.1.1;
    allow 192.168.1.0/24;
    allow 10.1.1.0/16;
    deny all;
}

参考资料:http://nginx.org/en/docs/http/ngx_http_access_module.html

企业问题案例: Nginx做反向代理的时候可以限制客户端IP吗?

答:可以,具体方法如下:

方法一:使用if来控制,命令如下:

if ($remote_addr = 10.0.0.7)
{
    return 403;
}

if ($remote_addr = 218.247.17.130)
{
    set $allow_access_root 'ture';      #我也不知道什么意思
}

方法二:利用deny和allow只允许IP访问,命令如下:

location / {
    root html/blog;
    index index.php index.html index.htm;
    allow 10.0.0.7;
    deny all;
}

方法三:只拒绝某些IP访问,命令如下:

location / {
    root html/blog;
    index index.php index.html index.htm;
    deny 10.0.0.7;
    allow all;
}

注意事项:

  • deny一定要加一个IP,否则会直接跳转到403,不再往下执行了,如果403默认页是在同一域名下,会造成死循环访问。
  • 对于allow的IP段,从允许访问的段位从小到大排列,如127.0.0.0/24的下面才能是10.10.0.0/16,其中:
  • 24表示子网掩码:255.255.255.0
  • 16表示子网掩码:255.255.0.0
  • 8表示子网掩码:255.0.0.0
  • 以deny all:结尾,表示除了上面允许的,其他的都禁止。如:
deny 192.168.1.1;
allow 127.0.0.0/24;
allow 192.168.0.0/16;
allow 10.10.0.0/16;
deny all;

4.4 配置Nginx,禁止非法域名解析访问企业网站

问题:Nginx如何防止用户IP访问网站(恶意域名解析,也相当于是直接IP访问企业网站)

方法一:让使用IP访问网站的用户,或者恶意解析域名的用过户,收到501错误,命令如下:

server {
    listen 80 default_server;
    server_name _;
    return 501;
}

#说明:直接报501错误,从用户体验上不是很好

方法二:通过301跳转到主页,命令如下:

server {
    listen 80 default_server;
    server_name _;
    rewrite ^(.*) http://www.yunjisuan.com/$1 permanent;
}

方法三:发现某域名恶意解析到公司的服务器IP,在server标签里添加以下代码即可,若有多个server则要多处添加。

if ($host ! ~ ^www\.yunjisuan\.com$)
{
    rewrite ^(.*) http://www.yunjisuan.com/$1 permanent;
}

#说明:代码含义为如果header信息的host主机名字非www.yunjisuan.com,就301跳转到www.yunjisuan.com

五,Nginx图片及目录防盗链解决方案

5.1 什么是资源盗链

简单的说,就是某些不法网站未经允许,通过在其自身网站程序里非法调用其他网站的资源,然后在自己的网站上显示这些调用的资源,达到填充自身网站的效果。这一举动不仅浪费了调用资源网站的网络流量,还造成其他网站的带宽及服务压力吃紧,甚至宕机。

下面通过示意图阐述资源被盗链原理,如下图所示:

QQ截图20170831214823.png-89.1kB

5.2 网站资源被盗链带来的问题

若网站图片及相关资源被盗链,最直接的影响就是网络带宽占用加大了,带宽费用多了,网络流量也可能忽高忽低,Nagios/Zabbix等报警服务频繁报警,类似下图所示:

QQ截图20170831215104.png-187.4kB

最严重的情况就是网站的资源被非法使用,使网站带宽成本加大和服务器压力加大,这有可能导致数万元的损失,且网站的正常用户访问也会受到影响。

5.3 企业真实案例:网站资源被盗链,出现严重问题

某日,接到从事运维工作的朋友的紧急求助,其公司的CDN源站,源站的流量没有变动,但CDN加速那边的流量无故超了好几个GB,不知道怎么处理。
该故障的影响:由于是购买的CDN网站加速服务,因此虽然流量多了几个GB,但是业务未受影响。只是,这么大的异常流量,持续下去可直接导致公司无故损失数万元。解决这个问题可体现运维的价值。

那么,这样的问题如何及时发现,又如何处理呢?

第一:对IDC及CDN带宽做监控报警
第二:作为高级运维或运维经理,每天上班的重要任务,就是经常查看网站流量图,关注流量变化,关注异常流量。
第三:对访问日志做分析,迅速定位异常流量,并且和公司市场推广等保持较好的沟通,以便调度带宽和服务器资源,确保网站正常的访问体验。

5.4 常见防盗链解决方案的基本原理

(1)根据HTTP referer实现防盗链

  • 在HTTP协议中,有一个表头字段叫referer,使用URL格式来表示是哪里的链接用了当前网页的资源。通过referer可以检测访问的来源网页,如果是资源文件,可以跟踪到显示它的网页地址,一旦检测出来源不是本站,马上进行阻止或返回指定的页面。
  • HTTP referer是header的一部分,当浏览器向Web服务器发送请求时,一般会带上referer,告诉服务器我是从哪个页面链接过来的,服务器借此获得一些信息用于处理。Apache,Nginx,Lighttpd三者都支持根据HTTP referer实现防盗链,referer是目前网站图片,附件,html等最常用的防盗链手段。下图是referer防盗链的基本原理图。

QQ截图20170901200511.png-157.1kB

(2)根据cookie防盗链

  • 对于一些特殊的业务数据,例如流媒体应用通过ActiveX显示的内容(例如,Flash,Windows Media视频,流媒体的RTSP协议等),因为他们不向服务器提供referer header,所以若采用上述的referer的防盗链手段,就达不到想要的效果。
  • 对于Flash,Windows Media视频这种占用流量较大的业务数据,防盗链是比较困难的,此时可以采用Cookie技术,解决Flash,Windows Media视频等的防盗链问题。

例如:ActiveX插件不传递referer,但会传递Cookie,可以在显示ActiveX的页面的<head></head>标签内嵌入一段JavaScript代码,设置“Cookie:Cache=av”如下:

<script>document.cookie="Cache=av;domain=domain.com;path=/";</script>

然后就可以通过各种手段来判断这个Cookie的存在,以及验证其值的操作了。
根据Cookie来防盗链的技术比较复杂,我们不在这里涉及,同学们如果感兴趣,或者企业确实需要,可通过网络来了解相关方法。

(3)通过加密变换访问路径实现防盗链

此种方法比较适合视频及下载类业务数据的网站。例如:Lighttpd有类似的插件mod_secdownload来实现此功能。先在服务器端配置此模块,设置一个固定用于加密的字符串,比如yunjisuan,然后设置一个url前缀,比如/mp4/,再设置一个过期时间,比如1小时,然后写一段PHP代码,利用加密字符串和系统时间等通过md5算法生成一个加密字符串。最终获取到的文件的URL链接中会带有一个时间戳和一个加密字符的MD5数值,在访问时系统会对这两个数据进行验证。如果时间不在预期的时间段内(如1小时内)则失效;如果时间戳符合条件,但是加密的字符串不符合条件也会失效,从而达到防盗链的效果。

5.5 Nginx Web服务实现防盗链实战

在默认情况下,只需要进行简单的配置,即可实现防盗链处理。请看下面的实例。

(1)利用referer,并且针对扩展名rewrite重定向

#下面的代码为利用referer且针对扩展名rewrite重定向,即实现防盗链的Nginx配置。
location ~* \.(jpg|gif|png|swf|flv|wma|wmv|asf|mp3|mmf|zip|rar)$
{
    valid_referers none blocked *.yunjisuan.com yunjisuan.com;  #
    if ($invalid_referer)
    {
        rewrite ^/ http://www.yunjisuan.com/img/nolink.jpg;
    }
}
#提示:要根据自己公司的实际业务(是否有外链的合作)进行域名设置。

(2)利用referer,并且针对站点目录过滤返回错误码

针对目录的方法如下:

location /images {
    root /data/www/www;
    valid_referers none blocked *.yunjisuan.com yunjisuan.com;
    if ($invalid_referer) {
        return403;
    }
}

在上面这段防盗链设置中,分别针对不同文件类型和不同的目录进行了设置,同学们可以根据自己需求进行类似设定。下面是上述代码的说明:

5.6 NginxHttpAccessKeyModule实现防盗链介绍

  • 如果不怕麻烦,有条件实现的话,推荐使用NginxHttpAccessKeyModule。
  • 其运行方式是:如download目录下有一个file.zip文件。对应的URI是http://www.abc.com/download/file.zip,使用ngx_http_accesskey_module 模块后就成了http://www.bac.com/download/file.zipkey=09093abeac094,只有正确地给定了key值,才能下载download目录下的file.zip,而且key值是与用户的IP相关的,这样就可以避免被盗链了。据说,现在NginxHttpAccessKeyModule连迅雷都可以防了,同学们执行尝试一下。

5.7 在产品设计上解决盗链方案

产品设计时,处理盗链问题可将计就计,为网站上传的图片增加水印。例如:下图就是为网站上传的图片增加的水印。

QQ截图20170901211858.png-23kB

为图片添加版权水印是很有效的方法。网站直接转载图片一般是为了快捷,但是对于有水印的图片,很多站长是不愿意转载的。

六,Nginx错误页面的优雅显示

6.1 生产环境常见的HTTP状态码列表

QQ截图20170901213041.png-357.1kB

6.2 为什么要配置错误页面优雅显示

在网站的运行过程中,可能因为页面不存在或系统过载等原因,导致网站无法正常响应用户的请求,此时Web服务会返回系统默认的错误码,或者很不友好的页面,如下图所示:

QQ截图20170901213712.png-32.9kB

我们可以将404,403等的错误信息页面重定向到网站首页或其他事先指定的页面,提升网站的用户访问体验。

范例1:对错误代码403实行本地页面跳转,命令如下:

server {
    listen 80;
    server_name     www.yunjisuan.com;
    location / {
        root html/www;
        index   index.html  index.htm;
    }
    error_page  403 /403.html;      #当出现403错误时,会跳转到403.html页面
}

#上面的/403.html是相对于站点根目录html/www的。

范例2:对错误代码404实行本地页面优雅显示,命令如下:

server {
    listen  80;
    server_name www.yunjisuan.com;
    location / {
        root    html/www;
        index   index.html  index.htm;
        error_page  404 /404.html;
        #当出现404错误时,会跳转到404.html页面
    }
}
#代码中的/404.html是相对于站点根目录html/www的

范例3: 50x页面放到本地单独目录下,进行优雅显示

error_page  500 502 503 504 /50x.html;
location = /50x.html {
    root    /data/www/html;
}
#这里指定单独的站点目录存放到50x.html文件中。

范例4: 错误状态码URL重定向,命令如下:

server {
    listen 80;
    server_name www.yunjisuan.com;
    location / {
        root    html/www;
        index   index.html  index.htm;
        error_page  404 http://bbs.yunjisuan.com;
        #当出现404错误时,会跳转到指定的URL http://bbs.yunjisuan.com页面显示给用户,这个URL一般是企业另外的可用地址。
        access_log  /usr/local/nginx/logs/bbs_access.log    commonlog;
    }
}
#代码中的/404.html是相对于站点根目录html/www的。

范例5: 将错误状态码重定向到一个location,命令如下:

location / {
    error_page  404 = @fallback;
}
location @fallback {
    proxy_pass  http://backend;
}

6.3 阿里门户网站天猫的Nginx优雅显示配置案例如下:

error_page  500 501 502 503 504 http://err.tmall.com/error2.html;
error_page  400 403 404 405 408 410 411 412 413 414 415 http://err.tmall.com/error1.html;

七,Nginx站点目录文件及目录权限优化

7.1 单机LNMP环境目录权限严格控制措施

为了保证网站不遭受木马入侵,所有站点目录的用户和组都应该为root,所有的目录权限是755;所有的文件权限是644.设置如下:

[root@LNMP nginx]# ls -l /usr/local/nginx/html/ | tail -5
-rw-r--r--.  1 root root  8048 Jan 11  2017 wp-mail.php
-rw-r--r--.  1 root root 16255 Apr  6 14:23 wp-settings.php
-rw-r--r--.  1 root root 29896 Oct 19  2016 wp-signup.php
-rw-r--r--.  1 root root  4513 Oct 14  2016 wp-trackback.php
-rw-r--r--.  1 root root  3065 Aug 31  2016 xmlrpc.php
  • 以上的权限设置可以防止黑客上传木马,以及修改站点文件,但是,合理的网站用户上传的内容也会被拒之门外。那么如何让合法的用户可以上传文件,而又不至于被黑客利用攻击呢?
  • 如果是单机的LNMP环境,站点目录和文件属性设置如下。
  • 先把所有的目录权限设置为755,所有的文件权限设置为644,所用的目录,以及文件用户和组都是root;然后把用户上传资源的目录权限设置为755,将用户和组设置为Nginx服务的用户;最后针对上传资源的目录做资源访问限制。
  • 部分公司所采用的授权方式不是很安全,常见的有如下两种:
chmod -R 777 /directory
chmod -R nginx.nginx /directory
  • 上述两种授权方法虽然不能说错误,但是没有做到授权最小化,会给网站带来非常大的安全隐患,特别是木马入侵的时候。
  • 在比较好的网站业务架构中,应把资源文件,包括用户上传的图片,附件等服务和程序服务分离,最好把上传程序服务也分离出来,这样就可以从容地按照之前所述进行安全授权了。

7.2 Nginx企业网站集群超级安全设置

结合Linux权限体系及Nginx大型集群架构进行配置,严格控制针对Nginx目录的访问才能降低网站被入侵的风险。比如,可根据下图中的企业集群架构逻辑图和不同角色提供的不同服务来严格控制不同服务器的Nginx目录权限。

QQ截图20170901234255.png-157.2kB

下图为集群架构中不同于前面Web业务的权限管理细化。

服务器角色权限处理安全系数
动态Web集群 目录权限755,文件权限644,所用目录,以及文件用户和组都是root。环境为Nginx+PHP 文件不能被改,目录不能被写入,安全系数10
static图片集群 目录权限755,文件权限644,所用的目录,以及文件用户和组都是root。环境为Nginx 文件不能被改,目录不能被写入,安全系数10
上传upload集群 目录权限755,文件权限644,所用的目录,以及文件用户和组都是root。特别:用户上传的目录设置为755,用户和组使用Nginx服务配置的用户 文件不能被改,目录不能被写入,但是用户上传的目录允许写入文件且需要通过Nginx的其他功能来禁止读文件,安全系数8

做到上述的设置后,网站服务在系统层面被入侵的风险就大大降低了。

八,Nginx防爬虫优化

8.1 robots.txt机器人协议常识

  • Robots协议(也称为爬虫协议,机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。
  • 我理解的是robots.txt是通过代码控制搜索引擎蜘蛛索引的一个手段,以便减轻网站服务器的带宽使用率,从而让网站的空间更稳定,同时也可以提高网站其他页面的索引效率,提高网站收录。
  • 我们只需要创建一个robots.txt文本文件,然后在文档内设置好代码,告诉搜索引擎我网站的哪些文件你不能访问。然后上传到网站根目录下面,因为当搜索引擎蜘蛛在索引一个网站时,会先爬行查看网站根目录下是否有robots.txt文件。

8.2 机器人协议起源

  • 2011年10月25日,京东商城正式将一淘网的搜索爬虫屏蔽,以防止一淘网对其内容进行抓取。

京东的robots.txt设置如下:
https://www.jd.com/robots.txt

QQ截图20170902000613.png-27.6kB

  • 2008年9月8日,淘宝网宣布封杀百度爬虫,百度忍痛遵守爬虫协议。因为一旦破坏协议,用户的隐私和利益就无法得到保障,搜索网站就谈不上人性关怀。

淘宝的robots.txt设置如下:
https://www.taobao.com/robots.txt

QQ截图20170902000647.png-41.3kB

  • 2012年8月,360综合搜索被指违反robots协议。

360的robots.txt设置如下:
http://www.360.cn/robots.txt

QQ截图20170902000824.png-27.4kB

8.3 Nginx防爬虫优化配置

我们可以根据客户端的user-agents信息,轻松地阻止指定的爬虫爬取我们的网站。下面来看几个案例。

范例1:阻止下载协议代理,命令如下:

##Block download agents##
if ($http_user_agent ~* LWP:Simple | BBBike | wget) 
{
    return 403;
}

#说明:如果用户匹配了if后面的客户端(例如wget),就返回403.

这里根据$http_user_agent获取客户端agent,然后判断是否允许或返回指定错误码。

范例2:添加内容防止N多爬虫代理访问网站,命令如下:

#这些爬虫代理使用“|”分隔,具体要处理的爬虫可以根据需求增加或减少,添加的内容如下:
if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot-Modile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Yahoo! SSlurp  China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot")
{
    return 403;
}

范例3:测试禁止不同的浏览器软件访问

示例代码如下:

if ($http_user_agent ~* "Firefox|MSIE")
{
    rewrite ^(.*) http://www.yunjisuan.com/$1 permanent;
}

#如果浏览器为Firefox或IE,就会跳转到http://www.yunjisuan.com

九,利用Nginx限制HTTP的请求方法

在之前的HTTP协议原理一节中,讲解了很多HTTP方法,其中最常用的HTTP方法为GET,POST,我们可以通过Nginx限制HTTP请求的方法来达到提升服务器安全的目的,例如,让HTTP只能使用GET,HEAD和POST方法的配置如下:

#Only allow these request methods
if ($request_method ! ~ ^(GET|HEAD|POST)$)
{
    return 501;
}

当上传服务器上传数据到存储服务器时,用户上传写入的目录就不得不给Nginx对应的用户相关权限,这样一旦程序有漏洞,木马就有可能被上传到服务器挂载的对应存储服务器的目录里,虽然我们也做了禁止PHP,SH,PL,PY等扩展名的解析限制,但还是会遗漏一些想不到的可执行文件。对于这样情况,该怎么办呢?事实上,还可以通过限制上传服务器的Web服务(可以具体到文件)使用GET方法,防止用户通过上传服务器访问存储内容,让访问存储渠道只能从静态或图片服务器入口进入。例如,在上传服务器上限制HTTP的GET方法的配置如下:

#Only deny GET request methods ##
if ($request_method ~* ^(GET)$)
{
    return 501;
}
#提示:还可以加一层location,更具体地限制文件名

实际效果如下图所示:

QQ截图20170902210551.png-30kB

十,使用CDN做网站内容加速

10.1 什么是CDN

  • CDN的全称是Content Delivery Network,中文意思是内容分发网络。简单地讲,通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的Cache服务器内,通过智能DNS负载均衡技术,判断用户的来源,让用户就近使用与服务器相同线路的带宽访问Cache服务器,取得所需的内容。例如:天津网通用户访问天津网通Cache服务器上的内容,北京电信访问北京电信Cache服务器上的内容。这样可以有效减少数据在网络上传输的时间,提高访问速度。
  • CDN是一套全国或全球的1分布式缓存集群,其实质是通过智能DNS判断用户的来源地域及上网线路,为用户选择一个最接近用户地域,以及和用户上网线路相同的服务器节点,因为地域近,且线路相同,所以,可以大幅提升用户浏览网站的体验。
  • CDN产生背景之一:BGP机房虽然可以提升用户体验,但是价格昂贵,对于用户来说,CDN的诞生可以提供比BGP机房更好的体验(让同一地区,同一线路的用户访问和当地同一线路的网站),BGP机房和普通机房有将近5~10倍的价格差。CDN多使用单线的机房,根据用户的线路及位置,为用户选择靠近用户的位置,以及相同的运营商线路,不但提升了用户体验,价格也降下来了。
  • [x] CDN的价值:
    • 为架设网站的企业省钱
    • 提升企业网站的用户访问体验(相同线路,相同地域,内存访问)
    • 可以阻挡大部分流量攻击,例如:DDOS攻击

10.2 CDN的特点

  • [x] CDN就是一个具备根据用户区域和线路智能调度的分布式内存缓存集群。其特点如下:
    • 通过服务器内存缓存网站数据,提高了企业站点(尤其含有大量图片,视频等的站点)的访问速度,并大大提高企业站点的稳定性(省钱且提升用户体验)。
    • 用户根据智能DNS技术自动选择最适合的Cache服务器,降低了不同运营商之间互联瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量。
    • 加快了访问速度,减少了原站点的带宽
    • 用户访问时从服务器的内存中读取数据,分担了网络流量,同时减轻了原站点负载压力等。
    • 使用CDN可以分担源站的网络流量,同时可以减轻原站点的负载压力,并降低黑客入侵及各种DDOS攻击对网站的影响,保证网站有较好的服务质量。

下面是通过curl命令访问163网站的header信息,可以看到163网站的首页就使用了CDN进行加速。

[root@LNMP logs]# curl -I www.163.com
HTTP/1.1 200 OK
Expires: Sat, 02 Sep 2017 13:53:55 GMT
Date: Sat, 02 Sep 2017 13:52:35 GMT
Server: nginx
Content-Type: text/html; charset=GBK
Transfer-Encoding: chunked
Vary: Accept-Encoding,User-Agent,Accept
Cache-Control: max-age=80
Age: 34
X-Via: 1.1 fzhwtxz25:6 (Cdn Cache Server V2.0), 1.1 wangtong42:0 (Cdn Cache Server V2.0)
Connection: keep-alive

上面的“(CdnCacheServerV2.0)”表示163首页使用了CDN加速。

10.3 企业使用CDN的基本要求

首先要说的是,不是所有的网站都可以一上来就能用CDN的。要加速的业务数据应该存在独立的域名,例如:img1-4.yunjisuan.com/video1-4.yunjisuan.com,业务内容图片,附件,JS,CSS等静态元素,这样的静态网站域名才可以使用CDN。

下面来看一个DNS解析范例。DNS服务器加速前的A记录如下:

;A records
img.yunjisuanl.com IN A 124.106.0.21 (企业服务器的IP)

#删除上面的记录,命令如下:
img.yunjisuanl.com IN A 124.106.0.21 (服务器的IP)

#然后,做下面的别名解析:
;CNAME records
img.yunjisuan.com IN CNAME bbs
img.yunjisuan.com 3M IN CNAME img.yunjisuan.com.cachecn.com.

提示:
这个img.yunjisuan.com.cachecn.com.地址必须是事先由CDN公司配置好的CDN公司的域名。国内较大的CDN提供商为网宿,蓝讯,快网。

十一,Nginx程序架构优化

解耦是开发人员中流行的一个名词,简单地说就是把一堆程序代码按照业务用途分开,然后提供服务,例如:注册登录,上传,下载,浏览列表,商品内容页面,订单支付等都应该是独立的程序服务,只不过在客户端看来是一个整体而已。如果中小公司做不到上述细致的解耦,起码也要让下面的几个程序模块独立。

  • 网页页面服务。(静态,动态页面)
  • 图片附件及下载服务。(upload)
  • 上传图片服务。(static)
  • 上述三者的功能尽量分离。分离的最佳方式是分别使用独立的服务器(需要改动程序),如果程序实在不易更改,次选方案是在前端负载均衡器Haproxy/Nginx上,根据URI(例如目录或扩展名)过滤请求,然后抛给后面对应的服务器。
  • 例如:根据扩展名分发,请求http://www.yunjisuan.com/a/b.jpg就应抛给图片服务器(独立的静态服务器最适合使用CDN);根据URL路径分发,请求http://www.yunjisuan.com/upload/index.php就应抛给上传服务器。不符合上面两个要求的,默认抛给Web服务器。

说明:可以部署3台服务器,人为分布请求服务器。当然了,这适合并发比较高,服务器较多的情况。程序架构分离了,效率,安全性都会提高很多。

十二,使用普通用户启动Nginx(监牢模式)

12.1 为什么要让Nginx服务使用普通用户

默认情况下,Nginx的Master进程使用的是root用户,worker进程使用的是Nginx指定的普通用过户,使用root用户跑Nginx的Master进程有两个最大的问题:

  • 管理权限必须是root,这就使得最小化分配权限原则遇到难题。
  • 使用root跑Nginx服务,一旦网站出现漏洞,用户就可以很容易地获得服务器的root权限。

因此,如果能有一种不用给开发人员,甚至普通运维人员管理员权限,就可以很好地管理Nginx服务的方法,具体内容将在下节为大家讲解。

12.2 给Nginx服务降权的解决方案

解决方案如下:

  • 给Nginx服务降权,用inca用户跑Nginx服务,给开发及运维设置普通账号,只要与inca同组即可管理Nginx,该方案解决了Nginx管理问题,防止root分配权限过大。
  • 开发人员使用普通账户即可管理Nginx服务及站点下的程序和日志。
  • 采取项目负责制度,即谁负责项目维护,出了问题就是谁负责。

很多公司开发和运维为root权限争得不可开交,甚至大打出手。
参考资料:到底要不要给开发人员管理服务器的权限?(http://down.51cto.com/data/844517).

12.3 给Nginx服务降权实战

本优化属架构优化(同样适合其他软件),通过Nginx启动命令的-c参数指定不同的Nginx配置文件,可以同时启动多个实例,并使用普通的用户运行服务。
Nginx安装后的启动命令路径为“/usr/local/nginx/sbin/nginx”,可以通过加-h参数查看相关参数的用法,命令及结果如下:

[root@LNMP logs]# /usr/local/nginx/sbin/nginx -h
nginx version: nginx/1.6.2
Usage: nginx [-?hvVtq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload
  -p prefix     : set prefix path (default: /usr/local/nginx/)
  -c filename   : set configuration file (default: conf/nginx.conf)     #使用指定的配置文件而不是conf目录下的nginx.conf这里我们就是通过nginx -c路径的功能来实现跑多实例nginx
  -g directives : set global directives out of configuration file

较常用的方法是使服务跑在指定用户的家目录下面,这样相对比较安全,同时有利于批量业务部署和上线。

配置普通用户启动Nginx的过程如下:

1)添加用户并创建相关目录和文件,操作如下:

[root@LNMP ~]# useradd yunjisuan
[root@LNMP ~]# su - yunjisuan
[yunjisuan@LNMP ~]$ pwd
/home/yunjisuan
[yunjisuan@LNMP ~]$ mkdir conf logs www         #在普通用户家目录下创建nginx配置文件目录
[yunjisuan@LNMP ~]$ ls
conf  logs  www
[yunjisuan@LNMP ~]$ cp /usr/local/nginx/conf/mime.types ~/conf/     #复制媒体类型配置文件
[yunjisuan@LNMP ~]$ echo "yunjisuan" > www/index.html           #创建网页首页

2)配置Nginx配置文件。配置后的查看命令如下:

[yunjisuan@LNMP ~]$ cat conf/nginx.conf 
worker_processes	4;
worker_cpu_affinity 0001 0010 0100 1000;
worker_rlimit_nofile 65535;
error_log	/home/yunjisuan/logs/error.log;
user yunjisuan yunjisuan;
pid	/home/yunjisuan/logs/nginx.pid;
events {
	use	epoll;
	worker_connections	10240;
}
http {
	include 	mime.types;
	default_type	application/octet-stream;
	sendfile	on;
	keepalive_timeout	65;
	log_format main '$remote_addr-$remote_user[$time_local]"$request"'
    '$status $body_bytes_sent "$http_referer"'
    '"$http_user_agent""$http_x_forwarded_for"';
	
	server {
		listen 8080;
		server_name	www.yunjisuan.com;
		root	/home/yunjisuan/www;
		location / {
			index	index.php index.html index.htm;
		}
		access_log	/home/yunjisuan/logs/web_blog_access.log main;
	}
}
[yunjisuan@LNMP ~]$ tree
.
├── conf
│   ├── mime.types
│   └── nginx.conf
├── logs
└── www
    └── index.html

3 directories, 3 files

说明如下:

  • 所有参数的值,带路径的都要改成/home/yunjisuan.
  • 特权用户root使用的80端口,改为普通用过户使用的端口,在1024以上,这里为8080.

3)启动Nginx,命令如下:

[yunjisuan@LNMP ~]$ ps -ef | grep nginx | grep -v grep
[yunjisuan@LNMP ~]$ /usr/local/nginx/sbin/nginx -c /home/yunjisuan/conf/nginx.conf &>/dev/null &
[1] 2478
[yunjisuan@LNMP ~]$ ps -ef | grep nginx | grep -v grep
503        2479      1  0 14:34 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx -c /home/yunjisuan/conf/nginx.conf
503        2480   2479  0 14:34 ?        00:00:00 nginx: worker process                                         
503        2481   2479  0 14:34 ?        00:00:00 nginx: worker process                                         
503        2482   2479  0 14:34 ?        00:00:00 nginx: worker process                                         
503        2483   2479  0 14:34 ?        00:00:00 nginx: worker process                                         
[1]+  Done                    /usr/local/nginx/sbin/nginx -c /home/yunjisuan/conf/nginx.conf &>/dev/null

#提示503是用户yunjisuan的UID号
[yunjisuan@LNMP ~]$ id yunjisuan
uid=503(yunjisuan) gid=503(yunjisuan) groups=503(yunjisuan)

特别提示:
此处启动nginx,如果不定向到空会显示一些提示,不是错误,可以通过&>/dev/null 定向到空,从而忽略不见。

[yunjisuan@LNMP ~]$ killall nginx
[yunjisuan@LNMP ~]$ ps -ef | grep nginx | grep -v grep
[yunjisuan@LNMP ~]$ /usr/local/nginx/sbin/nginx -c /home/yunjisuan/conf/nginx.conf &
[1] 2495
[yunjisuan@LNMP ~]$ nginx: [alert] could not open error log file: open() "/usr/local/nginx/logs/error.log" failed (13: Permission denied)
2017/08/30 14:38:20 [warn] 2495#0: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /home/yunjisuan/conf/nginx.conf:5

#此处错误可以忽略,定向输出到&>/dev/null

4)配置好解析后,浏览器带端口访问结果如下图所示:

QQ截图20170903013350.png-11.8kB

5)解决普通端口非80提供服务的问题

用负载均衡器解决Web服务非80端口的转换问题,负载均衡器可为Haproxy,Nginx,F5等。

本解决方案的优点如下:

  • 给Nginx服务降权,让网站更安全。
  • 按用户设置站点权限,使站点更独立(无需虚拟化隔离)
  • 开发不需要用root即可完整管理服务及站点。
  • 可实现责任划分,即网络问题属于运维的责任,网站打不开就是开发的责任,或者两者共同承担。

十三,控制Nginx并发连接数量

  • ngx_http_limit_conn_module这个模块用于限制每个定义的key值的连接数(Nginx默认已经被编译),特别是单IP的连接数。
  • 不是所有的连接数都会被计数。一个符合计数要求的连接是整个请求头已经被读取的连接。

控制Nginx并发连接数量参数的说明如下:

1)limit_conn_zone参数:

语法:limit_conn_zone key zone=name:size;
上下文:http
#用于设置共享内存区域,key可以是字符串,Nginx自带变量或前两个组合,如$binary_remote_addr,$server_name.name为内存区域的名称,size为内存区域的大小。

2)limit_conn参数:

语法:limit_conn zone number;
上下文:http,server,location
#用于指定key设置最大连接数。当超过最大连接数时,服务器会返回503(Service Temporarily Unavailable)错误

13.1 限制单IP并发连接数

Nginx的配置文件如下:

[root@web03 nginx]# cat conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    server {
        listen       80;
        server_name  www.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
	        limit_conn addr 1;      #限制同IP的并发为1;
        }
    }
}

测试1:利用Webbench进行并发连接测试(模拟一个客户端)

[root@localhost ~]# webbench -c 1 -t 5 http://192.168.0.225/
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.0.225/
1 client, running 5 sec.
Speed=341472 pages/min, 4808941 bytes/sec.
Requests: 28456 susceed, 0 failed.

#提示:192.168.0.225为web服务器,
#-c:指定客户端数量
#-t:指定持续时间

我们查看Web服务器的access.log日志如下:

[root@web03 nginx]# tail logs/access.log 
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:29:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"

从上边的日志我们可以看到,当并发数为1时(就一个客户端),nginx正常记录用户的访问情况,http返回状态码为200

测试2:利用Webbench进行多客户端并发测试(模拟2个客户端)

[root@localhost ~]# webbench -c 2 -t 5 http://192.168.0.225/
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.0.225/
2 clients, running 5 sec.
Speed=565608 pages/min, 5808154 bytes/sec.
Requests: 47134 susceed, 0 failed.

我们查看Web服务器的access.log日志如下:

[root@web03 nginx]# tail logs/access.log 
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:31:08 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"

从上边的日志我们可以看出,当我们用单IP地址模拟两个并发客户端进行连接时,日志以1:1比例交替出现了503错误。即Nginx已经做了并发连接限制,对超过限制的请求返回503.

测试3:利用Webbench进行多客户端并发测试(模拟3个客户端)

[root@localhost ~]# webbench -c 3 -t 5 http://192.168.0.225/
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.0.225/
3 clients, running 5 sec.
Speed=653388 pages/min, 6162385 bytes/sec.
Requests: 54449 susceed, 0 failed.

我们查看Web服务器的access.log日志如下:

[root@web03 nginx]# tail logs/access.log 
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:16:39:20 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"

经过测试,可以看得出,200与503基本出现次数为1:2,即Nginx已经做了并发连接限制,对超过限制的请求返回503.

以上功能的应用场景之一是用于服务器下载,命令如下:

location /download/ {
    limit_conn addr 11;
}

上面的命令限制访问download下载目录的连接数,该连接数1.

13.2 限制虚拟主机总连接数

不仅可以限制单IP的并发连接数,还可以限制虚拟主机总连接数,甚至可以对两者同时限制。

Nginx的配置文件如下:

[root@web03 nginx]# cat conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_zone $server_name zone=perserver:10m;
    server {
        listen       80;
        server_name  www.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
	    #limit_conn addr 1;
	    limit_conn perserver 2;     #设置虚拟主机连接数为2
        }
    }
}

测试:

#重启Nginx服务后进行测试
root@localhost ~]# webbench -c 5 -t 5 http://192.168.0.225/
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.0.225/
5 clients, running 5 sec.
Speed=682128 pages/min, 7674820 bytes/sec.
Requests: 56844 susceed, 0 failed.

我们查看Web服务器的access.log日志如下:

[root@web03 nginx]# tail -20 logs/access.log 
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 503 213 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
192.168.0.240 - - [30/Aug/2017:17:11:14 -0400] "GET / HTTP/1.0" 200 612 "-" "WebBench 1.5"
[root@web03 nginx]# grep -c 200 logs/access.log 
35850
[root@web03 nginx]# grep -c 503 logs/access.log 
20998
[root@web03 nginx]# cat logs/access.log | wc -l
56848

结论:200和503出现的次数近似2:1
至此,Nginx限制连接数的应用实践就讲解完毕了。
更多资料可参考:http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

十四,控制客户端请求Nginx的速率

ngx_http_limit_req_module模块用于限制每个IP访问每个定义key的请求速率。

语法:limit\_req\_zone key zone=name: size rate=rate;
上下文1:http
#用于设置共享内存区域,key可以是1字符串,Nginx自带变量或前两个组合,如$binary_remote_addr。name为内存区域的名称,size为内存区域的大小,rate为速率,单位为r/s,每秒一个请求。
limit_req参数说明如下:
语法:limit_req zone=name [burst=number][nodelay];
上下文:http,server,location
  • 这里运用了令牌桶原理,burst=num,一共有num块令牌,令牌发完后,多出来的那些请求就会返回503。
  • 换句话说,一个银行,只有一个营业员,银行很小,等候室只有5个人的位置。因此,营业员一个时刻只能为一个人提供服务,剩下的不超过5个人可以在银行内等待,超出的人不提供服务,直接返回503。
  • nodelay默认在不超过burst值的前提下会排队等待处理,如果使用此参数,就会处理完num + 1次请求,剩余的请求都视为超时,返回503。

用于测试的Nginx配置文件如下:

[root@web03 nginx-1.10.2]# cat /usr/local/nginx/conf/nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;      #以请求的客户端IP作为key值,内存区域命令为one,分配10m内存空间,访问速率限制为1秒1次请求(request)
#    limit_conn_zone $binary_remote_addr zone=addr:10m;
#    limit_conn_zone $server_name zone=perserver:10m;
    server {
        listen       80;
        server_name  www.yunjisuan.com;
        location / {
            root   html;
            index  index.html index.htm;
	        limit_req zone=one burst=5;     #使用前面定义的名为one的内存空间,队列值为5,即可以有5个请求排队等待
#	        limit_conn addr 1;
#           limit_conn addr 1;
        }
    }
}
[root@web03 nginx-1.10.2]# /usr/local/nginx/sbin/nginx -s reload          #重启服务

利用Webbench进行测试

[root@localhost ~]# webbench -c 100 -t 10 http://192.168.0.225/
Webbench - Simple Web Benchmark 1.5
Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.
Benchmarking: GET http://192.168.0.225/
100 clients, running 10 sec.
Speed=749706 pages/min, 4811119 bytes/sec.
Requests: 124951 susceed, 0 failed.

我们查看Web服务器的access.log日志如下:

[root@web03 nginx]# > logs/access.log 
[root@web03 nginx]# grep -c 200 logs/access.log 
11
[root@web03 nginx]# grep -c 503 logs/access.log 
124978
[root@web03 nginx]# cat logs/access.log | wc -l
124994

#说明:本次测试只成功响应了11个请求,近视1秒处理1个请求。多余的请求一律用503进行响应。

QQ截图20170903121844.png-153.6kB

  • 通过过滤排重日志,可以看到第一个请求返回200,为正常处理,剩余2582次+9858次超过了限制的在1秒内执行完成,但是都返回了503
  • 具体过程原理为:Nginx在第1秒先处理第一个请求,同时接下来的5个请求等待排队,剩下的(2582+9858)请求返回503。接着第2秒到第6秒处理等待的5个请求。
    更多内容可参考:http://nginx.org/en/docs/http/ngx_http_limit_req_module.html。

十五,本章重点回顾

  1. 安全优化:隐藏Nginx软件名及版本号。
  2. 性能加安全优化:连接超时参数及FastCGI相关参数调优
  3. 性能优化:expires缓存功能及调试查看方法。
  4. 性能优化:gzip压缩功能及调试查看方法。
  5. 安全优化:集群中各角色服务站点目录权限控制策略。
  6. 安全优化:站点目录下所有的文件和目录访问控制。
  7. 性能加安全优化:静态资源防盗链解决方案
  8. 性能加安全优化:robots.txt协议及防爬虫优化解决方案
  9. 用户体验优化:错误页面优雅显示方法
  10. 安全优化:限制http请求方法
  11. 性能加安全优化:CDN加速知识
  12. 安全优化:监牢模式运行Nginx方案策略
  13. 性能加安全优化:Nginx并发连接数及请求速率控制
 
 
 
posted @ 2021-02-22 20:58  昌北F4  阅读(166)  评论(0编辑  收藏  举报