jinhailang / blog

技术博客:知其然,知其所以然
https://github.com/jinhailang/blog/issues
60 stars 6 forks source link

Nginx(WAF 项目)配置优化初探 #28

Open jinhailang opened 6 years ago

jinhailang commented 6 years ago

proxy buffer

引用:

buffer工作原理
首先第一个概念是所有的这些proxy buffer参数是作用到每一个请求的。每一个请求会安按照参数的配置获得自己的buffer。proxy buffer不是global而是per request的。

proxy_buffering 是为了开启response buffering of the proxied server,开启后 proxy_buffers 和proxy_busy_buffers_size 参数才会起作用。

无论proxy_buffering是否开启,proxy_buffer_size(main buffer)都是工作的,proxy_buffer_size所设置的buffer_size的作用是用来存储upstream端response的header。

参考目前大多数的开源 WAF 项目,并没有对这块有特别的调整,即都是使用的默认值

默认 proxy_buffering 是开启的,开启 buffer 能有效的提高请求交互效率,特别是对于客户源站响应比较慢的情况

综上,这块参数调整如下:

proxy_buffer_size 8k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_buffering on;

基础优化

worker_processes 启动 worker 进程数,为了减少上下文切换,最好等于系统内核数,提高处理效率。auto 将自动设置成内核数。 worker_cpu_affinity 将进程绑定到固定的内核。auto 自动绑定到可用的内核。

注意,如果是容器部署的话可能会有问题,因为默认情况下使用主机 CPU 资源是不受限制的,当然你可以启动的时候指定 CPU 使用数量限制。

worker_processes auto;
worker_cpu_affinity auto;

正则优化

开启 pcre_jit

pcre_jit on;

但是,这只是对配置解析时已知的正则开启 “just-in-time compilation” (PCRE JIT)。

如果要在 ngx_lua 模块中启用 PCRE JIT,需要源码编译 pcre 时,指定 --enable-jit,具体见春哥回答

具体编译命令严格按照这种方式编译,完成后,实用工具 ngx-pcrejit(./ngx-pcrejit -p 7566),验证 ngx_lua 是否启用 PCRE JIT

tcp 优化

默认 Nginx 不会立即将足够小的数据包发送出去(Nagle 算法),而是最多等待 0.2s,待数据包达到足够大时才一起发送,类似缓存机制,可以提高网络效率。

tcp_nopush 优化一次发送的数据量,需要与 sendfile 配合使用。

tcp_nodelay 禁用延迟发送(Nagle 算法)。

两者看似矛盾,但是可以一起使用,最终的效果是先填满包,再尽快发送。

tcp_nopush         on;
tcp_nodelay        on;
sendfile           on;

timeout 调整

配置优化如下:

proxy_connect_timeout 65; 
proxy_read_timeout    65;

启用 proxy_cache(待定)

将重复请求的资源缓存到我们(Razor)这边能有效的较少源站压力,且提高客户端响应速度。但是,会带来一定副作用,比如,资源无法及时更新;缓存的资源不够精确,与请求需要的资源不符合等等问题。所以,建议根据源站实际情况,作为 razor 配置项,由用户(管理员)来配置

proxy_cache_path 指定缓存保存路径 expires 设置浏览器缓存失效时间

配置参考:

proxy_cache_path  /data/nginx/cache levels=1:2 keys_zone=one:100m inactive=1d max_size=1g;
proxy_cache_key   $host$uri$is_args$args;

server {
    location / {
        ...
        proxy_cache               one;
        proxy_cache_valid         200 304 10m;
        proxy_cache_valid 301 302 1h;
        proxy_cache_lock          on;
        proxy_cache_lock_timeout  5s;
        #proxy_cache_valid any    1m;
        expires 12h;
        ...
    }

gzip

目前,如果客户端支持压缩编码(带有请求头:Accept-Encoding,浏览器默认都支持),而且用户源站也支持对应的压缩编码格式,则返回的响应就是已经被压缩编码的。

如果,Razor 开启 zip (gzip on;)有效的唯一场景是,客户源站不支持压缩编码格式,我们(Razor)来帮它压缩。因为,客户源站一般都是支持压缩编码的,而且,在我们这边压缩会消耗 CPU,且只能节省我们到客户端的带宽资源,并不划算。而且参考其他 WAF 实现,也都没有开启 gzip。

至于 https://www.oschina.net/question/17_3971 这里说的所有请求,我们这边代理到客户源站时,都添加头(Accept-Encoding),即默认所有的请求客户端都是支持压缩格式的。我认为不妥,虽然目前主流浏览器都是支持解压的,但是,我们要对接各种源站,请求客户端也很多样,万一客户端不支持解压,就可能出现乱码的情况,作为第三方 WAF,不宜替源站作这种决策。

基于以上,这块保持现状,不启用 gzip

reuseport 启用

reuseport 开启端口复用。详见介绍 blog

NGINX 1.9.1 发布版本中引入了一个新的特性 —— 允许套接字端口共享,该特性适用于大部分最新版本的操作系统,其中也包括 DragonFly BSD 和内核 3.9 以后的 Linux 操作系统。套接字端口共享选项允许多个套接字监听同一个绑定的网络地址和端口,这样一来内核就可以将外部的请求连接负载均衡到这些套接字上来。

可以减少多个 Worker 进程接受新连接的竞争,提高多核机器的系统性能。启用后, accept_mutex 配置会失效。

使用示例:

http {
     server {
          listen 80 reuseport;
          server_name  localhost;
          # ...
     }
}

stream {
     server {
          listen 12345 reuseport;
          # ...
     }
}