zl程序教程

您现在的位置是:首页 >  工具

当前栏目

Nginx学习笔记,持续记录

笔记学习Nginx 记录 持续
2023-06-13 09:17:02 时间

使用过程中发现很多新问题,在这里记录一下;

Nginx学习笔记(一)

https://nicen.cn/1004.html

Nginx配置文件块层级

events块

nginx events 模块主要是nginx 和用户交互网络连接优化的配置内容

  • accept_mutex,这个配置主要可以用来解决常说的"惊群"问题。大致意思是在某一个时刻,客户端发来一个请求连接,Nginx后台是以多进程的工作模式,也就是说有多个worker进程会被同时唤醒,但是最终只会有一个进程可以获取到连接,如果每次唤醒的进程数目太多,就会影响Nginx的整体性能。如果将上述值设置为on(开启状态),将会对多个Nginx进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。
  • multi_accept,用来设置是否允许同时接收多个网络连接
  • worker_connections;用来配置单个worker进程最大的连接数,nginx 默认连接数是1024
  • use,用来设置Nginx服务器选择哪种事件驱动来处理网络消息,此处所选择事件处理模型是Nginx优化部分的一个重要内容,method的可选值有select/poll/epoll/kqueue等,之前在准备centos环境的时候,强调过要使用linux内核在2.6以上,就是为了能使用epoll函数来优化Nginx

另外这些值的选择,我们也可以在编译的时候使用:–with-select_module、–without-select_module、 --with-poll_module、–without-poll_module来设置是否需要将对应的事件驱动模块编译到Nginx的内核。

events{ 
	accept_mutex on; 
	multi_accept on; 
	worker_commections 1024; 
	use epoll; 
}

upstream块

参考:https://blog.csdn.net/carefree2005/article/details/121242227

upstream定义在http块内 ,Nginx的负载均衡功能依赖于ngx_http_upsteam_module模块,所支持的代理方式包括proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass和grpc_pass。ngx_http_upstream_module模块有允许Nginx定义一组或多组服务组,使用的可以通过proxy_pass代理方式把网站的请求发送到事先定义好的对应upstream组的名字上。通常用于实现负载均衡 。

upstream backend {
    server backend1.example.com       weight=5;  #执行realserver,可以赋权重
    server backend2.example.com:8080; 
    server unix:/tmp/backend3;

    server backup1.example.com:8080   backup;  #backup表示该节点为热备节点,激活节点失效时启用
    server backup2.example.com:8080   down;  #下线服务器,可以在real服务器需要维护时配置
}

server {
   listen	80;  #listen、server_name这些正常配置
   server_name	www.test.com;
   location / {
        proxy_pass http://backend;  ##反向代理执行定义的upstream名字
    }
}

1. 调度算法

调度算法一般分为两类:第一类为静态调度算法,即负载均衡器根据自身设定的规则进行分配,不需要考虑后端节点服务器的情。第二类为动态调度算法,即负载均衡器会根据后端节点的当前状态来决定是否分发请求。

1.1 轮询

rr 轮循即round robin 默认调度算法,静态调度算法。客户端请求顺序把客户端的请求逐一分配到不同的后端节点服务器,这相当于 LVS 中的 rr 算法,如果后端节点服务器宕机(默认情况下nginx 只检测80端口)。宕机的服务器会自动从节点服务器池中剔除,以便客户端的用户访问不受影响。新的请求会分配给正产的服务器。

1.2权重轮询

wrr即weight 权重轮循,静态调度算法。在 rr 轮循算法的基础上加上权重,即为权重轮循算法,当使用该算法时,权重和用户访问成正比,权重值越大,被转发的请求也就越多。可以根据服务器的配置和性能指定权重值大小,有效解决新旧服务器性能不均带来的请求分配问题。

upstream  show {
    server 192.168.0.141 weight=1;
    server 192.168.0.142 weight=2; 
}

1.3IP哈希

ip_hash是静态调度算法,每个请求按客户端 IP 的 hash 结果分配,当新的请求到达时,先将其客户端IP通过哈希算法哈希出一个值,在随后的客户端请求中,客户 IP 的哈希值只要相同,就会被分配至同一台服务器,该调度算法可以解决动态网页的 session 共享问题,但有时会导致请求分配不均,即无法保证 1:1 的负载均衡,因为在国内大多数公司都是 NAT 上网模式,多个客户端会对应一个外部 IP,所以,这些客户端都会被分配到同一节点服务器,从而导致请求分配不均。LVS 负载均衡的 -P 参数、keepalived 配置里的 persistence_timeout 50 参数都类似这个 Nginx 里的 ip_hash 参数,其功能均为解决动态网页的 session 共享问题。

upstream show {
    ip_hash;
    server 192.168.0.141 ;
    server 192.168.0.142 ;
}

1.4最小连接数

least_conn是动态调度算法,会根据后端节点的连接数来决定分配情况,哪个机器连接数少就分发。

1.5最短响应时间

最短响应时间(fair)调度算法是动态调度算法,会根据后端节点服务器的响应时间来分配请求,响应时间端的优先分配。这是更加智能的调度算法。此种算法可以依据页面大小和加载时间长短只能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx 本身是不支持 fair 调度算法的,如果需要使用这种调度算法,必须下载 Nginx 的相关模块 upstream_fair。

upstream show {
    fair;
    server 192.168.0.141 ;
    server 192.168.0.142 ;
}

1.5url_hash算法

url_hash算法是动态调度算法,按访问 URL 的 hash 结果来分配请求,使每个 URL 定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率命中率。(多用于后端服务器为缓存时的场景下)Nginx 本身是不支持 rul_hash的,如果需要使用这种调度算法,必须安装 Nginx 的hash 模块软件包。

upstream show {
    server 192.168.0.141 ;
    server 192.168.0.142 ;
    hash $request_uri;
    hash_method crc32;
}

1.6least_conn

负载均衡指令least_conn的含义,按照nginx文档的说法:

Specifies that a group should use a load balancing method where a request is passed to the server with the least number of active connections, taking into account weights of servers. If there are several such servers, they are tried in turn using a weighted round-robin balancing method.

意思是请求将被传递给当前拥有最少活跃连接的server,同时考虑权重weight的因素。

upstream show {
    least_conn;
    server 192.168.0.141 ;
    server 192.168.0.142 ;
}

查看nginx源码,在http模块ngx_http_upstream_least_conn_module.c中,决定“最少连接”的逻辑是这样的:

if (best == NULL || peer->conns * best->weight < best->conns * peer->weight){
   best = peer; //选择peer为当前server
   many = 0;
   p = i;
} else if (peer->conns * best->weight == best->conns * peer->weight) {
   many = 1;
} 

http块常用配置指令

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

server_names_hash_bucket_size 128;

client_header_buffer_size 32k;  
large_client_header_buffers 4 32k;
client_max_body_size 1024m;

sendfile on;
sendfile_max_chunk 512k;
tcp_nopush on;

keepalive_timeout 60;

tcp_nodelay on;

fastcgi_connect_timeout 300; # http server location,指定nginx与后端fastcgi server连接超时时间
fastcgi_send_timeout 3600; # http server location,指定nginx向后端传送请求超时时间(指已完成两次握手后向fastcgi传送请求超时时间)
fastcgi_read_timeout 300; # http server location,指定nginx接受后端fastcgi响应请求超时时间 (指已完成两次握手后nginx接受fastcgi响应请求超时时间)
fastcgi_buffer_size 64k; # http server location,指定nginx读取fastcgi响应第一部分需要用多大的缓冲区,这个值表示将使用一个64kb的缓冲区响应第一部分应答(应答头),可以设置为fastcgi_buffers缓存区大小
fastcgi_buffers 4 64k; #  http server location,指nginx需要用多大的缓冲区缓冲fastcgi的应答请求(整个应答),如果一个php脚本所产生的页面大小为256kb,
#那么会分配4个64kb缓冲区来缓存,如果页面大于256kb,那么大于256kb的部分会缓存到fastcgi_temp指定的路径中,但是因为内存中数据处理远快于磁盘, 所以这个值应该为站点
#中php所产生的页面大小的中间值, 如果站点大部分php脚本产生的页面为:256kb, 那么可以设置成成"8 16k 4 64k"

fastcgi_busy_buffers_size 128k; # http server location,整个数据请求需要多大的缓存区,建议设置为fastcgi_buffers值的两倍
fastcgi_temp_file_write_size 256k; # http server location,写入缓存文件使用多大的数据块,默认值是fastcgi_buffer值的2倍

fastcgi_param SERVER_NAME $server_name; # http server location,传递参数 

gzip on;
gzip_min_length  1k;
gzip_buffers     4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types   text/plain application/javascript application/x-javascript text/javascript text/css application/xml application/xml+rss;
gzip_vary on;
gzip_proxied   expired no-cache no-store private auth;
gzip_disable   "MSIE [1-6].";

#limit_conn_zone $binary_remote_addr zone=perip:10m;
##If enable limit_conn_zone,add "limit_conn perip 10;" to server section.

server_tokens off; #server_tokens在打开的情况下会使404页面显示Nginx的当前版本号。
access_log  /home/wwwlogs/access.log;

1.mime.types

include引入该文件内声明的MIME类型。application/octet-stream代表直接当做文件返回给前端下载。

2.default_type 

上面MIME类型没有匹配时,默认的处理方式

3.server_names_hash_bucket_size

为了提高快速寻找到相应server name的能力,Nginx使用散列表来存储server name,server_names_hash_bucket_size设置每个散列桶占用的内存大小。

4.client_header_buffer_size

Syntax: client_header_buffer_size size;
Default: client_header_buffer_size 1k;
Context: http, server

用于http, server块中,假设client_header_buffer_size的配置为1k,如果(请求行+请求头)的大小如果没超过1k,放行请求。如果(请求行+请求头)的大小如果超过1k,则以large_client_header_buffers配置为准 。

5.large_client_header_buffers

Syntax: large_client_header_buffers number size;
Default: large_client_header_buffers 4 8k;
Context: http, server

假设large_client_header_buffers的配置为4 8k,则对请求有如下要求

请求行(request line)的大小不能超过8k,否则返回414错误 请求头(request header)中的每一个头部字段的大小不能超过8k,否则返回400错误(实际是494错误,但nginx统一返回400了) curl -H "header1=aaa" -H "header2=bbb" -v http://127.0.0.1/,这里的header1=xxx和header2=xxx就是请求头中的头部字段 (请求行+请求头)的大小不能超过32k(4 * 8k)

6.client_max_body_size

表示HTTP请求包体的最大值。

语法:client_max_body_size size;
默认:client_max_body_size 1m;
配置块:http、server、location

7.sendfile

简单来说就是启用sendfile()系统调用来替换read()和write()调用,减少系统上下文切换从而提高性能,当 nginx 是静态文件服务器时,能极大提高nginx的性能表现,而当 nginx 是反向代理服务器时,则没什么用了。

8.sendfile_max_chunk

每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。

9.tcp_nopush 

在 nginx 中,tcp_nopush 配置和 tcp_nodelay “互斥”。它可以配置一次发送数据的包大小。也就是说,它不是按时间累计 0.2 秒后发送包,而是当包累计到一定大小后就发送。 在 nginx 中,tcp_nopush 必须和 sendfile 搭配使用。

10.keepalive_timeout

语法:keepalive_timeout time
默认:keepalive_timeout 75;
配置块:http、server、location

keepalive连接在闲置超过一定时间后(默认75秒),服务器和浏览器都会去关闭这个连接。

11.tcp_nodelay

Nginx的 TCP_NODELAY 选项使得在打开一个新的 socket 时增加了TCP_NODELAY选项。

这时会造成一种情况:终端应用程序每产生一次操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据以及40个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞。 为了避免这种情况,TCP堆栈实现了等待数据 0.2秒钟,因此操作后它不会发送一个数据包,而是将这段时间内的数据打成一个大的包。这一机制是由Nagle算法保证。

Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为默认配置了,但有些场合下把这一选项关掉也是合乎需要的。现在假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。

现在假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。

如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的 TCP_NODELAY = on 选项来完成,这样就禁用了Nagle 算法。(不需要等待0.2s)

对接PHP

在对应的server块内加入如下配置:

location ~ [^/].php(/|$)
{
	try_files $uri =404;
        fastcgi_pass  unix:/tmp/php-cgi.sock; #有多种形式
	fastcgi_index index.php;
	include fastcgi.conf;
}

1.php-fpm

nginx和php-fpm在同一台服务器上,这时可以直接用unixsocket进程间通信,不走tcp端口通信,可以节约创建连接的时间,从而提高性能。

[www]
listen = /tmp/php-cgi.sock
# 引入指定配置文件
include=/srv/vhost/*/php-fpm.conf[/shell]
# 通过php-fpm覆盖php.ini
php_admin_value[disable_functions]=phpinfo

#覆盖php.ini
php_value:设定指定的值,但不能设定布尔值。
php_flag:用来设定布尔值的配置指令
php_admin_value:设定指定的指令的值。不能用于 .htaccess 文件。任何用 php_admin_value 设定的指令都不能被 .htaccess 或 virtualhost 中的指令覆盖。
php_admin_flag:用来设定布尔值的配置指令。不能用于 .htaccess 文件。任何用 php_admin_flag 设定的指令都不能被 .htaccess 或 virtualhost 中的指令覆盖。

Nginx端传递参数,在fpm中增加值:

# 覆盖php-fpm配置
fastcgi_param PHP_ADMIN_VALUE "open_basedir=/home/wwwroot/:$document_root/:/tmp/:/proc/";

Nginx补充

1. location匹配的优先级

先大致分为以下几种匹配类型:

  • location = /url ,= 开头表示精确匹配,只有匹配上才生效
  • location /url.txt ,完全匹配
  • location ^~ /url ,^~ 开头表示前缀匹配
  • location ~ pattern, ~ 开头表示区分大小写的正则匹配
  • location ~* pattern ,~*开头表示不区分大小写的正则匹配
  • location /url ,不带任何开头的也表示前缀匹配,但是优先级在正则匹配之后
  • location /, 通配匹配,任何未匹配到其他location的请求都会匹配到,相当于switch的default

实际测试,访问http://192.168.1.23/assets/a.txt:

#精确匹配
location = /assets/a.txt {
  return 200 1;
}
#正则匹配区分大小写
location ~ /assets(.*) {
  return 200 4;
}
#正则匹配不区分大小写
location ~* /assets(.*) {
  return 200 4;
}
#完全匹配
location /assets/a.txt {
  return 200 2;
}
#前缀匹配
location ^~ /assets {
  return 200 3;
}
#前缀匹配
location /assets {
  return 200 5;
}
#default
location / {
  return 200 6;
}

最终测试结果为1 -> 4 -> 2 -> 3/5 -> 6 ,并且与顺序无关。

指令大全

1.add_header

add_header name value [always]

add_header 指令用于添加返回头字段,当且仅当状态码为200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0)时有效。

如果提供了第三个参数 always,那么无论状态码是多少,都会生效。

2.root & alias

location /img/ {
    alias /var/www/image/;
}

#若按照上述配置的话,则访问/img/目录里面的文件时,ningx会自动去/var/www/image/目录找文件

location /img/ {
     root /var/www/image;
}

#若按照这种配置的话,则访问/img/目录下的文件时,nginx会去/var/www/image/img/目录下找文件。

alias是一个目录别名的定义,root则是最上层目录的定义。有一个重要的区别是alias后面必须要用“/”结束,否则会找不到文件的,而root则可有可无

3.user命令

user命令用于配置运行Nginx服务器的worker进程的用户和用户组。 Nginx只能访问该用户所能访问的目录。

Syntax: user user [group];
defualt: user nobody nobody;
Context: main

4.worker_processes指令

指定Nginx worker 进程个数 ,每个 worker 进程都是单线程的进程,它们会调用各个模块以实现多种多样的功能。如 果这些模块确认不会出现阻塞式的调用,那么,有多少 CPU 内核就应该配置多少个进程;反 之,如果有可能出现阻塞式调用,那么需要配置稍多一些的 worker 进程。

worker_cpu_affinity 配置来绑定 CPU 内核。为什么要绑定 worker 进程到指定的 CPU 内核呢 ? 假定每一个 worker 进程都是非常繁忙 的 , 如果多个 worker 进程都在抢同一个 CPU , 那么这就会出现同步问题 。 反之 , 如果每一 个 worker 进程都独享一个 CPU , 就在内核的调度策略上实现了完全的并发。

2个CPU内核
worker_processes 2;
worker_cpu_affinity 01 10;

4个CPU内核
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;

8个CUP内核
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

5.listen指令

listen 443 ssl http2; #启用ssl,http2
listen 80 default_server reuseport; #default默认主机

reuseport的作用:https://zhuanlan.zhihu.com/p/452932307 

6.gzip

Nginx Gzip分为动态压缩和静态压缩,动态压缩时,压缩由Nginx完成,静态压缩时直接调用静态文件。

gzip_static on;  

7.internal

internal,默认值:no,使用字段: location

internal指令指定某个location只能被“内部的”请求调用,外部的调用请求会返回”Not found” (404)

“内部的”是指下列类型:

  • 指令error_page重定向的请求
  • ngx_http_ssi_module模块中使用include virtual指令创建的某些子请求。
  • ngx_http_rewrite_module模块中使用rewrite指令修改的请求。

问题记录

1.不带斜杠访问目录

当请求URL后面没有 / ,Nginx 目录中没有对应的文件,就会自动进行 301 并加上 /;

如果是 Nginx 版本号大于 1.11.8,可以考虑用 absolute_redirect off;

absolute_redirect ,server_name_in_redirect和port_in_redirect三个设置项中,根据Nginx的源代码中Response Header处理算法逻辑,Nginx能够控制重定向的关键配置项是:absolute_redirect,在整个Nginx代码中,absolute_redirect在控制在Response Header如何增加Location url。

absolute_redirect设置成On,则生成absolute url作为Location url。absolute_redirect设置成Off,则生成relative url作为 Location url。

absolute url是包含完整信息的url,比如http://www.test.com:8080/index/1.html 这样的URL地址,relative url 则省略了服务器名字和端口号,比如 /index/1.html

因为relative url没有端口号,没有Host名字,所以absolute_redirect 设置On的时候,server_name_in_redirect和port_in_redirect两项设置才会起作用。

实际案例

1. 拒绝访问

需求是只允许指定IP能够访问这个资源,使用了deny和allow,测试结果如下:

#所有请求都被拒绝
location ~ /assets {
    deny all;
    allow 192.168.1.23;
}
#192.168.1.23访问成功,其他请求被拒绝
location ~ /assets {
    deny all;
    allow 192.168.1.23;
}

#这样的话所有请求结果都是return的结果
location ~ /assets {
 return 200 666; #放这里也是
 deny all;
 allow 192.168.1.23;
 return 200 666;
}

正常deny之后应该是返回403,现在返回的404也不知道哪的问题。

2. 反向代理加/和不加/

在使用Nginx反向代理的时候,如果想针对某个目录去做反向代理,如下:

location /proxy {
	proxy_pass http://www.woshipm.com;
}

此时访问 /proxy 反向代理访问的实际链接将会变成   http://www.woshipm.com/proxy;改成如下之后:

location /proxy { 
     add_header Access-Control-Allow-Origin *;
     add_header Access-Control-Allow-Methods *;
     add_header Access-Control-Allow-Headers *;
     proxy_pass http://www.woshipm.com/;
}

此时访问 /proxy 反向代理访问的实际链接将会变成  http://www.woshipm.com;案例:

location /websocket {
	proxy_pass http://websocket.nicen.cn:5703;
	proxy_http_version 1.1;
	proxy_read_timeout 3600s;
	proxy_send_timeout 300s;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";
}

再来一个案例:

location /{
     add_header Access-Control-Allow-Origin *;
     add_header Access-Control-Allow-Methods *;
     add_header Access-Control-Allow-Headers *; 
     proxy_pass http://api.test.com;
     if ( $request_method  ~* OPTIONS ){
        return 200 $request_method;
     }
}

location /ws {
     add_header Access-Control-Allow-Origin *;
     add_header Access-Control-Allow-Methods *;
     add_header Access-Control-Allow-Headers *; 
     proxy_pass http://api.test.com;
     proxy_http_version 1.1;
     proxy_read_timeout 3600s;
     proxy_send_timeout 300s;
     proxy_set_header Upgrade $http_upgrade;
     proxy_set_header Connection "upgrade";
}

3. 反向代理发送真实IP

 #以下为新增内容
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;  #获取客户端真实IP
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

4. 变量拼接

${uri}_${args}