nginx配置
nginx配置
这是一篇比较全面的,关于nginx如何配置的手册。包含一般配置。示例等。须明白,配置不是代码,不一定严格按着配置顺序生效。
参考:
- 官方手册地址
- 官方手册-核心模块功能
- https://segmentfault.com/a/1190000022282235?utm_source=tag-newest
- Nginx 日志配置实践
- Nginx双机主备(Keepalived实现)
- Nginx升级
思路:
- 配置讲解
- nginx 命令
- 线上的示例配置讲解。线上配置、避坑
- 使用场景
- 升级原有的镜像、增加组件,如何操作
配置讲解
整体
整体结构
main
http {
upstream { … }
split_clients {…}
map {…}
geo {…}
server {
if () {…}
location {
limit_except {…}
}
location {
location {
}
}
}
server {
}
}
包含配置块、动作指令、值指令等。
语法
注释只能用#,不能用//,报错如下:
nginx: [emerg] unknown directive "//"
配置块使用花括号,不用加分号。普通的指令,必须结尾加分号。
指令块的嵌套
在 Nginx 配置文件中,指令块是可以互相嵌套的,例如上面的示例,http 块中可以包含多个 server 块,server 块中还会包含多个 location 块,每一个块中都有相应的指令。
而每一个指令都有 Context 上下文,也就是生效的环境,这在 Nginx 的官方文档中说的很清楚,例如下面的两条指令,Context 中都表明了各自可以生效的环境,access_log 指令可以在多个上下文中生效:
Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
access_log off;
Default: access_log logs/access.log combined;
Context: http, server, location, if in location, limit_except
Syntax: log_format name [escape=default|json|none] string ...;
Default: log_format combined "...";
Context: http
常用的上下文环境:
http, server, location, if
全局指令
user
定义Nginx运行的用户和用户组。需要系统存在这个用户组。
user nginx;
daemon
在docker中,需要前台启动时,这个很重要。
daemon on; #后台启动
daemon off; #前台启动,容器内部可能用到。
worker_processes
nginx进程数,建议设置为等于CPU总核心数。即每个CPU运行1个工作进程。
worker_processes 8;
worker_processes auto;
CPU核心数,输入 :
grep processor /proc/cpuinfo | wc -l
worker_cpu_affinity
设置worker进程的cpu亲和力,达到充分利用多核cpu 。参见:worker_cpu_affinity
worker_cpu_affinity auto;
- 2核cpu,开启2个进程
worker_processes 2;
worker_cpu_affinity 01 10;
解释:01表示启用第一个CPU内核,10表示启用第二个CPU内核
worker_cpu_affinity 01 10;表示开启两个进程,第一个进程对应着第一个CPU内核,第二个进程对应着第二个CPU内核。
- 2核cpu,开启4个进程
worker_processes 4;
worker_cpu_affinity 01 10 01 10; #二进制
- 4个cpu,开启4个进程
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
error_log
全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log info;
access_log
同上。具体参见:日志部分。
rewrite_log
rewrite记录。注意,只有on|off。
rewrite on;
log_format
#日志格式设定
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
pid
进程文件。
pid /usr/local/nginx/logs/nginx.pid;
worker_rlimit_nofile
一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 65535;
events
events
{
# 参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;
#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 51200;
multi_accept off;
accept_mutex off;
}
multi_accept
multi_accept可以让nginx worker进程尽可能多地接受请求。它的作用是让worker进程一次性地接受监听队列里的所有请求,然后处理。如果multi_accept的值设为off,那么worker进程必须一个一个地接受监听队列里的请求。
accept_mutex_delay
当accept_mutex功能启用后,只有一个持有mutex锁的worker进程会接受并处理请求,其他worker进程等待。
events {
accept_mutex_delay 500ms;
}
http
通用设置
server_names_hash_bucket_size 128; #服务器名字的hash表大小
client_header_buffer_size 32k; #上传文件大小限制
large_client_header_buffers 4 64k; #设定请求缓
client_max_body_size 8m; #设定请求缓
sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
tcp_nopush on; #防止网络阻塞
tcp_nodelay on; #防止网络阻塞
keepalive_timeout 120; #长连接超时时间,单位是秒
fast_cgi
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;
gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml;
压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_vary on;
index显示列表
(一般为企业内部使用)
autoindex on;//自动显示目录
autoindex_exact_size off;//人性化方式显示文件大小否则以byte显示
autoindex_localtime on;//按服务器时间显示,否则以gmt时间显示
default_type
设置响应的默认MIME类型。默认:text/plain
default_type text/plain;
server
每一个server配置相当于一个虚拟主机。
虚拟主机是一种特殊的软硬件技术,它可以将网络上的每一台计算机分成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就可以实现一台主机对外提供多个web服务,每个虚拟主机之间是独立的,互不影响的
通过nginx可以实现虚拟主机的配置,nginx支持三种类型的虚拟主机配置
- 基于ip的虚拟主机, (一块主机绑定多个ip地址)
- 基于域名的虚拟主机(servername)
- 基于端口的虚拟主机(listen如果不写ip端口模式)
listen
listen 指令在 server 块中生效,用来配置监听哪些端口,由这些端口来处理请求。listen 指令的配置如下:

如示例所示,listen 指令可以监听的类型有多种,可以配置监听地址和端口,也可以是仅地址和仅端口,还可以仅监听 IPv6 等等。
监听80端口,默认服务器,开启ipv6。
listen [::]:80 default_server ipv6only=on;
server_name
server_name 指令是用来配置究竟是哪个 server 来处理我们的请求的。有时候,一个 server_name 中可能会有多个域名,这时候是如何选择的呢?
类型:
- server_name 指令后可以跟多个域名,第一个是主域名,多个域名之间空格分隔
- 泛域名:仅支持在最前或最后加 *,例如:
server_name *.taohui.tech - 正则表达式匹配:
server_name www.taohui.tech ~^www\d+\.taohui\.tech$;
匹配规则:
- 精确匹配(与顺序无关)
- * 在前的泛域名(与顺序无关)
- * 在后的泛域名(与顺序无关)
- 按文件中的顺序匹配正则表达式域名
- default server
- 第 1 个
- listen 指定 default
当 server_name 指令后有多个域名时,会有一个 server_name_in_redirect 的配置,这个配置默认关闭,它使用来控制域名重定向的,也就是这个配置开启之后,请求过来会重定向到主域名访问。
Syntax server_name_in_redirect on | off;
Default server_name_in_redirect off;
Context http, server, location
还可以用正则表达式创建变量
# 使用 $1/$2 的方式引用变量 server { server_name ~^(www\.)?(.+)$; location / { root /sites/$2; } }- ``` # 还可以通过加一个 ?<> 的方式来命名变量 server { server_name ~^(www\.)?(?<domain>.+)$; location / { root /sites/$domain; } }
特殊的配置规则
- .test.tech 可以匹配 test.tech *.test.tech
- _ 匹配所有域名请求
- “” 匹配没有传递 host 头部的请求
root
root /var/www/example;
include
include指令加载文件位置,相当于nginx.conf位置。
#加载vhost配置文件。
include vhost/*.conf;
include mime.types;
error_page
使用该error_page指令,您可以配置返回自定义页面以及错误代码,在响应中替换其他错误代码,或将浏览器重定向到其他URI。
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
在以下示例中,当NGINX Plus无法找到页面时,它将代码替换301为代码404,并将客户端重定向到http:/example.com/new/path.html。当客户端仍尝试访问其旧URI的页面时,此配置很有用。该301代码通知浏览器该页面已永久移动,并且需要在返回时自动用新地址替换旧地址。
location /old/path.html {
error_page 404 =301 http://example.com/new/path.html;
}
index
index index.html index.htm index.php;
server_tokens
启用或禁用在错误页和“服务器”响应头字段中发出nginx版本。
Syntax: server_tokens on | off | build | string;
Default: server_tokens on;
Context: http, server, location
location
基本语法:location [=||*|^~] /uri/ { … }
注意:上面location、符号、路径、花括号之间有空格。尤其是正则表达式,空格不能少。
示例:
location = / {
# 只匹配 / 查询。
}
location / {
# 匹配任何查询,因为所有请求都以 / 开头。但是正则表达式规则和长的块规则将被优先和查询匹配。
}
location ^~ /images/ {
# 匹配任何以 /images/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试。
}
location ~*.(gif|jpg|jpeg)$ {
# 匹配任何以 gif、jpg 或 jpeg 结尾的请求。
}
location ~*.(gif|jpg|swf)$ {
valid_referers none blocked start.igrow.cn sta.igrow.cn;
if ($invalid_referer) {
#防盗链
rewrite ^/ http://$host/logo.png;
}
}
# 嵌套模式
# 首先,匹配到/路径
location / {
# 然后我们匹配到一个最具体的子字符串,注意这不是regluar表达式
location ^~ /css{
# 下面是匹配的正则表达式
location ~ /css/.*\.css$ {
return 302;
}
return 402;
}
}
# 上面,访问 /css /cssjkjk/ /css/1.txt 返回402。
# 访问 /css/1.css /css/2/3.css 返回302 。
表述1:
location URI {} 对当前路径及子路径下的所有对象都生效;
location = URI {} 注意URL最好为具体路径。 精确匹配指定的路径,不包括子路径,因此,只对当前资源生效;
location ~ URI {} location ~* URI {} 模式匹配URI,此处的URI可使用正则表达式,~区分字符大小写,~*不区分字符大小写;
location ^~ URI {} 禁用正则表达式
表述2:
=:对URI做精确匹配;
location = / {
...
}
~:对URI做正则表达式模式匹配,区分字符大小写;
~*:对URI做正则表达式模式匹配,不区分字符大小写;
^~:对URI的左半部分做匹配检查,不区分字符大小写;注意,不是正则表达式。
不带符号:匹配起始于此uri的所有的url;
表述3:
= 严格匹配。如果这个查询匹配,那么将停止搜索并立即处理此请求。
~ 为区分大小写匹配(可用正则表达式)
!~为区分大小写不匹配
~* 为不区分大小写匹配(可用正则表达式)
!~*为不区分大小写不匹配
^~ 如果把这个前缀用于一个常规字符串,那么告诉nginx 如果路径匹配那么不测试正则表达式。
总结:
优先级:= 大于 ^~ 大于 |* 大于 /|/dir/
备注:优先级理解为,最后一次匹配的优先级,即谁最可能成为最后一次匹配。或者理解为,结束匹配的优先级。
location配置规则
location 的执行逻辑:
1、“普通 location ”的匹配规则是“最大前缀”,因此“普通 location ”的确与 location 编辑顺序无关;
2、“正则 location ”的匹配规则是“顺序匹配,且只要匹配到第一个就停止后面的匹配”;
“普通location ”与“正则 location ”之间的匹配顺序是:先匹配普通 location ,再“考虑”匹配正则 location 。
注意这里的“考虑”是“可能”的意思,也就是说匹配完“普通 location ”后,有的时候需要继续匹配“正则 location ”,有的时候则不需要继续匹配“正则 location ”。两种情况下,不需要继续匹配正则 location :
( 1 )当普通 location 前面指定了“ ^~ ”,特别告诉 Nginx 本条普通 location 一旦匹配上,则不需要继续正则匹配;
( 2 )当普通location 恰好严格匹配上,不是最大前缀匹配,则不再继续匹配正则
@路径
@路径,相当于起别名。与proxy_pass配合使用。
location / {
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://backend.example.com;
}
IP访问控制
location / {
deny IP /IP段;
deny 192.168.1.109;
allow 192.168.1.0/24;192.168.0.0/16;192.0.0.0/8;
}
deny all;拒绝所有。
另外,配合location的正则, if模块等,完成基于特定路径、请求头的访问控制。
用户认证访问
模块ngx_http_auth_basic_module 允许使用“HTTP基本认证”协议验证用户名和密码来限制对资源的访问
location ~ (.*)\.avi$ {
auth_basic "closed site";
auth_basic_user_file conf/users;
}
账号密码通过httpd-tools配置
yum install httpd
htpasswd -c -d /usr/local/users zhangyang
nginx访问状态监控
location /basic_status {
stub_status on;
}
字符编码
location / {
charset utf-8; #一般是在个别的location中加入此项,具体情况具体对待
rewrite .* /index.html break;
}
expires缓存
指定缓存时间。
#图片缓存时间设置
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 10d;
}
#JS和CSS缓存时间设置
location ~ .*\.(js|css)?$
{
expires 1h;
}
etag缓存
etag on | off;
- 开启缓存
location~ .*\.(gif|jpg|jpeg|png|bmp|ico|rar|css|js|zip|xml|txt|flv|swf|mid|doc|cur|xls|pdf|txt|)$
{
FileETag on;
etag_format "%X%X";
expires 30d;
}
- 禁用缓存配置
location ~ index\.html$
{
add_header Cache-Control "no-store";
expires -1;
etag off;
}
last-modify
add_header
添加响应头信息。
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Origin "http://local.dev.com";
add_header Access-Control-Allow-Credentials "true";
rewrite重写机制
参考:
格式:
rewrite
关键字 正则 替代内容 flag标记
正则:perl兼容正则表达式语句进行规则匹配
替代内容:将正则匹配的内容替换成replacement
flag标记:rewrite支持的flag标记
last #本条规则匹配完成后,继续向下匹配新的location URI规则
break #本条规则匹配完成即终止,不再匹配后面的任何规则
redirect #返回302临时重定向,浏览器地址会显示跳转后的URL地址
permanent #返回301永久重定向,浏览器地址栏会显示跳转后的URL地址
rewrite参数的标签段位置:
server,location,if
示例:(url增加前缀)
rewrite ^ /prefix$1;
示例:
http {
# 定义image日志格式
log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;
# 开启重写日志
rewrite_log on;
server {
root /home/www;
location / {
# 重写规则信息
error_log logs/rewrite.log notice;
# 注意这里要用‘’单引号引起来,避免{}
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;
# 注意不能在上面这条规则后面加上“last”参数,否则下面的set指令不会执行
set $image_file $3;
set $image_type $4;
}
location /data {
# 指定针对图片的日志格式,来分析图片类型和大小
access_log logs/images.log mian;
root /data/images;
# 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
try_files /$arg_file /image404.html;
}
location = /image404.html {
# 图片不存在返回特定的信息
return 404 "image not found\n";
}
}
}
try_files
同rewrite一样,也能达到重写url的目的。
该try_files指令可用于检查指定的文件或目录是否存在;NGINX会进行内部重定向,否则会返回指定的状态码。例如,要检查是否存在与请求URI相对应的文件,请使用try_files伪指令和$uri变量,如下所示:
server {
root /www/data;
location /images/ {
try_files $uri /images/default.gif;
}
}
该文件以URI的形式指定,该URI使用在当前位置或虚拟服务器的上下文中设置的root或alias指令进行处理。在这种情况下,如果不存在与原始URI对应的文件,则NGINX将内部重定向到由最后一个参数指定的URI,返回/www/data/images/default.gif。
最后一个参数也可以是状态代码(直接在等号之后)或位置名称。在以下示例中,如果try_files指令的任何参数都无法解析到现有文件或目录,则将返回404错误。
location / {
try_files $uri $uri/ $uri.html =404;
}
请求将被重定向到指定位置,该位置会将其传递给代理服务器。
location / {
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://backend.example.com;
}
return
location / {
return 403; # 或者 '403'。
}
location /permanently/moved/url {
return 301 http://www.example.com/moved/here;
}
或者直接返回文本内容:
location /scc {
default_type text/plain;
return 502 "服务正在升级,请稍后再试……";
}
返回json也是可以的:
location ~ ^/get_json {
default_type application/json;
return 200 '{"status":"success","result":"nginx json"}';
}
利用上请求的参数:
location ~ ^/get_text/article/(.*)_(\d+).html$ {
default_type text/html;
set $s $1;
set $d $2;
return 200 str:$s$d;
}
proxy_pass反向代理
参考:代理如何去掉或增加前缀
通常的代理服务器,只用于代理内部网络对Internet的连接请求,客户机必须指定代理服务器,并将本来要直接发送到Web服务器上的http请求发送到代理服务器中由代理服务器向Internet上的web服务器发起请求,最终达到客户机上网的目的。
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器
注意事项:
如果location包含了正则表达式,则 “proxy_pass”不能包含URI part 。即 proxy_pass http://localhost:8080;结尾不能带任务URI部分。
location /basic_status {
proxy_pass http://localhost/;
}
更复杂的配置:
location /ucarapi/ {
proxy_pass http://httpds/; #已定义的upstream
proxy_connect_timeout 3; //连接超时时间
proxy_read_timeout 30;
proxy_set_header Host tapi.51ucar.cn; //HTTP头信息,后端服务器根据此来找到特定虚拟主机
proxy_set_header X-Real-IP $remote_addr; //HTTP头信息,真实IP
proxy_set_header X-Scheme $scheme;
}
另外,公司常用的:
upstream php {
server 127.0.0.1:81;
}
server {
listen 80;
server_name localhost;
location / {
root /yd;
index index.php index.html index.htm;
if (!-e $request_filename) {
proxy_pass http://php;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location ~ \.php$ {
proxy_set_header Host $host:APP_PORT;
#proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:81;
}
}
proxy_pass
proxy_connect_timeout
proxy_set_header
代理时,添加请求头。
upstream
反向代理配合upstream使用
upstream httpds {
server 192.168.43.152:80;
server 192.168.43.153:80;
}
weight(权重)
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream httpds {
server 127.0.0.1:8050 weight=10 down;
server 127.0.0.1:8060 weight=1;
server 127.0.0.1:8060 weight=1 backup;
}
down:表示当前的server暂时不参与负载
weight:默认为1.weight越大,负载的权重就越大。
backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。
max_conns
可以根据服务的好坏来设置最大连接数,防止挂掉,比如1000,我们可以设置800
upstream httpds {
server 127.0.0.1:8050 weight=5 max_conns=800;
server 127.0.0.1:8060 weight=1;
}
max_fails、 fail_timeout
max_fails:失败多少次 认为主机已挂掉则,踢出,公司资源少的话一般设置2~3次,多的话设置1次
max_fails=3 fail_timeout=30s代表在30秒内请求某一应用失败3次,认为该应用宕机,后等待30秒,这期间内不会再把新请求发送到宕机应用,而是直接发到正常的那一台,时间到后再有请求进来继续尝试连接宕机应用且仅尝试1次,如果还是失败,则继续等待30秒…以此循环,直到恢复。
upstream httpds {
server 127.0.0.1:8050 weight=1 max_fails=1 fail_timeout=20;
server 127.0.0.1:8060 weight=1;
}
负载均衡算法
轮询、 weight、 ip_hash、 url_hash、 least_conn、 least_time
健康检查模块
配置一个status的location
location /status {
check_status;
}
在upstream配置如下
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
fast_pass
参考:ngx_http_fastcgi_module 的那些事
示例:
location ~ \.php($|/) {
fastcgi_pass unix:/dev/shm/php-fpm.unix; #最重要的一项,根据实际情况来配置(根据php的配置文件listen的配置来配置,其值可以是一个域名、IP地址:端口、或者是一个Unix的Socket文件。
)
fastcgi_index index.php; #当请求以/结尾的时候,会将请求传递给所设置的index.php文件处理。
fastcgi_split_path_info ^(.+\.php)(.*)$; #Nginx默认获取不到PATH_INFO的值,得通过fastcgi_split_path_info指定定义的正则表达式来给$fastcgi_path_info赋值。
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi.conf;
}
配置php的fastcgi:
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.php index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
location ~ \.php$ {
root html;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
include fastcgi_params;
}
}
include指令加载文件位置,相当于nginx.conf位置,fastcgi_params内容如下:
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REQUEST_SCHEME $scheme;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
ngx_http_fastcgi_module用来处理FastCGI的模块。PHP一般是以PHP-CGI的形式在运行,它就是一种FastCGI,我们在进程中看到的PHP-FPM是PHP-CGI的管理调度器。
四个常见、重要的配置项
fastcgi_pass
作用域:location, if in location
设置FastCGI服务,其值可以是一个域名、IP地址:端口、或者是一个Unix的Socket文件。
同时,它也只支持一个FastCGI服务集群。
# TCP形式传递
fastcgi_pass localhost:9000;
# Socket形式传递
fastcgi_pass unix:/tmp/fastcgi.socket;
# 传递给集群
upstream cloud {
server cgi_1.cloud.com;
server cgi_2.cloud.com;
}
fastcgi_pass cloud;
fastcgi_param
作用域:http, server, location
设置一个传递给FastCGI服务的参数,可以是文本或者是变量。
# 例如在接入层Nginx上面传递如下5个参数
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# 那么在FastCGI上面,例如PHP-CGI上面就可以通过$_SERVER这个超全局变量获取。
$_SERVER['REMOTE_ADDR']
$_SERVER['REMOTE_PORT']
$_SERVER['SERVER_ADDR']
$_SERVER['SERVER_PORT']
$_SERVER['SERVER_NAME']
可传递的参数,遵循CGI/1.1规范定义。
可以从Github上面看到Nginx在3年前实现FastCGI的参数传递后,基本就没变过了。
fastcgi_index
作用域:http, server, location
当请求以/结尾的时候,会将请求传递给所设置的index.php文件处理。
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;
fastcgi_split_path_info
作用域:location
Nginx默认获取不到PATH_INFO的值,得通过fastcgi_split_path_info指定定义的正则表达式来给$fastcgi_path_info赋值。
其正则表达式必须要有两个捕获。
- 第一个捕获的值会重新赋值给
$fastcgi_script_name变量。 - 第二个捕获到的值会重新赋值给
$fastcgi_path_info变量。
例子:
location ~ ^(.+\.php)(.*)$ {
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_param SCRIPT_FILENAME /path/to/php$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
原始请求是 /show.php/article/0001。
通过分割,FastCGI得到的结果是:
- SCRIPT_FILENAME:
/path/to/php/show.php - PATH_INFO:
/article/0001
Nginx在0.7.31以前是没有fastcgi_split_path_info这个指令的,而0.7.x这个版本一直存活了好多年,后面才高歌猛进,导致网上存在大量旧版本通过正则自己设置PATH_INFO的方法。
踩了好多次依旧不记得怎么设置的ThinkPHP
为什么总是踩坑?因为我们都会通过重写来隐藏index.php文件,而ThinkPHP的教程,默认教的是旧版Nginx写法,且URL_MODE必须设置为3也说得很隐晦(URL_MODE默认为0)。
例如ThinkPHP的说明有一段旧版的Nginx设置指引。
location / { // …..省略部分代码
if (!-e $request_filename) {
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
}
该规则是通过将请求rewrite给/index.php?s=来实现的,其ThinkPHP的URL_MODE配置必须为3,也就是兼容模式。
如果使用本文中的传递PATH_INFO方式,且隐藏index.php,则ThinkPHP的URL_MODE需要改为2。
如果使用本文中的传递PATH_INFO方式,但不隐藏index.php,则ThinkPHP的URL_MODE改为1。
cgi.fix_pathinfo
cgi.fix_pathinfo参数,藏在PHP-FPM的php.ini配置里面,其默认值为1。
这里存在一个安全风险,我也不通,详情不表,看鸟哥的文章:http://www.laruence.com/2010/05/20/1495.html
习惯性将其设置为0即可。
if 模块
if语句块长用在做单独的限制,如限制访问特定的资源,然后对此类请求做处理,rewire或者deny或者proxy_pass等等。
if ($http_user_agent ~ MSIE) {proxy_pass
^(.*)$ /msie/$1 break;
} #如果UA包含"MSIE",rewrite请求到/msid/目录下
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
} #如果cookie匹配正则,设置变量$id等于正则引用部分
if ($request_method = POST) {
return 405;
} #如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302
if ($slow) {
limit_rate 10k;
} #限速,$slow可以通过 set 指令设置
if (!-f $request_filename){ #-e 也行,表示存在
break;
proxy_pass http://127.0.0.1;
} #如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查
if ($args ~ post=140){
rewrite ^ http://example.com/ permanent;
} #如果query string中包含"post=140",永久重定向到example.com
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.jefflei.com www.leizhenfang.com;
if ($invalid_referer) {
return 404;
} #防盗链
}
文件判断
不管filename是什么类型,!-e加了!就取反
-e filename 如果 filename存在,则为真
-d filename 如果 filename为目录,则为真
-f filename 如果 filename为常规文件,则为真
-L filename 如果 filename为符号链接,则为真
-r filename 如果 filename可读,则为真
-w filename 如果 filename可写,则为真
-x filename 如果 filename可执行,则为真
-s filename 如果文件长度不为0,则为真
-h filename 如果文件是软链接,则为
https
在http配置下新增一个server
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /usr/local/nginx/ssl/nginx.crt;
ssl_certificate_key /usr/local/nginx/ssl/nginx.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#禁止在header中出现服务器版本,防止黑客利用版本漏洞攻击
server_tokens off;
#如果是全站 HTTPS 并且不考虑 HTTP 的话,可以加入 HSTS 告诉你的浏览器本网站全站加密,并且强制用 HTTPS 访问
fastcgi_param HTTPS on;
fastcgi_param HTTP_SCHEME https;
access_log /usr/local/nginx/logs/httpsaccess.log;
root /home/movie/;
location / {
proxy_set_header X-Forwarded-For $remote_addr;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:55055;
}
location /mp4 {
index index.html index.htm;
}
}
http转https请求
server {
listen 80;
server_name dev-payment.xxxx.cn;
return 307 https://dev-payment.xxxx.cn$request_uri;
}
注意:曾经遇到过这么一个坑,http转https的时候会将POST转换为GET请求,此时需要这样配置
日志
参考:Nginx 日志配置实践
前言
Nginx日志对于统计、系统服务排错很有用。
Nginx日志主要分为两种:access_log(访问日志)和error_log(错误日志)。通过访问日志我们可以得到用户的IP地址、浏览器的信息,请求的处理时间等信息。错误日志记录了访问出错的信息,可以帮助我们定位错误的原因。
本文将详细描述一下如何配置Nginx日志。
设置access_log
访问日志主要记录客户端的请求。客户端向Nginx服务器发起的每一次请求都记录在这里。客户端IP,浏览器信息,referer,请求处理时间,请求URL等都可以在访问日志中得到。当然具体要记录哪些信息,你可以通过log_format指令定义。
语法
access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
# 设置访问日志
access_log off;
# 关闭访问日志
- path 指定日志的存放位置。
- format 指定日志的格式。默认使用预定义的combined。
- buffer 用来指定日志写入时的缓存大小。默认是64k。
- gzip 日志写入前先进行压缩。压缩率可以指定,从1到9数值越大压缩比越高,同时压缩的速度也越慢。默认是1。
- flush 设置缓存的有效时间。如果超过flush指定的时间,缓存中的内容将被清空。
- if 条件判断。如果指定的条件计算为0或空字符串,那么该请求不会写入日志。
另外,还有一个特殊的值off。如果指定了该值,当前作用域下的所有的请求日志都被关闭。
作用域
可以应用access_log指令的作用域分别有http,server,location,limit_except。也就是说,在这几个作用域外使用该指令,Nginx会报错。
以上是access_log指令的基本语法和参数的含义。下面我们看一几个例子加深一下理解。
基本用法
access_log /var/logs/nginx-access.log
该例子指定日志的写入路径为/var/logs/nginx-access.log,日志格式使用默认的combined。
access_log /var/logs/nginx-access.log buffer=32k gzip flush=1m
该例子指定日志的写入路径为/var/logs/nginx-access.log,日志格式使用默认的combined,指定日志的缓存大小为32k,日志写入前启用gzip进行压缩,压缩比使用默认值1,缓存数据有效时间为1分钟。
使用log_format自定义日志格式
Nginx预定义了名为combined日志格式,如果没有明确指定日志格式默认使用该格式:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
如果不想使用Nginx预定义的格式,可以通过log_format指令来自定义。
语法
log_format name [escape=default|json] string ...;
- name 格式名称。在access_log指令中引用。
- escape 设置变量中的字符编码方式是json还是default,默认是default。
- string 要定义的日志格式内容。该参数可以有多个。参数中可以使用Nginx变量。
下面是log_format指令中常用的一些变量:
$bytes_sent
发送给客户端的总字节数
$body_bytes_sent
发送给客户端的字节数,不包括响应头的大小
$connection
连接序列号
$connection_requests
当前通过连接发出的请求数量
$msec
日志写入时间,单位为秒,精度是毫秒
$pipe
如果请求是通过http流水线发送,则其值为"p",否则为“."
$request_length
求长度(包括请求行,请求头和请求体)
$request_time
请求处理时长,单位为秒,精度为毫秒,从读入客户端的第一个字节开始,直到把最后一个字符发送张客户端进行日志写入为止
$status
响应状态码
$time_iso8601
标准格式的本地时间,形如“2017-05-24T18:31:27+08:00”
$time_local
通用日志格式下的本地时间,如"24/May/2017:18:31:27 +0800"
$http_referer
请求的referer地址。
$http_user_agent
客户端浏览器信息。
$remote_addr
客户端IP
$http_x_forwarded_for
当前端有代理服务器时,设置web节点记录客户端地址的配置,此参数生效的前提是代理服务器也要进行相关的x_forwarded_for设置。
$request
完整的原始请求行,如 "GET / HTTP/1.1"
$remote_user
客户端用户名称,针对启用了用户认证的请求
$request_uri
完整的请求地址,如 "https://daojia.com/"
下面演示一下自定义日志格式的使用:
access_log /var/logs/nginx-access.log
mainlog_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
我们使用log_format指令定义了一个main的格式,并在access_log指令中引用了它。假如客户端有发起请求:https://suyunfe.com/,我们看一下我截取的一个请求的日志记录:
112.195.209.90 - - [20/Feb/2018:12:12:14 +0800] "GET / HTTP/1.1" 200 190 "-" "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36" "-"
我们看到最终的日志记录中$remote_user、$http_referer、$http_x_forwarded_for都对应了一个-,这是因为这几个变量为空。
设置error_log
错误日志在Nginx中是通过error_log指令实现的。该指令记录服务器和请求处理过程中的错误信息。
语法
配置错误日志文件的路径和日志级别。
error_log file [level];
Default:
error_log logs/error.log error;
第一个参数指定日志的写入位置。
第二个参数指定日志的级别。level可以是debug, info, notice, warn, error, crit, alert,emerg中的任意值。可以看到其取值范围是按紧急程度从低到高排列的。只有日志的错误级别等于或高于level指定的值才会写入错误日志中。默认值是error。
基本用法
error_log /var/logs/nginx/nginx-error.log
它可以配置在:main, http, mail, stream, server, location作用域。
例子中指定了错误日志的路径为:/var/logs/nginx/nginx-error.log,日志级别使用默认的error。
open_log_file_cache
每一条日志记录的写入都是先打开文件再写入记录,然后关闭日志文件。如果你的日志文件路径中使用了变量,如access_log /var/logs/$host/nginx-access.log,为提高性能,可以使用open_log_file_cache指令设置日志文件描述符的缓存。
语法
open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
- max 设置缓存中最多容纳的文件描述符数量,如果被占满,采用LRU算法将描述符关闭。
- inactive 设置缓存存活时间,默认是10s。
- min_uses 在inactive时间段内,日志文件最少使用几次,该日志文件描述符记入缓存,默认是1次。
- valid:设置多久对日志文件名进行检查,看是否发生变化,默认是60s。
- off:不使用缓存。默认为off。
基本用法
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
它可以配置在http、server、location作用域中。
例子中,设置缓存最多缓存1000个日志文件描述符,20s内如果缓存中的日志文件描述符至少被被访问2次,才不会被缓存关闭。每隔1分钟检查缓存中的文件描述符的文件名是否还存在。
总结
Nginx中通过access_log和error_log指令配置访问日志和错误日志,通过log_format我们可以自定义日志格式。如果日志文件路径中使用了变量,我们可以通过open_log_file_cache指令来设置缓存,提升性能。
变量
nginx自带的变量如下,除此之外,还可以通过set来设置自己的变量。详见调试->set。
| 变量 | 说明 |
|---|---|
| $args | 请求中的参数,如www.123.com/1.php?a=1&b=2的$args就是a=1&b=2 |
| $content_length | HTTP请求信息里的”Content-Length” |
| $conten_type | HTTP请求信息里的”Content-Type” |
| $document_root | nginx虚拟主机配置文件中的root参数对应的值 |
| $document_uri | 当前请求中不包含指令的URI,如www.123.com/1.php?a=1&b=2的$document_uri就是1.php,不包含后面的参数 |
| $host | 主机头,也就是域名 |
| $http_user_agent | 客户端的详细信息,也就是浏览器的标识,用curl -A可以指定 |
| $http_cookie | 客户端的cookie信息 |
| $limit_rate | 如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置, 则显示0 |
| $remote_addr | 客户端的公网ip |
| $remote_port | 客户端的port |
| $remote_user | 如果nginx有配置认证,该变量代表客户端认证的用户名 |
| $request_body_file | 做反向代理时发给后端服务器的本地资源的名称 |
| $request_method | 请求资源的方式,GET/PUT/DELETE等 |
| $request_filename | 当前请求的资源文件的路径名称,相当于是document_uri的组合 |
| $request_uri | 请求的链接,包括和args |
| $scheme | 请求的协议,如ftp,http,https |
| $server_protocol | 客户端请求资源使用的协议的版本,如HTTP/1.0,HTTP/1.1,HTTP/2.0等 |
| $server_addr | 服务器IP地址 |
| $server_name | 服务器的主机名 |
| $server_port | 服务器的端口号 |
| $uri | 和$document_uri相同 |
| $http_referer | 客户端请求时的referer,通俗讲就是该请求是通过哪个链接跳过来的,用curl -e可以指定 |
geo
参见:Nginx通过geo模式实现限速白名单和全局负载均衡 - 运维笔记
启动命令
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
示例:
# 帮助
nginx -h
# 启动
nginx
# 前台启动
nginx -g 'daemon off;' # -g 可以指定全局参数
# 加载新配置
nginx -s reload
# 停止
nginx -s stop
nginx -s quit
# 测试配置文件是否正确,另外此命令可以看到配置文件的位置。
nginx -t #默认位置下的配置
nginx -t -c /path/to/nginx.conf
# 查看nginx是否启动
ps aux |grep nginx
# 查看安装时,使用的配置,或者安装了哪些模块
nginx -V
模块
模块安装
模块解读
ngx_http_stub_status_module
附录
Nginx服务器性能优化的三大方面
摘抄: 原文
Nginx服务器非常快,但是Nginx的默认设置并没有针对具体的硬件进行调优。在这篇文章中,我们要把Nginx的性能发挥到极限。Nginx的配置分为三大部分:worker进程配置、I/O配置、TCP配置。我们将分别对这三大配置展开讨论,并在最后给出综合性的配置。
Nginx的worker进程配置
worker_processes
worker_processes directive指定nginx worker进程的数量。它是一个全局性配置,不属于events模块,也不属于http或location模块。
worker_processes 1;
默认的值是1,意味着nignx只打开一个worker进程。最优的设置是worker进程数量要与CPU的核数相等。我们可以用lscpu命令来找出CPU的核数。
lscpu
也可以用
cat /proc/cpuinfo | grep 'processor' | wc -l
另外,我们也可以将worker_processes的值设为auto,这样nginx会自动检测CPU核数并打开相同数量的worker进程。
当nginx添加了SSL证书时,最好要打开多个worker进程。SSL握手会进行硬盘I/O操作。 所以打开多个worker进程有利于性能的提升。
accept_mutex
当我们为nginx打开了多个worker进程后,我们需要配置如何选择worker进程来完成相应的请求处理。在events模块中,我们可以设置
events {
accept_mutex on;
}
accept_mutex会轮流来选择worker进程。Nginx默认开启了accept_mutex。
如果accept_mutex的值被设为off,那么当有请求需要处理时,所有的worker进程都会从waiting状态中唤醒,但是只有一个worker进程能处理请求,这造成了thundering herd现象,这个现象每一秒钟会发生多次。它使服务器的性能下降,因为所有被唤醒的worker进程在重新进入waiting状态前会占用一段CPU时间。
accept_mutex_delay
当accept_mutex功能启用后,只有一个持有mutex锁的worker进程会接受并处理请求,其他worker进程等待。accept_mutex_delay指定的时间就是这些worker进程的等待时间,过了等待时间下一个worker进程便取得mutex锁,处理请求。accept_mutex_delay在events模块中指定,默认的值为500ms。
events {
accept_mutex_delay 500ms;
}
worker_connections
worker_connections的默认值是512,它在events模块中。它指定了一个worker进程在同一时间可以处理的最大请求数。
events {
worker_connections 512;
}
将它的值增加到1024左右。
web服务器同时处理的请求数并不等于它同时服务的客户端数量。一个浏览器会打开多个并发连接来下载网页的各个部分,如图片、脚本等等。而且不同的浏览器对同一个网页打开的并发连接数量也会有所不同。
worker_rlimit_nofile
由于每一个socket都会打开一个文件描述符,所以服务器可以同时处理连接数量受到系统文件描述符数量的限制。如果nginx打开的socket数量超过了文件描述符的数量,那么在error.log文件中会出现too many opened files错误。我们可以用下面的命令来查看文件描述符的数量:
$ ulimit -n
Nginx worker进程默认的用户名是www-data,用户www-data所拥有的文件描述符的数量要大于worker进程数量与worker_connections之乘积。 nginx有一个worker_rlimit_nofile directive,可以用来设置系统可用的文件描述符。这与ulimit设置可用文件描述符的作用是一样的。如果它们都设置了可用文件描述符,那么worker_rlimit_nofile会覆盖ulimit的设置。
worker_rlimit_nofile 20960;
查看操作系统对一个进程施加的限制,我们可以用命令读取/etc/$pid/limits文件,$pid是进程的pid。
multi_accept
multi_accept可以让nginx worker进程尽可能多地接受请求。它的作用是让worker进程一次性地接受监听队列里的所有请求,然后处理。如果multi_accept的值设为off,那么worker进程必须一个一个地接受监听队列里的请求。
events {
multi_accept on;
}
默认Nginx没有开启multi_accept。
如果web服务器面对的是一个持续的请求流,那么启用multi_accept可能会造成worker进程一次接受的请求大于worker_connections指定可以接受的请求数。这就是overflow,这个overflow会造成性能损失,overflow这部分的请求不会受到处理。
use
Nginx处理请求的方法有很多种,每一个方法都允许Nginx Worker进程监测多个socket文件描述符。这些方法都分别依赖于特定的平台,用于生成Nginx二进制文件的configure命令会选择适合当前平台的最有效的方法。如果要使用另外的方法,那么我们必须先启用这些方法。
我们可以用use这个directive来选择另外的处理请求的方法。use directive属于events模块。
events {
use select;
}
Nginx支持以下请求处理方法:
- select: 这是一种标准的请求处理方法。如果一个平台上缺少相应的更加有效的方法,那么Nginx会自动使用select方法。
- poll: 这是一种标准的请求处理方法。如果一个平台上缺少相应的更加有效的方法,那么Nginx会自动使用poll方法。
- kqueue: 这是BSD家族操作系统上可用的一种高效的请求处理方法。可用于FreeBSD, OpenBSD, NetBSD和OS X。kqueue方法会忽略multi_accept。
- epoll: 这是Linux系统上可用的一种高效的请求处理方法,类似于kqueue。它有一个额外的directive,那就是epoll_events。epoll_events指定了Nginx可以向内核传递的事件数量。默认的值是512。
Nginx的I/O配置
sendfile
当一个程序需要传输文件时,Linux内核首先将文件数据缓冲,然后将文件数据传送给程序缓冲,最后程序将文件数据传输到目的地。Sendfile方法是一种数据传输的更高效的方法,数据在内核中的文件描述符之间传输,而不需要将数据传输给程序缓冲。这种方法的结果是改善了对操作系统资源的利用。
我们可以用sendfile directive来启用sendfile方法,在http,server,location三个模块都可以定义。
http {
sendfile on ;
}
默认情况下,sendfile 的值是on。
Direct I/O
通常,Linux内核会尝试优化和缓存读写请求。数据缓存在内核里,将来任何的读取相同数据的请求会变得更快,因为不需要从缓慢的硬盘中读取数据。
Direct I/O是文件系统的一个功能,它允许程序绕过内核缓存,直接在硬盘上读写数据,这可以充分利用CPU频数,改善缓存的有效性。Directo I/O适用于访问率少的数据。这些数据不需要缓存在任何位置。我们可以用directio directive来启用这一功能,在http, server和location当中定义。
location /video/ {
directio 4m;
}
在上面的设置中,任何大于4M的文件都将以Direct I/O的形式直接从硬盘读取。默认directio没有启用。
如果某个请求是通过directo I/O,那么这个请求不能使用sendfile功能。
Direct I/O取决于硬盘的块大小。Nginx可以使用directio_alignment directive来设置块大小,在http, server, location中定义
location /video/ {
directio 4m;
directio_alignment 512;
}
512字节适用于大多数情况。如果Linux文件系统是XFS,那么块大小应该为4KB。
异步I/O
异步I/O允许一个进程在不阻塞和等待的情况下进行I/O操作。
aio在http, server和location中定义。Linux2.6.22+和FreeBSD4.3支持aio。
location /data {
aio on;
}
默认情况下,aio是关闭的。在Linux发行版上,要先启用directio后才能启用aio。在FreeBSD上,必须先关闭sendfile才能让aio生效。
如果Nginx没有–with-file-aio这个模块,那么启用aio会导致unknown directive aio错误。
aio可以指定线程数,这个多线程功能只在Linux发行版上才可用,并且只能在epoll, kqueue 或 eventport方法下可用。
为了使用多线程,在编译Nginx时要添加–with-threads选项。然后在/etc/nignx/nignx.conf文件中使用一个全局设置。
thread_pool io_pool threads=16;
http {
....
location /data {
sendfile on;
aio threads=io_pool;
}
}
综合I/O设置
这三个directive可以综合起来实现不同的目标。在下面的配置中,如果文件的大小小于directio指定的大小,那么使用sendfile功能。以异步I/O的方式读取directio服务的文件。
location /archived-data/ {
send file on;
aio on;
directio 4m;
}
Nginx的TCP配置
HTTP是一个应用层协议,在传输层使用TCP协议。在TCP协议中,数据是以一块一块的TCP数据包传输的。Nginx提供了多个directive,可以用来调整TCP栈。
TCP_NODELAY
TCP/IP网络有一个“小数据包”的问题,如果一条信息中只有一个字符,那么网络可能会堵塞。这样的数据包大小为41字节,其中TCP信头40字节,内容为1字节。像这种小数据包,它们的开销是4000%。大量的小数据包可以迅速让网络饱和。
John Nagle发明了Nagle算法,它在一定的时间段,将小数据包暂存,将这些小数据包集合起来,整合为一个数据包发送,在下一个时间段又是如此。这改善了网络传输的效率。时间段通常为200ms。
但值得注意的是,小数据包的问题在telnet的通信过程中仍然存在。在telnet中,每一次敲键都分别在网络发送。不过这跟web服务器没有关联。web服务器发送的文件大多是大数据包,所以它们会被立即发送,而不需要等待200ms。
TCP_NODELAY可以用来关闭Nagle的缓冲算法,将数据尽快地发送。Nginx可以在http, server, location当中定义
http {
tcp_nodelay on;
}
Nginx默认启用了tcp_nodelay。Nginx在keep-alive模式下会使用tcp_nodelay。
TCP_CORK
除了Nagle算法外,Linux内核提供了一个TCP_CORK选项,也可以解决小数据包问题。TCP_CORK告诉TCP栈将数据包附加在另外一个数据包,当数据包饱和时或程序明确地指示发送时再发送。在FreeBSD和Mac OS系统上,TCP_NOPUSH选项相当于TCP_CORK。
Nginx可以在http, server和location模块中定义tcp_nopush。
http {
tcp_nopush on;
}
Nginx默认启用了tcp_nopush
上面的两个directives负责不同的任务。tcp_nodelay可以降低网络延迟,而tcp_nopush可以优化发送的数据包。
同时启用tcp_nopush和sendfile可以确保在传输文件时,内核总是生成最大量的完整TCP数据包以供传输,而最后一个TCP数据包可以是不完整的,这个不完整的数据包
大综合
下面的配置综合了前面所讨论的worker进程配置、I/O配置、TCP配置。
worker_processes 1; #假设CPU为单核
worker_rlimit_nofile 8000;
events {
multi_accept on;
use epoll;
worker_connections 1024;
}
http {
sendfile on;
aio on;
directio 4m;
tcp_nopush on;
tcp_nodelay on;
}
做完上述配置后,重启nginx
sudo service nginx restart
性能测试对比
做上述修改前,主页负载测试结果如下,平均花费4814毫秒加载完主页。

做修改后,主页负载测试结果如下,平均花费3851毫秒加载完主页,节约1秒钟。不过从下图中可以看到虽然样本时间下降了,延迟却有所上升。当连接数少时,延迟在500ms左右,而当连接数多时,延迟却升到了2000多。

调试
nginx设置,如location的匹配顺序、rewrite等,单从手册和别人的说明,都不如调试更加直观。调试,能直观的看到location等配置的过程,效果,这样就能更加确定匹配方式。个人目前,想到一下方式进行调试。
set方法
通过set来测试是否匹配到特殊的指令块,如if、location等。
server{
set $loginfo 0;
if (!-e $request_filename){
set $loginfo 1234;
rewrite ^/(.*) /index.php/$1 last;
}
add_header 'Log-Info' $loginfo;
}
调试过程:

查看匹配结果
http://dev.announce.com:30029/admin/index/index.html

但是,如果proxy_pass返回错误,结果标记 Loginfo 就看不到了。
add_header
add_header不能在if模块内设置,但是可以通过set 变量,然后将此变量在通过add_header的来显示。
deny
deny all。先匹配到的location会直接返回。
return
return 302;
return 403;
配合nodejs
调试环境搭建
使用docker-compose等方式。