nginx,


进程模型

nginx是基于URL等应用层信息的负载均衡,而四层就是基于IP+端口的负载均衡。

nginx是多进程、多路复用的,有一个master进程和n个worker进程,安装nginx后使用ps命令能够发现:

root@bogon:/server/deployment/apache-tomcat-8.5.39/webapps# ps -ef|grep nginx
root       1581      1  0 Apr05 ?        00:00:00 nginx: master process /data/program/nginx/sbin/nginx
nobody     2099   1581  0 00:04 ?        00:00:00 nginx: worker process

master进程充当整个进程组与用户的交互接口,接收外界信号并向各个worker进程发送信号,同时监控worker进程的运行状态。

master不处理网络事件,不负责业务的执行,只通过管理worker进程来实现服务的重启、平滑升级、配置文件实时生效等功能。

worker进程之间是相互独立的。主要处理客户端和服务器间的数据读写操作,进程阻塞点是在像select()、epoll_wait()等这样的I/O多路复用函数调用处,等待发生数据可读/写事件。

worker进程处理请求过程

master进程首先创建socket,bind并且listen在80端口(所以master进程需要root权限);

然后master进程再fork出多个worker进程,每个worker进程都去accept这个socket,当请求来时,使用锁机制,让抢到锁的worker进程去处理这个请求。

惊群处理

多个worker进程等待同一个socket的连接事件,当这个事件发生时,这些进程被同时唤醒,就是惊群。

nginx解决方案: 使用锁机制,让抢到锁的worker进程去处理这个请求,如果操作系统支持原子整型,会使用共享内存实现原子上锁,否则使用文件上锁。

配置文件

安装nginx后,初始配置文件如下:

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

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

    #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  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
}

配置文件分三段:

main;
events;
http;

main段

用于配置nginx全局指令:

user root root; #配置用户或者组,默认为nobody nobody

#worker_processes 1; #允许生成的进程数,默认为1,一般为CPU核心数或核心数-1

#pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址 

error_log log/error.log debug; #指定日志路径,级别。可放入main段,http段,以及server块,常用级别:debug|info|warn|error

event 段

网络连接相关配置:

events {
    accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on 
  
    multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off 
  
    #use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport,默认epoll 
    
    worker_connections 1024; #每个worker进程最大处理连接数,根据业务量可调 
}

http段

定义web服务器相关配置。可嵌套多个server:

http {
    # ssh相关配置
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #指定密码为openssl支持的格式,Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on; #依赖SSLv3和TLSv1协议的服务器密码将优先于客户端密码
    ssl_ciphers RC4:HIGH:!aNULL:!MD5; #密码加密方式

    #tcp_nopush on; #使用nagle算法,数据包不会立即传送出去,等到数据包最大时一次性的传输,有助于解决网络堵塞
    #tcp_nodelay on; #是否禁用nagle算法,禁用后数据包将立即投递

    client_max_body_size 256m; #上传文件限制,默认1mb,如果上传大文件必须修改此配置
    sendfile on; #允许sendfile方式传输文件,默认为off,可配置在http块,server块,location块
    sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限
    
    #access_log off; #取消服务日志
    # 日志格式,可以根据该格式对日志进行分析
    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 /server/logs/nginx-access.log; #访问日志
    error_log /server/logs/nginx-error.log; # 错误日志
    
    include /etc/nginx/mime.types; #文件扩展名与文件类型映射表
    #default_type application/octet-stream; #默认文件类型,默认为text/plain
    keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块
    
    gzip on; #开启Gzip
    gzip_min_length 1k; #大于1K的才压缩
    gzip_disable "msie6"; #IE6对Gzip不友好
    #压缩文件类型,JavaScript两种写法都写上
    gzip_types text/plain application/x-javascript text/css 
        application/xml text/javascript application/x-httpd-php 
        image/jpeg image/gif image/png;
    gzip_buffers 4 16k; #Gzip buffer,以16k4倍为单位申请缓冲区内存
    #gzip_http_version 1.0; #http版本,应为1.1
    #gzip_comp_level 2; #压缩级别,1-10,数字越大压缩的越好
    #gzip_vary off;# 给CDN和代理服务器使用,针对相同url,可以根据头信息返回压缩和非压缩副本
   
   upstream test {
       server 127.0.0.1:8080; 
       server 192.168.10.121:3333 backup; #热备
   }
   
   error_page 404 https://www.baidu.com; #错误页 
   
   server {
       listen 9090; #监听端口 
       server_name 127.0.0.1; #主机名称,可以使用域名,例如:www.baidu.com
       keepalive_requests 120; #单连接请求上限次数。 

       access_log /server/logs/www.test.net-access.log;
       error_log /server/logs/www.test.net-error.log;

       location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。
           deny 127.0.0.1; #拒绝的ip 
           allow 172.18.5.54; #允许的ip
           root html; #定义服务器的默认网站根目录位置
           index index.php index.html index.htm; #定义首页索引文件的名称
           proxy_pass http://test; #请求转向自定义服务器列表
       }
   }
}

location配置

location / 通用匹配
location = /uri 精准匹配
location ^~ /uri 正则:前缀匹配
location ~ /uri 正则匹配

规则的优先级:

1. 精准匹配是优先级最高
2. 普通匹配(最长的匹配)
3. 正则匹配

其他配置信息

proxy_next_upstream

语法:

proxy_next_upstream [error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 |http_404 | off ];

配置块:

http、server、location

默认情况下,上游服务器一旦开始发送响应数据,Nginx反向代理服务器会立刻把应答包转发给客户端。因此,一旦Nginx开始向客户端发送响应包,不允许切换到下一个服务器继续处理。目的是保证客户端只收到来自同一个服务器的应答。

默认值:

proxy_next_upstream error timeout;

以如下配置举例:

upstream tomcat {
    server 192.168.47.128:8080;
    server 192.168.47.129:8080;
    server 192.168.47.130:8080;
}

例如请求在128执行,proxy_next_upstream默认值表示当128服务器转发出现错误或者超时时,会按照负载均衡策略切换到下一台服务器进行处理,例如129。

改为如下则表示当发生超时、错误、500、404时都切换服务器处理:

proxy_next_upstream error timeout http_500 http_404;

proxy_connect_timeout

用于设置nginx与upstream server的连接超时时间,如果无法在指定时间建立连接,就会报错。默认值:

proxy_connect_timeout 60s;

配置块:

http, server, location

proxy_send_timeout

向后端写数据的超时时间,过了指定时间后端还没有收到数据,连接会被关闭

proxy_read_timeout
从后端读取数据的超时时间,过了指定时间没有读取到数据,那么nginx和后端的连接会被关闭,如果一个请求的处理时间比较长,可以把这个值设置得大一些。

请求头处理

当使用nginx做反向代理时,后端获取的请求头信息实际是nginx的信息,如果需要获取原始请求息,需要在location中做些处理:

server {
    listen 80;
    server_name localhost;
    location / {
        proxy_redirect off;
        proxy_pass_header Server; #指定转发的头部字段
        proxy_set_header Host $http_host; #$http_host与$host表示请求的host头,区别在于$http_host会带有端口号(非80/443)
        proxy_set_header X-Scheme $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.47.128:8080;
    }
}

proxy_redirect

配置块:

http、server、location

当上游服务器返回的响应是重定向或刷新请求(如HTTP响应码是301或者302)时,proxy_redirect可以重设HTTP头部的location或refresh字段。

如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,这时候就可以用proxy_redirect这个指令设置。

假设被代理服务器返回Location字段为:

http://localhost:8000/test/uri/
proxy_redirect http://localhost:8000/test/ http://www.test.com/uri/;

将Location字段重写为:

http://www.test.com/uri/。

或者在代替的字段中不写服务器名:

proxy_redirect http://localhost:8000/test/ /;

这样就达到了代替的效果。

X-real-ip $remote_addr;

可以在web服务器端获得用户的真实ip

X-Forwarded-For $proxy_add_x_forwarded_for;

保存了请求经过流转所经过的服务器地址链。

例如,一个请求经过了两个nginx转发,假设这两台nginx都做了如下设置:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for

到达第一台nginx时,$proxy_add_x_forwarded_for变量的"X-Forwarded-For"部分是空的,只有$remote_addr,$remote_addr的值是用户的ip,于是赋值后X-Forwarded-For的值就是用户的真实IP地址;

到达第二台nginx时,现在的$proxy_add_x_forwarded_for变量的X-Forwarded-For部分包含的是用户的真实IP,$remote_addr部分的值是上一台nginx的ip地址,于是通过这个赋值以后现在的X-Forwarded-For的值就变成了“用户的真实IP,第一台nginx的IP”,依次形成链条。

负载均衡

upstream模块通过简单的调度算法来实现客户端IP到后端服务器的负载均衡,默认算法为轮询。

轮询

upstream tomcat {
    server 192.168.47.128:8080;
    server 192.168.47.129:8080;
    server 192.168.47.130:8080;
}

weight(权重)

指定轮询几率,weight值和访问概率成正比,用于后端服务器性能不均衡情况。

upstream tomcat {
    server 192.168.47.128:8080 weight=2;
    server 192.168.47.129:8080 weight=3;
    server 192.168.47.130:8080 weight=5;
}

ip_hash

将请求的IP做hash,结果是每个请求都会固定发送到唯一的服务器,可以解决session共享问题。

upstream tomcat {
    ip_hash; 
    server 192.168.47.128:8080 max_fails=2 fail_timeout=60s;
    server 192.168.47.129:8080;
    server 192.168.47.130:8080 backup;
}

属性配置

max_fails

请求失败的最大次数,当超过最大次数时,返回proxy_next_upstream模块定义的逻辑。

fail_timeout

max_fails次失败后。暂停的时间。

backup

其他全部的非backup机器down或者忙的时候,才会请求backup机器。

动静分离

通过location模块设置,将静态文件直接返回,而不是通过后端服务器处理,大大减少服务器处理请求的次数,例如对js、css、png等文件设置:

location ~ .*\.(js|css|png|svg|ico|jpg)$ {
   root /server/deployments/static;
}

缓存配置

浏览器缓存将文件保存在客户端,好的缓存策略可以减少对网络带宽的占用,减小服务器的负担。Nginx通过expires设置缓存,格式:

expires 30s|m|h|d

示例:

location ~ .*.(jpg|jpeg|gif|bmp|png|js|css|ico)$ {
    root /server/deployments/static;
    expires 1d;
}

表示缓存过期时间为1天。

防盗链

Nginx配置防盗链语法:

valid_referers none | blocked | server_names | string ...;

配置块:

server, location

示例:

location ~ .*.(jpg|jpeg|gif|bmp|png|js|css|ico)$ {
    valid_referers none blocked 192.168.47.128 www.test.com;
    if ($invalid_referer) {
        return 404;
    }
    root /server/deployments/static;
    expires 1d;
}

跨域配置

location中添加如下配置:

   add_header 'Access-Control-Allow-Origin' '*'; #允许来自所有的访问地址
   
   add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS'; #支持的请求方式
   
   add_header 'Access-Control-Allow-Header' 'Content-Type,*'; #支持的媒体类型

示例:

server {
    listen 80;
    server_name localhost;
    location / {
        proxy_pass http://192.168.47.128:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        add_header 'Access-Control-Allow-Origin' '*'; #允许来自所有的访问地址
        add_header 'Access-Control-Allow-Methods' 'GET,PUT,POST,DELETE,OPTIONS'; #支持的请求方式
        add_header 'Access-Control-Allow-Header' 'Content-Type,*'; #支持的媒体类型
    }
}

ssh配置

upstream test {
    server 192.168.47.128:8080;
}

server {
    listen   443 ssl;
    server_name www.test.net;

    # 服务端认证配置
    ssl_certificate /etc/letsencrypt/live/www.test.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/www.test.net/privkey.pem;

    ssl_client_certificate /root/.ssl/www.test.net-ca.crt; #客户端证书
    ssl_verify_client on; #开启客户端认证

    access_log /server/logs/www.test.net-access.log;
    error_log /server/logs/www.test.net-error.log;

    location / {
        proxy_redirect off;
        proxy_pass_header Server;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header x-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://test;
    }
}

关于证书生成链接。

附:nginx安装

官网下载安装包,解压后首先配置configure:

./configure --prefix=/data/program/nginx

期间会提示安装依赖包,安装:

 apt-get install build-essential
 apt-get install libpcre3 libpcre3-dev
 apt-get install openssl libssl-dev
 apt-get install zlib1g-dev

配置好后,make:

make && make install

到nginx安装目录:

cd /data/program/nginx/

启动和停止:

sbin/nginx
sbin/nginx -s stop

指令参数:

root@bogon:/data/program/nginx/sbin# ./nginx -h
nginx version: nginx/1.14.2
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]

Options:
  -?,-h         : this help
  -v            : show version and exit
  -V            : show version and configure options then exit
  -t            : test configuration and exit
  -T            : test configuration, dump it and exit
  -q            : suppress non-error messages during configuration testing
  -s signal     : send signal to a master process: stop, quit, reopen, reload
  -p prefix     : set prefix path (default: /data/program/nginx/)
  -c filename   : set configuration file (default: conf/nginx.conf)
  -g directives : set global directives out of configuration file

访问:

http://192.168.47.128(nginx所在机器IP)

invalid PID number 处理

执行如下命令:

/usr/local/nginx/sbin/nginx -s reload 

重新读取配置文件后,再启动nginx启动不起来:

root@iZ2ze0rydhf43s74zkmk2cZ:/server/tar# service nginx status
鈼[0m nginx.service - LSB: starts and stops the nginx web server
   Loaded: loaded (/etc/init.d/nginx; bad; vendor preset: enabled)
   Active: active (exited) since Tue 2019-03-26 14:47:14 CST; 30min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 4987 ExecStop=/etc/init.d/nginx stop (code=exited, status=0/SUCCESS)
  Process: 5837 ExecReload=/etc/init.d/nginx reload (code=exited, status=0/SUCCESS)
  Process: 5020 ExecStart=/etc/init.d/nginx start (code=exited, status=0/SUCCESS)

Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ nginx[5837]: Reloading nginx ...
Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ nginx[5837]: nginx: [error] invalid PID number "" in "/var/run/nginx/nginx.pid"
Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ nginx[5837]: nginx not running
Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ nginx[5837]: -e .
Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ nginx[5837]: done
Mar 26 15:10:58 iZ2ze0rydhf43s74zkmk2cZ systemd[1]: Reloaded LSB: starts and stops the nginx web server.
Mar 26 15:11:05 iZ2ze0rydhf43s74zkmk2cZ systemd[1]: Started LSB: starts and stops the nginx web server.
Mar 26 15:14:39 iZ2ze0rydhf43s74zkmk2cZ systemd[1]: Started LSB: starts and stops the nginx web server.
Mar 26 15:15:06 iZ2ze0rydhf43s74zkmk2cZ systemd[1]: Started LSB: starts and stops the nginx web server.
Mar 26 15:17:37 iZ2ze0rydhf43s74zkmk2cZ systemd[1]: Started LSB: starts and stops the nginx web server.

主要报错信息为:

nginx: [error] invalid PID number "" in "/var/run/nginx/nginx.pid"

查看nginx.pid,发现内容为空。解决方法为使用nginx -c重新set配置文件:

[root@localhost nginx] nginx -c /usr/local/nginx/conf/nginx.conf

相关内容