hopeless-dream

导航

nginx读书笔记二----nginx配置

NGINX中的进程

nginx使用一个master进程监控管理多个worker进程。master进程负责加载配置、启停等。worker进程负责处理用户请求。

一般情况下,worker进程数与服务器的CPU核心数相等。worker进程之间通过共享内存、原子操作等一些进程间通信机制来实现负载均衡等功能。

nginx采用master-worker的方式运行是因为以下两点原因:

  (1)由于master进程不会对用户请求提供服务, 只用于管理真正提供服务的worker进程, 所以master进程可以是唯一的,因为nginx一般监听在80端口(<1024特权端口一般由root启动),所以master进程的权限要高于worker进程,这样master才能管理worker经常。当任意一个worker进程出现故障导致coredump时,master进程会启动新的worker进程提供服务。

  (2)多个worker进程处理请求不但可以提高服务的健壮性,同时,避免了大量进程处理请求时的进程间上下文切换带来的系统资源消耗。

  (3)在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限 制,worker进程不会进入睡眠状态。因此,当Nginx上的进程数与CPU核心数相等时,进程间切换的代价最小。

多进程模型的原理

Nginx主进程监听外部信号,通过channel将信号传递给工作进程。工作进程之间通过共享内存来实现进程间通信。

信号(signal)

(1)master进程支持的信号量

信号量 说明
TERM(15), INT(2) 快速停止nginx的进程
QUIT(3) 优雅的关闭nginx进程(等所有请求结束后关闭)
HUP(1) 平滑重启,重载配置文件
USR1(10) 重新打开日志文件(日志切割使用)
USR2(12) nginx升级时使用
WINCH(28) 优雅的关闭worker进程

(2)worker进程支持的信号量

  TERM(15), INT(2),QUIT(3),USR1(10),WINCH(28)

channel

Nginx通过信道(channel)来传递外部信号。信道是用socketpair方法使用本机socket实现进程间通信。master进程发送消息通过信道传递给worker进程。这些操作会将接收消息的套接字注册到事件驱动(events)引擎(如epoll)中,当事件引擎监听到master发送的消息,会触发回调函数通知工作进程执行相应操作。

共享内存允许多个进程访问同一个内存地址。其中一个进程修改了内存中的数据后,其他进程都可以使用变更后的内容。可以通过配置文件定义共享内存,配置文件加载后,会将共享内存通过slab进行划分和管理。

Nginx进程调度

由于工作进程是主进程fork出来的,每个工作进程都继承了主进程的socket。所有的工作进程的事件监听列表会共享监听套接字,但是只能有一个工作进程可以建立网络连接,Nginx使用如下调度方法进行协调:

(1)无调度

所有工作进程竞争资源,最终只有一个工作进程可以建立连接。对于系统会产生大量的资源消耗,这就是惊群现象。

(2)互斥锁(1.11.3版本后默认关闭)

每个工作进程都周期性的竞争互斥锁,争抢到锁的进程开始处理HTTP请求,将当前进程的socket注册到时间引擎epoll,其他进程继续处理已经建立的连接,并周期性的轮询互斥锁的状态。当互斥锁释放的时候,进程继续争抢锁。

工作进程最大连接数的1/8与该进程可用连接数的(free_connection)差大于或等于1时,工作进程将放弃本次获取锁的机会,不再接收新的请求,继续处理已经建立的连接。互斥锁有效避免了惊群现象。

对于HTTP短连接,该机制避免了因为锁竞争导致的资源消耗;对于HTTP长链接,互斥锁会将压力集中在少数工作进程,导致进程的负载倾斜QPS下降。

作者说:

  OS may wake all processes waiting on accept() and select(), this is called thundering herd problem. 
This is a problem if you have a lot of workers as in Apache (hundreds and more), but this insensible if you have just several workers as nginx usually has. 
Therefore turning accept_mutex off is as scheduling incoming connection by OS via select/kqueue/epoll/etc (but not accept()).

惊群现象:一般nginx配置工作进程会和cpu数量相同,所以惊群现象不严重。

(3)套接字分片(socket sharding)

由内核分配,每个工作进程都有相同的监听套接字,当有请求连接时,由内核决定哪个工作进程的套接字接收该请求,充分利用了多核系统的性能。需要在配置listen时启用reuseport参数。

性能:套接字>互斥锁>无调度

 NGINX的模块配置

Nginx高度模块化,每个块配置项由一个配置项名和一对大括号组成。

events {
  …
} 
http {
    upstream backend {
        server 127.0.0.1:8080;
} 
    gzip on;
    server {
      …
      location /webstatic {
          gzip off;
            }
        }
}        

eventshttpserverlocationupstream等都是块配置项,所有的事件类配置都要在events块中, httpserver等配置也遵循这个规则。

块配置项可以嵌套。 内层块直接继承外层块,当内外层块中的配置发生冲突时, 究竟是以内层块还是外层块,取决于解析这个配置项的模块

配置项的的语法格式

 配置项名
    配置项值
    配置项值1
    配置项值2
    ...
;

配置项名以空格为分隔符

配置项值可以是数字或字符串和正则表达式。一个配置项可以包含多个值,配置项值之间由空格符分隔。每行配置的结尾要加上“;”

如果配置项值中包括语法符号,如空格,要使用单引号或双引号括起来。如:

log_format main '$remote_addr - $remote_user [$time_local] "$request" ';

Nginx配置文件中的单位

1)空间单位:
K或者k千字节(KiloByte,KB)
M或者m兆字节(MegaByte,MB)

(2)时间单位
ms(毫秒) , s(秒) , m(分钟) , h(小时) , d(天) , w(周, 包含7天) ,M(月, 包含30天) , y(年, 包含365天)

用于调试进程的配置项

守护进程daemon

语法: daemon on|off;
默认: daemon on;

错误日志配置

语法: error_log pathfile level;
默认: error_log logs/error.log error;

错误日志可以定位Nginx出现的故障,可以根据需求设置文件保存路径和输出级别。

日志输出级别的取值范围:debug、 info、notice、 warn、 error、 crit、 alert、emerg

注意:如果日志级别要设定到debug,必须在configure时加入--with-debug参数

对指定客户端输出debug级别日志

语法: debug_connection [IP|CIDR]

此配置项属于事件类配置。必须放在events模块中。CIDR指的是客户端地址段。例如:

events {
   debug_connection 172.19.50.5;
   debug_connection 172.19.50.0/24;
}

如上配置,只有172.19.50.0/24这个网段和172.19.50.5的请求才会输出debug级别日志。其他客户端地址的请求仍然使用error_log配置的日志级别。

限制coredump核心转储文件的大小

语法: worker_rlimit_core size;

 在Linux系统中, 当进程发生错误或收到信号而终止时, 系统会将进程执行时的内存内容(核心映像)写入core文件,作为调试使用。Nginx进程出现一些非法操作(如内存越界) 导致进程直接被操作系统强制结束时, 会生成核心转储core文件, 可以从core文件获取当时的堆栈、 寄存器等信息, 从而帮助我们定位问题。 但这种core文件中的许多信息不一定是用户需要的, 如果不加以限制, 那么可能一个core文件会达到几GB, 这样随便coredumps几次就会把磁盘占满

指定coredump文件生成目录

语法: working_directory path;

需确保worker进程有权限向working_directory指定的目录中写入权限

运行的配置项

嵌入其他配置文件 

语法: include pathfile;

pathfile可以是绝对路径,也可以是相对路径,相对于nginx.conf而言。

worker进程运行的用户和用户组

语法: user username [groupname];
默认: user nobody nobody;

当groupname缺省时,表示用户组名和用户名相同。

如果编译时,指定了--user=username和--group=groupname,那么运行worker进程将引用此username和groupname

指定worker进程可以打开的最大文件描述符数

语法: worker_rlimit_nofile number;

指定worker进程可以打开number个FD

优化性能的配置项

worker进程数

语法: worker_processes number;
默认: worker_processes 1;

一般配置为与主机的CPU核数相等。并且使用下面的worker_cpu_affinity配置来绑定CPU内核

worker进程绑定到指定CPU核心

语法: worker_cpu_affinity cpumask [cpumask...]

因为Linux内核都是抢占式的。当worker进程都比较繁忙时,会抢占同一个cpu,会造成同步问题。

例如:主机有两颗CPU,配置如下

worker_processes 4;
worker_cpu_affinity 10 01;

SSL硬件加速

语法: ssl_engine device;

如果服务器上有SSL硬件加速设备, 那么就可以进行配置以加快SSL协议的处理速度。
可以使用OpenSSL提供的命令来查看是否有SSL硬件加速设备:

[root@test ~]# openssl engine -t

系统调用gettimeofday的执行频率

语法: timer_resolution t;

一般不配置,除非是想要日志文件中打印的时间非常精确才配置

Nginx worker进程优先级设置

语法: worker_priority nice;
默认: worker_priority 0;

用于设置worker进程的nice值提高优先级。

Linux系统按照所有进程的优先级来决定哪个进程先执行。进程所分配的CPU时间片大小也与进程的优先级有关,优先级越高,进程分配到的时间片越大。优先级高的进程占用更多的系统资源。

Linux系统有两种优先级:

  静态优先级:Nice值是进程的静态优先级,取值范围是-20 ~ 19,-20是最高优先级,19是最低优先级,内核进程的nice值通常为-5。其他程序的优先级不应该比-5还小

  动态优先级:内核根据进程的执行情况做出的动态调整

 

事件类配置项

是否打开accept

语法: accept_mutex[on|off]
默认: accept_mutext on;

accept_mutexNginx的负载均衡锁 ,accept_mutex这把锁可以让多个worker进程轮流地、序列化地与新的客户端建立TCP连接。

当某一个worker进程建立的连接数量达到worker_connections配置的最大连接数的7/8时, 会大大地减小该worker进程试图建立新TCP接的机会, 以此实现所有worker进程之上处理的客户端请求数尽量接近。

使用accept锁后到真正建立连接之间的延迟时间

语法: accept_mutex_delay Nms;
默认: accept_mutex_delay 500ms;

在使用accept锁后, 同一时间只有一个worker进程能够取到accept锁。 这个accept锁不是阻塞锁, 如果取不到会立刻返回。

如果有一个worker进程试图取accept锁而没有取到, 它至少要等accept_mutex_delay定义的时间间隔后才能再次试图取锁。

选择事件模型 

语法: use [kqueue|rtsig|epoll|/dev/poll|select|poll|eventport];
默认: use epoll;

每个worker的最大连接数

语法: worker_connections number;

定义每个worker进程可以同时处理的最大连接数。

HTTP核心模块---ngx_http_core_module 

HTTP配置项都必须直属于http块、 server块、 location块、 upstream块或if块等。一个server块就是一台虚机主机,她只处理与之相对应的域名的请求。

域名通过server块中的server_name指定。

监听端口

语法: listen address:port [default_server|[bind|ssl]];
默认: listen
80; 配置块: server

listen后可以只加IP地址、 端口或主机名,例如:

listen 80;
listen 127.0.0.1;
listen *:8000;
listen localhost:8080;
listen 443 default_server ssl;

主机名

语法: server_name name [...];
默认: server_name "";
配置块: server

当有HTTP请求时,Nginx会取出header头中的Host,与每个server中的server_name匹配,匹配到的处理这个请求。有两种情况:

(1)有可能header中的Host与多个server_name匹配,这时需要根据优先级选择处理的server。优先级如下:

1) 首先选择所有字符串完全匹配的server_name, 如www.testweb.com 。
2) 其次选择通配符在前面的server_name, 如*.testweb.com。
3) 再次选择通配符在后面的server_name, 如www.testweb.*4) 最后选择使用正则表达式才匹配的server_name, 如~^\.testweb\.com$。

(2)当Host与server_name都不匹配,根据如下规则进行选择server

1) 优先选择在listen配置项后加入[default|default_server]的server块。
2) 找到匹配listen端口的第一个server块。
3) 如果server_name后跟着空字符串(如server_name "";),那么表示匹配没有Host这个HTTP头部的请求。

location

语法: location [=|~|~*|^~|@] /uri/{...}
配置块: server

根据用户请求中的URI来匹配上面的/uri部分,如果可以匹配,就选择该location块中的配置处理请求。location的匹配规则如下:

1) =表示把URI作为字符串, 以便与参数中的uri做完全匹配
2) ~表示匹配URI时是字母大小写敏感的。
3) ~*表示匹配URI时忽略字母大小写问题
4) ^~表示匹配URI时只需要其前半部分与uri参数匹配即可

uri参数里可以使用正则表达式。

location ~* \.(gif|jpg|jpeg)$ {
    ...
}

注意:location是有顺序的,当一个请求有可能匹配多个location时,这个请求会被第一个location处理

 一般情况下,会在最后一个location中使用/作为参数,它可以匹配所有请求,这样,即使前面的所有location都不匹配,也会由这个"/"进行处理。

文件路径的定义

以root方式设置资源路径

语法:root path;

默认:root html;

配置块:http、server、location、if

  location /data/ {
        root /web/html;
   }

如果请求的URI是/data/index/test.html,那么web服务器将返回/web/html/data/index/test.html文件的内容

alias设置资源路径别名

语法:alias  path;

配置块:location

    location conf {
        alias /usr/local/nginx/conf/;
  }

如果一个请求的URI为/conf/nginx.conf,而实际想访问的是/usr/local/nginx/conf/nginx.conf,就可以使用上面的配置

如果用root的方式来设置,需要配置成如下语句:

    location conf {
        root /usr/local/nginx/;
    }

可以理解为alias就相当于一个软连接,匹配的时候location设置的路径将被丢弃。而root则是根据URI路径做映射。

alias后可以跟正则表达式:如

    location ~ ^/test/(\w+)\.(\w+)$ {
        alias usrlocal/nginx/$2/$1.$2;
    }    

首页设置

语法:index file...;

默认:index index.html;

配置块:http、server、location

index使用ngx_http_index_module模块实现。index后可以跟多个文件参数,这些文件将按顺序匹配。

    location  {
        root path;
        index index.html htmlindex.php /index.php;
    }    

这时,nginx首先尝试访问 path/index.php,如果可以访问就直接返回,否则,再试图访问 path/htmlindex.php

根据HTTP返回码重定向页面

语法:error_page code[code...][=|=answer-code]uri|@named_location
配置块:HTTP、server、location、if

如果请求返回错误码,并且匹配了error_page中设置的code,将重定向到指定的URI中。

error_page 404 404.html;
error_page 502 503 504 50x.html;
error_page 403 http://example.com/forbidden.html;
error_page 404 = @fetch;

注意:虽然重定向了URI,但返回码还是原来的HTTP_CODE。可以通过 "=" 更改返回的错误码。如

error_page 404 =200 empty.gif;
error_page 404 =403 forbidden.gif;

也可以不指定确切的错误码,而是由重定向后实际处理的真实结果来决定, 只要把 “=”后面的错误码换成路径即可,如

error_page 404 = /empty.gif;

如果不想修改URI,只是想让请求重定向到location中处理,如

    location / {
        error_page 404 @fallback;
    }
    location @fallback {
        proxy_pass http://backend;
    }    

这样, 返回404的请求会被反向代理到http://backend 上游服务器中处理

try_files

语法:try_files path1 [path2] uri;

配置块:server、location

try_files后要跟若干路径, 如path1 path2..., 而且最后必须要有uri参数,

意义如下:尝试按照顺序访问每一个path, 如果可以有效地读取, 就直接向用户返回这个path对应的文件结束请求, 否则继续向下访问。

如果所有的path都找不到有效的文件, 就重定向到最后的参数uri上。 因此, 最后这个参数uri必须存在, 而且它应该是可以有效重定向的。 例如: 

    try_files systemmaintenance.html $uri $uri/index.html $uri.html @other;
    location @other {
        proxy_pass http://backend;
    }

表示如果前面的路径都匹配不到,就反向代理至http://backend服务器上。也可以指定错误码和error_page配合使用,如

    location {
        try_files $uri $uri /error.phpc=404 =404;
    }

 

posted on 2020-05-04 01:39  hopeless-dream  阅读(345)  评论(0编辑  收藏  举报