从零开始搭建后台服务 - Nginx(02)

385 阅读4分钟

从零开始搭建后台服务 - Nginx(02)

上一章主要对Nginx进行了学习和搭建,并以一个开源面板软件为背景展开配置探讨从零开始搭建后台服务 - Nginx(01),本章主要整合配置,实践Nginx同步到生产环境。

Nginx操作命令

Nginx 的命令在控制台中输入 nginx -h 就可以看到完整的命令,这里列举几个常用的命令:

yum install ‐y nginx #安装
nginx -v ##版本,我这边当前版本是nginx/1.20.1
nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启 
nginx -s reopen # 重启 
Nginx nginx -s stop # 快速关闭 
nginx -s quit # 等待工作进程处理完成后关闭 
nginx -T # 查看当前 Nginx 最终的配置 
nginx -t -c <配置路径> # 检查配置是否有问题,如果已经在配置目录,则不需要-c

systemctl 是 Linux 系统应用管理工具 systemd 的主命令,用于管理系统,我们也可以用它来对 Nginx 进行管理,相关命令如下:

systemctl start nginx # 启动 
systemctl stop nginx # 停止 
systemctl restart nginx # 重启 
systemctl reload nginx # 重新加载,用于修改配置后 
systemctl enable nginx # 设置开机启动 
systemctl disable nginx # 关闭开机启动 
systemctl status nginx # 查看运行状态

Nginx目录结构

安装后主要就是关注配置目录/etc/nginx,默认日志目录/var/log/nginx/usr/share/nginx/logs
查找目录执行一下命令cd / | find -name 'access.log'
这两个目录可以动态修改路径,方便起见我这准备配置目录结构不变,方便查找,日志目录单独存放。

Nginx入口配置

官方默认配置nginx.conf文件如下,

user nginx; # 运行用户,默认即是nginx,可以不进行设置
worker_processes auto; # Nginx 进程数,一般设置为和 CPU 核数一样
error_log /var/log/nginx/error.log; # Nginx 的错误日志存放目录
pid /run/nginx.pid; # Nginx 服务启动时的 pid 存放位置

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf; 

events {
    worker_connections 1024; # 每个进程允许最大并发数
}

http {
    # 设置日志模式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main; # Nginx访问日志存放位置

    sendfile            on;# 开启高效传输模式
    tcp_nopush          on;# 减少网络报文段的数量
    tcp_nodelay         on;
    keepalive_timeout   65;# 保持连接的时间,也叫超时时间,单位秒(非常重要)
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types; # 文件扩展名与类型映射表
    default_type        application/octet-stream; # 默认文件类型

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf; # 加载子配置项

    server {##这个节点非常重要
        listen       80; # 配置ipv4监听的端口
        listen       [::]:80; # 配置ipv6监听的端口
        server_name  _; # 配置的域名
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html; # 默认404对应的访问页面
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
        location = /50x.html {
        }
    }
}

官方入口配置大致就这么多,上面大意我用中文注释了。但是这样的配置很显然离正常上线还有一定距离,接下来主要来介绍Nginx实际投产中肯定会用到的功能以及配置调优。

Nginx配置结构解析

main
http {
    upstream { … }
    split_clients {…}
    map {…}
    geo {…}
    server {
        if () {…}
        location {
            limit_except {…}
        }
        location {
            location {
            }
        }
    }
    server {
    }
}

以上是Nginx的常用配置,这个配置文件中包含了多个指令块,有些指令块还是重复的,那么遵循的规则是怎样的?接下来会一一剖析。

server {
    listen 8080;
    root /home/geek/nginx/html;
    access_log logs/geek.access.log main;
    location /test {
        root /home/geek/nginx/test;
        access_log logs/access.test.log main;
    }
    location /dlib {
        alias dlib/;
    }
    location / {
    }

例如上面的配置文件,这里面在 server 块和 location 块中都配置了 root 指令,Nginx 的继承规则如下:

  • 子配置不存在时,直接使用父配置块的指令
  • 子配置存在时,覆盖父配置块
server {
    listen [::]:80 ;
    listen 80 ;
    server_name www.okpubg.com;
}
server {
    listen [::]:80 ;
    listen 80 ;
    server_name *.okpubg.com;
}
server {
    listen [::]:80 ;
    listen 80 ;
    server_name *.okpubg*;
}
server {
    listen [::]:80 ;
    listen 80 ;
    server_name ~^(www\.)?(.+)$;
}

以上配置了多条Server配置,在做匹配时是有先后顺序关系:

  • 精确匹配(与顺序无关)
  • 在前的泛域名(与顺序无关)
  • 在后的泛域名(与顺序无关)
  • 按文件中的顺序匹配正则表达式域名
  • default server
  • 第 1 个
  • listen 指定 default
map $args $foo {  
    default 0;  
    debug  1;  
}

以上是map指令,主要作用是创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量,例如上述$args是nginx内置变量,就是获取的请求 url 的参数。如果参数匹配到debug,那么foo的参数被设置为1,否则就是自定义的0.

一个正则表达式如果以 "~" 开头,表示这个正则表达式对大小写敏感。以 “~*”开头,表示这个正则表达式对大小写不敏感

Nginx常用功能和配置调优

根据以往经验,正式投产时服务器一定会面临诸多问题,以下会用最终配置视角来配置Nginx,可以抄作业,但是在抄作业前还是需要了解我为什么要这么做,以及我做了何种测试,如何测试,在文章最后会附上我这边的配置。

问题一:如何支持Https协议

这基本上是最常见的需求。做法很简单,拿到申请到的https秘钥文件xxx.key和xxx.crt证书,上传到服务器目录,再配置下,以下是我这边配置方案:

image.png

image.png

image.png Https协议证书是有过期时间的,因此在签名快要到期的时候申请SSL证书,如何申请,详细如下:
从零开始搭建后台服务(IAA业务)- 装机篇

问题二:如何支持反向代理

这个需求也很常规,几乎所有涉及到的服务,都会通过Nginx进行反向代理,如代理到本机上、代理到另外一台局域网服务器上,关键字是proxy_pass,我的配置示例如下:

image.png

问题三:如何负载均衡

负载均衡主要是在http节点内,一个单独的配置项,可以配置多个ip+端口,但是负载均衡Nginx是有三种不同的负载算法,默认为轮询,就是轮流来,以下是几种分配方式:

  1. 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
  2. weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
  3. ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
  4. fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;

image.png

问题四:如何支持二级/多级域名

设置二级或多级域名之前,你需要在DNS服务器配置好二级域名,然后在域名解析中配置好我们的入口流量IP地址,然后我们在 Nginx 上配置一下虚拟主机的访问监听,就可以拿到从这个二级域名过来的请求了

image.png 如果想所有二级域名都到一个业务上,那么加上通配符server_name *.okpubg.com,多级域名也需要同样的步骤,即:申请域名->DNS服务器配置该域名解析IP->新建一个Server节点server_name加上对应域名;

问题五:如何解决跨域问题

跨域问题本质上是浏览器同源策略的限制,比如a.cn的js不能访问b.cn的js因为他们不在同一个域下,同域解释就是域名相同,端口相同,协议相同,可以通过配置header偷的方式解决,如下所示:

location / {
    # 允许跨域的请求,可以自定义变量$http_origin,*表示所有
    add_header 'Access-Control-Allow-Origin' *;
    # 允许携带cookie请求
    add_header 'Access-Control-Allow-Credentials' 'true';
    # 允许跨域请求的方法:GET,POST,OPTIONS,PUT
    add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT';
    # 允许请求时携带的头部信息,*表示所有
    add_header 'Access-Control-Allow-Headers' *;
    # 允许发送按段获取资源的请求
    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    # 一定要有!!!否则Post请求无法进行跨域!
    # 在发送Post跨域请求前,会以Options方式发送预检请求,服务器接受时才会正式请求
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        # 对于Options方式的请求返回204,表示接受跨域请求
        return 204;
    }
}

问题六:如何保证频繁改动配置时不影响已有配置

配置文件改动是无法避免的事情,因为开发过程中随着业务的增大,势必对应的服务也会越来越多。我的解决方式是解耦配置项,一个业务大单元占用一个域名,一个域名作为一个单独的配置文件对待,而业务下细分的模块为location维度,随着开发的结束location也会在该文件中结束,直接形成生产环境,这样就避免频繁改动的问题。具体到细节如下图所示。

image.png

image.png

image.png

问题七:如何做到动静分离

首先要搞清楚动静分离是什么,比如当浏览器输入www.taobao.com访问淘宝首页时,打开开发者调试工具可以很明显的看到,首页加载会出现100+的请求数,而正常项目开发时,静态资源一般会放入到resources/static/目录下,我们不希望请求资源的时候使用业务服务器,而是静态资源比如CDN,那么此时只需要在一个目录下新建/www/nginx/project/resources/static ,里面存放各资源,配置路径的时候一下配置:

location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css){
    root   /www/nginx/project/resources/static;
    expires 7d;
}

也可以将静态资源上传到CDN服务器中,然后location中配置一个新的upstream指向

问题八:你性能调优都做了什么

主要在于配置字段的调优研究,此处参考了一些成熟的案例,结合自己的经验得出的结果,调优点如下:

root节点
worker_processes auto 这个字段为核心字段,默认值是1,他表明nginx工作进程数,此处最好的设定应该是>cpu的核心数,但是考虑到集群的通用性,不写死为固定数值,而是等于cpu的核心数,8核即8个工作线程。要注意的是: max connections = worker_processes * worker_connections

worker_rlimit_nofile 25000 配置这个字段表明每个worker_processes最多打开多少个文件,最好是大于worker_connections。

events节点
use epoll 配置使用epoll模式,将大幅提高连接数,并且不活跃的连接数也不影响实际活跃的连接数,据称最少提升10000个连接数

worker_connections 24000 默认值是1024,这个值表明一个工作进程最多承载24000个连接,但是需要注意这里包括了client连接和代理服务器的连接数,且应该小于worker_rlimit_nofile的值

accept_mutex on 配置这个节点主要避免发生惊群事件,即有一个连接过来时,工作进程陆续的接收连接,而不是所有进程被激活去抢连接

multi_accept on 启用多路复用,即收到一个连接通知时,接收尽可能多的连接

accept_mutex_delay 150ms 这个是配合multi_accept on使用的,本质是进程间的互斥锁使用时间,比如一个持有mutex锁的worker进程接受并处理请求,其他worker进程等待,过了等待时间下一个worker进程便取得mutex锁,处理请求

http节点
etag off默认是开启,浏览器会发送页面的Etag值,让服务器进行比较,服务器如果发现eTag值没变,则返回304,浏览器则从缓存中获取页面,如果不相等,则重新获取页面,当集群环境下这个会出现不匹配的状况

server_tokens off; 默认开启,配置隐藏Nginx的版本信息,提高安全性

charset utf-8; 设置字符编码为UTF-8

absolute_redirect off; 默认是开启,302重定向后不会带端口,响应头Location变成了相对路径

ignore_invalid_headers off;默认为true,配置off后,head头如果有非法字符,直接返回400(Bad Request)错误

keepalive_timeout 30s;默认数值是75秒,这是很重要的一个数值,常用于keepalive进行多路复用时,能较大成都提升性能,数值高了影响服务端性能,低了无效果,折中取了中间值30s

keepalive_requests 500;默认数值自1.19.10后改为100,keepalive被复用时一个请求能最多被执行500次,超出的部分重新再连,表现为再次进行三次握手的握手环节有时间

tcp_nopush on; 默认不开启,配置这个数值socket连接时不做发送操作,增大吞吐量

tcp_nodelay on;默认不开启,配置这个数值,keepalive时启用该选项,增大吞吐量

sendfile on; 默认不开启,配置用于加速文件传输时使用cp命令取代传统意义的读写

open_file_cache max=300 inactive=15s;
open_file_cache_valid 30s;
open_file_cache_min_uses 1;
open_file_cache_errors on;
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;
上述字段主要配合sendfile使用,表示缓存文件元数据(文件大小、修改时间等等)个数300个,如果15秒内没有被用户访问一次则抛弃掉该缓存,30秒检查一次,这个配置主要是作为缓存文件是提高性能

types_hash_max_size 1024;
server_names_hash_bucket_size 128;
以上是对服务器hash表设定初始容量,程序初始化时全局使用

gzip on;传输过程中开启gzip来压缩response内容
gzip_static on;如果目录下有同名的.gz文件,不需要额外压缩操作,增加性能
gzip_http_version 1.1;压缩http协议的版本,1.1版本,如果是ab测试注意修改为1.0版本
gzip_comp_level 5;数值是1-9,取5正好是CPU和大小的折中,一般可以压缩率能到75%
gzip_proxied any;任何情况下都启用压缩
gzip_vary on;往头信息中添加压缩标识
gzip_buffers 4 16k;基础参数,缓存使用的大小
gzip_min_length 1k;1k以内大小的不做gzip
gzip_disable msie6;ie6不做gzip
gzip是比较重要的优化手段,压缩率取了个折中的数值,使压缩率和时间取了个折中,压缩率一般有60%以上

send_timeout 15;默认值60,传输响应给客户端的超时时间,如果客户端在这段时间内没有收到任何消息,则连接关闭

client_header_timeout 15;默认值60s,表示如果60s内没有收到完整的http request header,则为超时,如果客户端超时,Nginx 返回 HTTP 408

client_body_timeout 15;默认值60s,表示如果连续的60s内没有收到客户端的1个字节,则表示超时 如果客户端超时,Nginx 返回 HTTP 408

client_body_buffer_size 32k;分配给请求数据的32k大小,如果超出则数据先存储到临时文件中

client_body_temp_path client_body_temp 1 2;超出大小时存放的临时目录,比如临时文件是:client_body_temp/7/000001234

client_max_body_size 256m;默认值1m,上传文件时文件最大大小,超出大小则报413错误

client_header_buffer_size 4k;默认值1k,如果(请求行+请求头)的大小如果没超过4k,放行请求,如果超过使用large_client_header_buffers字段

large_client_header_buffers 4 64k;默认值4 8k,表示请求行(request line)的大小不能超过64k,否则返回414错误,请求头(request header)中的每一个头部字段的大小不能超过64k,否则返回400错误,(请求行+请求头)的大小不能超过256k(4 * 64k)
reset_timedout_connection on;默认值false,表示当一个客户端连接超时,其相关的信息可能仍保留在内存中。启用该指令后,连接超时后将清除所有与内存的关联

以下是fastcgi的配置,cgi粗浅点理解就是一套协议,nginx无法执行外部程序,通过cgi协议就能访问外部程序
fastcgi_connect_timeout 300;连接超时时间
fastcgi_send_timeout 300;发送超时时间
fastcgi_read_timeout 300;读取超时时间
fastcgi_buffer_size 64k;buffer大小
fastcgi_buffers 4 64k;buffer容器大小
fastcgi_busy_buffers_size 128k;buffer繁忙时大小
fastcgi_temp_file_write_size 128k;写入时最大大小

以下是https的配置,如果https作为主要是服务那么keepalive可以较高,目前这个数值统一为30秒
ssl_protocols TLSv1.3 TLSv1.2; # TLSv1 TLSv1.1支持https协议
ssl_prefer_server_ciphers on; 这个配置开启意思为由服务端决定哪种加密算法 ssl_early_data on;指令开启 0-RTT的支持
ssl_ecdh_curve X25519:secp384r1:P-256; 配置服务器使用的密码套件和椭圆曲线,具体什么用要熟悉ssl算法流程才能清楚
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; 加密协议与套件 ssl_session_cache shared:SSL:10m;1 MB能存4000个session,所以这种设定可以存储40000
ssl_session_timeout 10m;开启SSL会话功能后,设置客户端能够反复使用存储在缓存中的会话参数时间 ssl_session_tickets on; 这一项存疑,有文章说开启这个会带来问题。功能是开启服务器会对它的所有会话数据(状态)并进行加密,再以票证的方式发回客户端。在接下来的连接中,客户端将票证提交回服务器,由服务器检查票证的完整性,解密其内容,再使用其中的信息恢复

线上环境调优后的配置

/etc/nginx/nginx.conf

#nginx 启动用户,设置一个没有多少特权的用户运行
user nginx;
#nginx 进程数
worker_processes  auto;
#nginx 开启利用多核 CPU
worker_cpu_affinity auto;
#nginx 最多打开文件数
worker_rlimit_nofile 25000;

events {
    #支持大量连接和非活动连接
    use epoll;
    #避免发生惊群现象
    accept_mutex       on;
    #启用多路复用
    multi_accept       on;
    #2个进程抢互斥锁的最少延迟时间
    accept_mutex_delay 150ms;
    #单个后台 worker process 进程的最大并发链接数
    worker_connections 24000;
}

http {
    include conf.d/http.conf;
}

/etc/nginx/conf.d/http.conf

# 基础配置
etag          off;
charset       utf-8;
charset_types
    text/css
    text/plain
    text/vnd.wap.wml
    text/javascript
    text/markdown
    text/calendar
    text/x-component
    text/vcard
    text/cache-manifest
    text/vtt
    application/json
    application/manifest+json;

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

server_tokens                  off;
absolute_redirect              off;
ignore_invalid_headers         off;

# 服务器配置
types_hash_max_size            1024;
server_names_hash_bucket_size  128;

# 性能配置
sendfile                       on;
tcp_nopush                     on;
tcp_nodelay                    on;

# 打开文件配置
open_file_cache                max=300 inactive=15s;
open_file_cache_valid          30s;
open_file_cache_min_uses       1;
open_file_cache_errors         on;
open_log_file_cache            max=1000 inactive=20s valid=1m min_uses=2;

# 存活配置
keepalive_timeout              30;
keepalive_requests             500;

# 客户端 IP 地址
# real_ip_header                 X-Forwarded-For;

# 日志配置
include conf.d/http.d/log_format.conf;

# 客户端配置
include conf.d/http.d/client.conf;

# 代理配置
include conf.d/http.d/proxy.conf;

# Gzip配置
include conf.d/http.d/gzip.conf;

# Brotli配置
#include conf.d/http.d/brotli.conf;

# SSL配置
include conf.d/http.d/ssl.conf;

# fastcgi配置
include conf.d/http.d/fastcgi.conf;

# 主机配置
include conf.d/vhost.d/*.conf;

/etc/nginx/conf.d/http.d/client.conf

# 客户端配置
send_timeout                15;
client_header_timeout       15;
client_body_timeout         15;
client_body_buffer_size     32k;
client_max_body_size        256m;
client_header_buffer_size   4k;
reset_timedout_connection   on;
large_client_header_buffers 4 64k;
client_body_temp_path       client_body_temp 1 2;

/etc/nginx/conf.d/http.d/gzip.conf

# FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。
gzip              on;
gzip_static       on;
gzip_http_version 1.1;
gzip_comp_level   5;
gzip_proxied      any;
gzip_vary         on;
gzip_buffers     4 16k;
gzip_min_length   512;
gzip_disable      msie6;
gzip_types
  text/plain
  text/css
  text/x-script
  text/x-component
  text/x-java-source
  application/javascript
  application/x-javascript
  text/javascript
  text/js
  image/x-icon
  image/x-win-bitmap
  image/vnd.microsoft.icon
  application/x-perl
  application/x-httpd-cgi
  text/xml
  application/xml
  application/atom+xml
  application/rss+xml
  application/json
  multipart/bag
  multipart/mixed
  application/xhtml+xml
  font/eot
  font/ttf
  font/otf
  font/x-woff
  image/svg+xml
  application/vnd.ms-fontobject
  application/x-font-opentype
  application/x-font-truetype
  application/x-font-ttf
  application/ttf
  application/x-ttf
  application/otf
  application/x-otf
  application/truetype
  application/opentype
  application/x-opentype
  application/woff
  application/eot
  application/font
  application/font-woff woff
  application/font-sfnt
;

/etc/nginx/conf.d/http.d/fastcgi.conf

# FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。
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;

/etc/nginx/conf.d/http.d/headers.conf

proxy_set_header Host               $host;
proxy_set_header Upgrade            $http_upgrade;
proxy_set_header Connection         $proxy_connection;
proxy_set_header X-Real-IP          $remote_addr;
proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host   $proxy_x_forwarded_host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto  $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Port   $proxy_x_forwarded_port;
proxy_set_header X-Forwarded-Ssl    $proxy_x_forwarded_ssl;
proxy_set_header Proxy              "";
proxy_redirect     off;
proxy_http_version 1.1;

/etc/nginx/conf.d/http.d/proxy.conf

# 代理配置
proxy_buffering   on;
proxy_buffer_size 8k;
proxy_buffers     100 8k;

# X-Forwarded-Host
map $http_x_forwarded_host $proxy_x_forwarded_host {
  default $http_x_forwarded_host;
  ''      $host;
}

# X-Forwarded-Proto
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
  default $http_x_forwarded_proto;
  ''      $scheme;
}

# X-Forwarded-Port
map $http_x_forwarded_port $proxy_x_forwarded_port {
  default $http_x_forwarded_port;
  ''      $server_port;
}

# X-Forwarded-Ssl
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
  default off;
  https   on;
}

# Connection
map $http_upgrade $proxy_connection {
  default upgrade;
  ''      '';
}

/etc/nginx/conf.d/http.d/ssl.conf

ssl_protocols               TLSv1.3 TLSv1.2; # TLSv1 TLSv1.1
ssl_prefer_server_ciphers   on;
ssl_early_data              on;
ssl_ecdh_curve              X25519:secp384r1:P-256;
ssl_ciphers                 ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;

ssl_session_cache           shared:SSL:10m;
ssl_session_timeout         10m;
ssl_session_tickets         on;

# ssl_stapling              on;
#ssl_stapling_verify         on;
#resolver_timeout            5s;
# ssl_trusted_certificate   ssl/trustchain.crt;
#resolver                    119.29.29.29 233.5.5.5 valid=300s;

/etc/nginx/conf.d/http.d/log_format.conf

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

log_format upstream_time '$remote_addr - $remote_user [$time_local] '
                         '"$request" $status $body_bytes_sent '
                         '"$http_referer" "$http_user_agent"'
                         'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"';

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

# QUIC
#log_format quic '$remote_addr - $remote_user [$time_local] "$request" '
#                '$status $body_bytes_sent "$http_referer" '
#                '"$http_user_agent" "$quic"';

# referrence from http://ltsv.org
log_format ltsv 'vhost:$host\t'
                'host:$remote_addr\t'
                'user:$remote_user\t'
                'time:$time_local\t'
                'method:$request_method\t'
                'uri:$request_uri\t'
                'protocol:$server_protocol\t'
                'status:$status\t'
                'size:$body_bytes_sent\t'
                'referer:$http_referer\t'
                'ua:$http_user_agent\t'
                'reqtime:$request_time\t'
                'apptime:$upstream_response_time\t'
                'cookie:$http_cookie\t'
                'set_cookie:$sent_http_set_cookie\t'
                'upstream_addr:$upstream_addr\t'
                'upstream_cache_status:$upstream_cache_status';

log_format json escape=json '{'
                '"host": "$host",'
                '"remote": "$remote_addr",'
                '"user": "$remote_user",'
                '"time": "$time_iso8601",'
                '"method": "$request_method",'
                '"uri": "$request_uri",'
                '"protocol": "$server_protocol",'
                '"status": $status,'
                '"size": $body_bytes_sent,'
                '"referrer": "$http_referer",'
                '"ua": "$http_user_agent",'
                '"reqtime": "$request_time",'
                '"forwardedfor": "$http_x_forwarded_for",'
                '"cache": "$upstream_cache_status",'
                '"nginx_access": true'
                '}';

# Refference from Graylog3 nginx + Docker content pack
log_format gelf_json escape=json '{'
                '"time": "$time_iso8601",'
                '"remote_addr": "$remote_addr", '
                '"connection": "$connection", '
                '"connection_requests": $connection_requests, '
                '"pipe": "$pipe", '
                '"body_bytes_sent": $body_bytes_sent, '
                '"request_length": $request_length, '
                '"request_time": $request_time, '
                '"response_status": $status, '
                '"request": "$request", '
                '"request_method": "$request_method", '
                '"host": "$host", '
                '"upstream_cache_status": "$upstream_cache_status", '
                '"upstream_addr": "$upstream_addr", '
                '"http_x_forwarded_for": "$http_x_forwarded_for", '
                '"http_referrer": "$http_referer", '
                '"http_user_agent": "$http_user_agent", '
                '"http_version": "$server_protocol", '
                '"remote_user": "$remote_user", '
                '"http_x_forwarded_proto": "$http_x_forwarded_proto", '
                '"upstream_response_time": "$upstream_response_time", '
                '"nginx_access": true'
                '}';


# 日志位置配置
error_log   logs/error.log;
access_log  logs/access.log json;

以上是基本不太需要变动的配置项,接下来就是主要变动的配置模板

配置模板

/etc/nginx/conf.d/vhost.d/templates/default.conf

server {
  listen [::]:80 default_server deferred;
  listen 80 default_server deferred;

  server_name _;

  return 301 https://$host$request_uri;
}

server {
  listen [::]:443 ssl http2 default_server;
  listen 443 ssl http2 default_server;

  server_name _;

  return 444;
}

测试

测试环境主要围绕模拟生产环境搭建,即Nginx + Dokcer Go Web服务

image.png

KeepAlive Timeout测试

image.png

image.png

Nginx查询连接数输入命令:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

#输出:
ESTABLISHED 8 #8个并发数
TIME_WAIT 1  #等价于keepalive的连接数
CLOSED //无连接是活动的或正在进行
LISTEN //服务器在等待进入呼叫
SYN_RECV //一个连接请求已经到达,等待确认
SYN_SENT //应用已经开始,打开一个连接
ESTABLISHED //正常数据传输状态/当前并发连接数
FIN_WAIT1 //应用说它已经完成
FIN_WAIT2 //另一边已同意释放
ITMED_WAIT //等待所有分组死掉
CLOSING //两边同时尝试关闭
TIME_WAIT //另一边已初始化一个释放
LAST_ACK //等待所有分组死掉

语法详解:

  • netstat -n 不解析名称
  • awk '/^tcp/' 代表搜索行首tcp
  • {++S[NF]} NF 最后一列值,S[] 数组,S[LISTEN]默认为0,++S[LISTEN]用来统计出现LISTEN的个数
  • {for(a in S) print a,S[a]} 表示打印S[]列表

开启1000个线程同时连接,测试:

image.png

image.png

开启500个线程同时连接:测试: image.png

image.png

过了keepalivetime时间后,回收连接

image.png

说明keepalivetimeout如预期

压力测试:

image.png

image.png

image.png

image.png

全部换位Http协议后:

image.png

image.png

image.png

循环测试,500个线程在5秒内执行完,循环50次:

image.png

image.png

image.png

循环测试,2000个线程在5秒内执行完,循环10次:

image.png

image.png

image.png

最后

Nginx暂时到这里结束,实际上以上测试还有较大问题,其中最关键一点,1秒钟单台机器开1000个线程,这实际上和线上环境差别较大,因为耗时你不能确定是你本机开了较多线程的原因还是Nginx处理就如此,比如我开测试工具开,延迟300ms乍看下来已经负载到较差的情况了,实际上开浏览器时依然还是50ms以内,这说明本身测试工具就有一定的问题,只能是后面同样的机器下做对比测试会比较好