[date: 2018-04-26 00:13] [visits: 8]

Nginx配置指南

对Nginx的学习并不多,但使用起来特别顺手且心中莫名放心,本文分享一份自己的nginx配置,并选择其中的一些配置项做些说明。

配置文件

nginx配置文件默认存放在/etc/nginx/nginx.conf,里面主要是http上下文的配置内容,而server等内容常被拆分成子文件通过include引入,以下是自己的nginx配置内容:

user root;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 512;
    use epoll;
    accept_mutex on;
}

http {
    include /etc/nginx/mime.types; # mime type表
    default_type text/plain; # response默认content-type
    charset utf8; # 字符编码

    server_tokens off;

    access_log /var/log/nginx/access.log main;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" "$host"';

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_requests 1000000;
    keepalive_timeout 30s;

    lingering_close on;
    lingering_time 30s;
    lingering_timeout 5s;

    client_body_buffer_size 16k;
    client_max_body_size 10m;
    client_header_buffer_size 1k;
    large_client_header_buffers 4 4k;    

    client_header_timeout 15s;
    client_body_timeout 15s;
    send_timeout 15s;

    output_buffers 1 32k;
    postpone_output 1460;

    proxy_http_version 1.1;
    proxy_set_header Connection "";

    map $sent_http_content_type $expires {
        default off;
        text/html -1;
        text/plain max;
        text/css max;
        text/javascript max;
        application/javascript max;
        application/x-javascript max;
        application/json max;
    }

    proxy_cache_path /var/cache/nginx keys_zone=main_cache:4m levels=1:2 max_size=2G;

    limit_req_zone $binary_remote_addr zone=one:2m rate=5r/s;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    include /etc/nginx/default.conf;
}

这份配置自己经常使用,偶尔根据环境不同做些局部调整,下面从作用与目的方面挑选部分内容进行说明。

全局设置

1-4行主要是全局设置,内容比较简单,由于自己的服务器长期只使用root用户,所以这里忽视了一些安全性风险而坚持使用root用户,避免文件权限带来的访问异常。

初学者在刚安装并启动Nginx时,最常见的一个错误就是403 Forbidden,通常原因是因为nginx默认使用一个名为nginx的用户作为work进程的所有者,而所登录用户的home目录往往都有权限限制,导致work进程没有权限访问用户home目录下的资源,从而返回403。

accept_mutex

nginx通常是由一个master进程协调调度多个work进程,当accept_mutex开启时,work进程会轮流接收新连接,此时为每个work进程所分配的负载相对均衡,如果关闭accept_mutex,当一个新的连接到到达时,不同work进程需要以“争夺”的方式获得该连接。当系统处于正常负载下运行,短暂的争夺会导致部分系统资源消耗过高与浪费,所以选择打开accept_mutex,避免这种浪费。

虽然打开accept_mutex可以让多个worker得到的连接相对均衡,却并不一定就是优点,假设存在10个worker,100个连接请求在打开accept_mutex的情况下会分配给每个worker10个,过了1分钟又来了100个连接请求,这时候也许部分worker早就处理好了自己手上10个连接,还有部分worker由于得到的是棘手任务还在继续忙活着处理之前的10个连接,这时候依旧平分这100个连接给10个worker是有些不合适的,可能会导致部分worker负载过高引起其它问题。这个场景完全是自己假想的,只为了表明有这种可能,主观认为几率极微,故不考虑。

server_tokens

平时比较少见但作用非常简单,off表明在响应头Server中隐藏nginx版本信息,可在不想让外界知道更多nginx相关信息时使用。

sendfile

正常如果将一个静态资源发送给客户端,一般需要通过read将数据从硬盘读取到内存,然后通过write将数据从内存写入socket,这个过程的数据流向是:disk -> kernel buffer -> user buffer -> kernel socket buffer -> 协议栈 -> done,整个过程涉及4次user space与kernel space上下文切换以及4次数据拷贝。

sendfile系统调用目的是简化通过网络在两个本地文件之间进行的数据传输过程,其数据流向是:disk -> kernel buffer -> 协议栈 -> done,整个过程只有2次user space与kernel space上下文切换以及2次数据拷贝,相比read/write减少了一半的上下文切换与数据拷贝次数,因而可以提高性能。

nginx中打开sendfile配置选项即可享受sendfile系统调用所带来的优化,但仍需注意该优化仅针对静态资源处理有效,对于反向代理并不起作用,这是因为sendfile中数据源句柄只能是文件句柄,而反向代理的双端都是socket句柄,导致不能使用sendfile。

由于sendfile系统调用导致数据不经过user space,因此与nginx中output-filter存在冲突,比如在同一个上下文中同时打开gzip与sendfile会导致sendfile失效。

tcp_nopush & tcp_nodelay

这两个参数其背后内容特别多,不做详细阐述,简单讲就是:打开tcp_nopush表示会累积小数据包到一定大小后再发送到网络上,而打开tcp_nodelay表示任何小数据包都会直接发送到网络上。

这两个参数看似冲突实则非也,同时打开sendfile,tcp_nopush与tcp_nodelay时,针对资源发送nginx会:

keepalive_requests

这个参数用来控制单个keepalive连接可以发送request的最大数,默认为100,当一个keepalive连接发送超过100个请求时,服务端会断开该连接。想不出需要在单个keepalive连接上限制总request数量的场景,所以这个设置一个较大的值用来表示没有限制。

lingering_*

配置中的三个值其实都是默认值,写出来是为了在配置中显式体现延迟关闭这个特性,当nginx打算关闭一个HTTP连接时,不会立即关闭TCP连接,而是先关闭写然后等待30s或连续5s没有收到数据后完全关闭TCP连接。

postpone_output

延迟客户端的数据传输,尽可能让nginx有至少所指定大小字节的数据要发送时,才开始发送,此选项是在nginx层面所作的与TCP_CORK类似优化。

map $sent_http_content_type $expires

map指令可以将一个变量值按照匹配规则映射到另外一个变量中,这里对响应content-type映射得到不同的过期值,在server中使用如下:

server {
    listen 80;
    server_name cdn.amsimple.com;

    gzip_comp_level 9;
    expires $expires;

    location / {
        root /root/amsimple/release/static;
    }
}

proxy_cache_path

声明一个代理缓存分区,用于缓存反向代理中上游的返回结果,详细护说明可以参考这篇博客,server中的使用:

server {
    # ...
    location ~* \.html$ {
        root /root/amsimple/release/static/html;
        try_files /$uri /$uri/index.html @amsimple;
    } 

    location @amsimple {
        proxy_cache main_cache;
        proxy_cache_valid 200 1h;

        proxy_cache_lock on;
        proxy_cache_use_stale updating;

        proxy_pass http://amsimple;
    }
}

上面的配置针对.html后缀的响应做了缓存处理,因为upstream中资源生成的开销相对较高,而html内容不经常变化,一个小时的缓存时间是可接受的。

limit_req_zone

根据请求IP限制请求频率,在之前的一遍文章中有详细讲述,此处略过。

总结

本来只是打算分享一下自己的nginx配置,但在写文章的过程中不仅对一些知识认识更加深刻,还发现之前的错误(同时声明sendfile与gzip、拼写错误),同时还存在两个疑惑:

这两个疑惑通过Google没有找到好的答案,需要真大神指导才行。