Nginx概述

Nginx是什么

Nginx是一个高性能的 HTTP 和反向代理服务器,特点是占有内存少,并发能力强,事实上 Nginx 的并发能力确实在同类型的网页服务器中表现较好,Nginx官网:点击这里

Nginx能干什么

Nginx 可以作为静态页面的 web 服务器,同时还支持 CGI 协议的动态语言,比如 perl、php 等,使用Nginx可以继续做反向代理、负载均衡、动静分离等服务。

准备工作

Nginx安装

环境

  • CentOS-7
  • VMware-workstation-15

安装依赖

yum install -y gcc gcc-c++ make libtool wget pcre pcre-devel zlib zlib-devel openssl openssl-devel

)

安装Nginx

  1. 下载从官网下载tar.gz文件
     title=
  2. 解压:tar -zxvf nginx-1.18.0.tar.gz,根据文件名来。
  3. 编译安装:

    cd nginx-1.18.0
     ./configure
     make && make install

Nginx常用命令

安装完成后的路径为:/usr/local/nginx
nginx位于/usr/local/nginx/sbin
所以以下命令都是基于这个目录的
当然也可以将/usr/local/nginx/sbin添加到环境变量中
  • 普通启动服务:./nginx
  • 配置文件启动:./nginx -c /usr/local/nginx/conf/nginx.conf
  • 暴力停止服务:./nginx -s stop
  • 优雅停止服务:./nginx -s quit
  • 检查配置文件:./nginx -t
  • 重新加载配置:./nginx -s reload
  • 查看相关进程:ps -ef | grep nginx

打开防火墙

firewall-cmd --permanent --add-port=80/tcp

# 重载
firewall-cmd reload

了解配置文件

Nginx的配置文件的格式非常符合逻辑,每一部分都对于着相应的功能。
 title=
 title=

通过图片,我们知道,Nginx的配置文件分为三大部分,加上http块包含两个部分就是5个模块,那么这5个模块分别是干什么的以及它们有哪些常用的配置呢?

全局模块(main模块)

该模块的配置影响nginx全局的指令。
常用的配置有:

#配置worker进程运行用户(和用户组),nobody也是一个linux用户,一般用于启动程序,没有密码
user nobody;
#user www www;

#配置工作进程数目,根据硬件调整,通常等于CPU数量或者2倍于CPU数量(双核时)
worker_processes 1;

#配置全局错误日志及类型,[debug | info | notice | warn | error | crit],默认是error
error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#配置进程pid文件
pid logs/nginx.pid;

#一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 65535;

events模块

该配置影响nginx服务器或与用户的网络连接。
常用的配置有:

#参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; 
#epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;

#单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 65535;

http模块

作为web服务器,http模块是nginx最核心的一个模块,配置项也是比较多的,项目中会设置到很多的实际业务场景,需要根据硬件信息进行适当的配置。可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。
一些常用配置:

#常见的一些基础配置
include mime.types; #文件扩展名与文件类型映射表
default_type application/octet-stream; #默认文件类型
charset utf-8; #默认编码
server_names_hash_bucket_size 128; #服务器名字的hash表大小
client_header_buffer_size 32k; #上传文件大小限制
large_client_header_buffers 4 64k; #设定请求缓冲
client_max_body_size 8m; #设定请求缓冲
sendfile on; #开启高效文件传输模式,对于普通应用设为on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。
tcp_nopush on; #防止网络阻塞
tcp_nodelay on; #防止网络阻塞
keepalive_timeout 120; #长连接超时时间,单位是秒

#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;

#gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml; #压缩类型
gzip_vary on; #增加响应头'Vary: Accept-Encoding'
limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用

server模块

配置虚拟主机的相关参数,一个http中可以有多个server。server模块可以配置网络监听、配置https服务、基于名称的虚拟主机配置、基于IP的虚拟主机配置。
常用的配置:

#虚拟主机的常见配置
server {
    listen       80; #配置监听端口
    server_name  localhost; #配置服务名
    charset utf-8; #配置字符集
    access_log  logs/host.access.log  main; #配置本虚拟主机的访问日志
    
    location / {
        root html; #root是配置服务器的默认网站根目录位置,默认为nginx安装主目录下的html目录
        index index.html index.htm; #配置首页文件的名称
    }
    
    error_page 404             /404.html; #配置404错误页面
    error_page 500 502 503 504 /50x.html; #配置50x错误页面
}

#配置https服务,安全的网络传输协议,加密传输,端口443
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;
    }
}

location模块

配置请求的路由,以及各种页面的处理情况。

root html; #root是配置服务器的默认网站根目录位置,默认为nginx安装主目录下的html目录
index index.html index.htm; #配置首页文件的名称

proxy_pass http://127.0.0.1:88; #反向代理的地址
proxy_redirect off; #是否开启重定向
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
#以下是一些反向代理的配置,可选。
client_max_body_size 10m; #允许客户端请求的最大单文件字节数
client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数,
proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置
proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k;  #设定缓存文件夹大小

我们在上面的图片中可以看到,location的配置格式一般是这样的:

location / {
            proxy_pass http://127.0.0.1:5000;
            root   html;
            index  index.html index.htm;
        }

即:

location [ = | ~ | ~* | ^~ ] uri {
          
        }

这些符号是对uri的约束,它们表示:

  • = :用于不含正则表达式的 uri 前,要求请求字符串与 uri 严格匹配,如果匹配成功,就停止继续向下搜索并立即处理该请求。
  • ~ :用于表示 uri 包含正则表达式,并且区分大小写。
  • ~* :用于表示 uri 包含正则表达式,并且不区分大小写。
  • ^~ :用于不含正则表达式的 uri 前,要求 Nginx 服务器找到标识 uri 和请求字符串匹配度最高的 location 后,立即使用此 location 处理请求,而不再使用 location 块中的正则 uri 和请求字符串做匹配。

需要注意的是:如果 uri 包含正则表达式,则必须要有 ~ 或者~* 标识

反向代理

Nginx 不仅可以做反向代理,还能用作正向代理来进行上网等功能。反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
 title=
更多见:【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器

案例一

实现效果

 title=
效果

即我们访问一个url,nginx自动将我们的请求转发到flask等服务器上。
 title=

实现步骤

  1. 修改hosts文件
    windos的host文件位于:C:\Windows\System32\drivers\etc\hosts,linux的host文件位于:/etc/hosts
  2. 修改nginx配置文件
    需要用到配置:http > server > location > proxy_pass

    vim /usr/local/nginx/conf/nginx.conf

    修改的是server模块中的location模块,指定proxy_pass参数,注意书写格式,结尾不要漏掉;

    server {
            listen       80;
            server_name  192.168.43.12;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                proxy_pass http://192.168.43.13:5000;
            }

    现在nginx已经配置完成了。

  3. 启动或重启nginx

    # nginx未运行,直接启动
    /usr/local/nginx/sbin/nginx
    
    # nginx正在运行,则重载配置
    /usr/local/nginx/sbin/nginx -s reload
  4. 启动flask项目

    python app.py

    由于是用于演示,flask写得特别简单:

    # app.py
    
    from flask import Flask
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        return "<h1>index</h1>"
    
    
    if __name__ == '__main__':
        app.run()
  5. 用浏览器访问:www.abc.com,检验是否代理成功。
假如没有代理成功,请检查防火墙,常用的防火墙命令如下:
查询 firewall-cmd --query-port=端口/协议
打开端口 firewall-cmd --permanent --add-port=端口/协议
关闭端口 firewall-cmd --permanent --remove-port=端口/协议
注意打开或关闭端口后,需要重新载入才能生效:firewall-cmd reload

案例二

上面只设置了一台代理服务器,假如我要设置多台呢?如何根据url将请求分配到不同服务器中?

实现效果

 title=
效果图

 title=
抽象一点的示意图

实现步骤

  1. 修改host
  2. 修改配置文件
    在server模块中加入两个location模块,~代表什么意思见:上文的 "准备工作" 中的 "了解配置文件"
location ~ /news/ {
        proxy_pass http://192.168.43.13:5000;
    }
   location ~ /music/ {
        proxy_pass http://192.168.43.14:5001;
    }
  1. 然后启动两个flask
  2. 再用浏览器访问

负载均衡

早期的系统的架构模式相对单一:客户端发送多个请求到服务器,服务器处理请求,有一些可能要与数据库进行交互,服务器处理完毕后,再将结果返回给客户端。
但是随着信息数量的不断增长,访问量和数据量的飞速增长,以及系统业务的复杂度增加,造成服务器处理客户端的请求变得日益缓慢,并发量特别大的时候,还容易造成服务器直接崩溃。这时候集群 的概念产生了,单个服务器解决不了,我们增加服务器的数量 ,然后将请求分发 到各个服务器上,将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的负载均衡。

 title=
示意图

实现效果

 title=
效果图

 title=
示意图

实现步骤

  1. 修改Nginx配置文件
    http > server > location > proxy_passhttp > upstream > server

    upstream mysite {    # 集群名字
            server 192.168.43.14:5000;
            server 192.168.43.13:5001;
        }
    server {
        location / {
            proxy_pass http://mysite;
        }
    }
  2. 启动flask项目
  3. 将flask的端口添加到防火墙
  4. 使用浏览器打开

负载均衡的策略

上面我们指定指定负载均衡,使用的是默认的策略:轮询,除此之外,nginx还有其它策略供我们选择:

  • 轮询(默认)
    每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器宕掉,能自动剔除。

    upstream server_pool{
        server 192.168.43.13:5000;
        server 192.168.43.14:5001;
    }
  • weight
    weight 代表权重,默认为1,权重越高被分配的客户端越多,weight 和访问比率成正比,用于后端服务器性能不均的情况。

    upstream server_pool{
        server 192.168.43.13:5000 weight=10;
        server 192.168.43.14:5001 weight=20;
    }
  • ip_hash
    每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。

    upstream server_pool{
        ip_hash;
        server 192.168.43.13:5000;
        server 192.168.43.14:5001;
    }
  • fair
    按后端服务器的响应时间来分配请求,响应时间短的优先分配。
    注意,此为第三方模块,需要安装 。可参考此文教程:nginx负载均衡fair模块安装和配置

    upstream server_pool{
        server 192.168.43.13:5000;
        server 192.168.43.14:5001;
        fair;
    }

动静分离

Nginx 动静分离简单来说就是把动态跟静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求跟静态请求分开,可以理解成使用 Nginx 处理静态页面,而其它的web应用处理动态页面。。动静分离从目前实现角度来讲大致分为两种,一种是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案;另外一种方法就是动态跟静态文件混合在一起发布,通过 Nginx 来分开。

实现效果

 title=
动静分离

由图可以看出,加载图片并没有走flask服务器,而是nginx处理的。

 title=
示意图

实现步骤

由于nginx处理静态资源的能力也强,所以这里使用nginx处理静态资源。

  1. 在nginx目录下创建一个static目录

    ─conf
    ├─contrib
    │  ├─unicode2nginx
    │  └─vim
    │      ├─ftdetect
    │      ├─ftplugin
    │      ├─indent
    │      └─syntax
    ├─docs
    ├─html
    │  └─static
    ├─logs
    ├─static  
    └─temp
        ├─client_body_temp
        ├─fastcgi_temp
        ├─proxy_temp
        ├─scgi_temp
        └─uwsgi_temp
  2. 修改配置文件

    location ~ .*\.(gif|jpg|ico|png|css|svg|js)$ {
       root static;
    }
    
    location / {
        proxy_pass http://192.168.43.14:5000;
    }

    该配置表示,gif、jpg、ico、png、css、svg、js结尾的,都去static目录找。具体要什么格式的文件,可以自己根据上面的配置,加上去。

  3. flask这样配置

    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    
    @app.route("/")
    def index():
        return render_template("index.html")
    
    
    if __name__ == '__main__':
        app.run(port=5000)

    这是index.html文件:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>index page</title>
    </head>
    <body>
    <h1>index</h1>
    <img src="/img/reload.gif" alt="reload" title="reload.gif">
    
    </body>
    </html>
  4. 将静态资源放到static目录下,根据请求确定是否要创建子目录。
    如我这里请求的是/img/reload.gif,所以要把reload.gif文件放在static/img/目录下
  5. 使用浏览器访问

    html文件,可使用以下格式配置,然后html文件放在nginx/html下
    location ~ .*\.(html|htm)$ {
            root html;
            expires 1h;
    }
    但我目前还不知道如何在html也分离的情况下,继续使用flask返回页面。知道的朋友可以在评论区留言,谢谢。

这里有几篇django前后端分离的文章,仅做记录

高可用集群

高可用集群是指以减少服务中断时间为目的的服务器集群技术。它通过保护用户的业务程序对外不间断提供的服务,把因软件/硬件/人为造成的故障对业务的影响降低到最小程度。一般来说就是,假如Nginx挂了,服务怎样才能不中断?(代理服务器挂了的话,Nginx不会把请求发送过去,见负载均衡哪里)

实现思路

 title=
示意图

实现步骤

由上图我们可知,要实现这个功能必须要有三个部分:

  1. Nginx MASTER
  2. Nginx BACKUP
  3. 虚拟IP实质上就是Keepalived
    关于见此文:Linux高可用之Keepalived
    下面我们开始正式配置。

注意:由于我们要求两台机器同时做nginx代理,nginx配置的IP地址应当是一样的,而Keepalived的router_id配置指的是当前机器的ip,所以应该不一样。

Nginx MASTER配置

  1. 安装keepalived

    yum install -y keepalived
  2. 配置keepalived

    vim /etc/keepalived/keepalived.conf
    主要注意这几个项
    router_id 当前虚拟机的IP地址
    state 这个机器是MASTER还是BACKUP
    interface 当前机器的网卡名称,用于绑定虚拟IP
    virtual_router_id 虚拟路由的编号

    注意:ip地址和网卡名称都应该根据自己的机器来配置

    ! Configuration File for keepalived
    
    global_defs {
       notification_email {
         acassen@firewall.loc
         failover@firewall.loc
         sysadmin@firewall.loc
       }
       notification_email_from Alexandre.Cassen@firewall.loc
       #邮件服务器通知地址(暂不配置,默认即可)
       smtp_server 192.168.43.1
       #邮件服务器超时时间(暂不配置,默认即可)
       smtp_connect_timeout 30
       #当前虚拟机的IP地址
       router_id 192.168.43.12
    }
    
    vrrp_script Monitor_Nginx {
     script "/etc/keepalived/nginx_check.sh"    #检测脚本执行的路径
     interval 2                                 #检测脚本执行的间隔
     weight 2                                   #检测脚本执行的权重
    }
    
    vrrp_instance VI_1 {
        state MASTER         #标识这个机器是MASTER还是BACKUP
        interface eth3       #当前机器的网卡名称  
        virtual_router_id 51 #虚拟路由的编号,主备必须一致
        priority 100         #主、备机取不同的优先级,主机值较大,备份机值较小
        advert_int 1         #(VRRP Multicast广播周期秒数)
        authentication {
            auth_type PASS   #(VRRP认证方式)
            auth_pass 1111   #(密码)
        }
        track_script {
            Monitor_Nginx #(调用nginx进程检测脚本)
        }
        virtual_ipaddress {
            192.168.43.50  #虚拟IP地址
        }
    }
  3. 新增keepalived的检测脚本

    vim /etc/keepalived/nginx_check.sh

    如果 nginx 停止运行,尝试启动,如果无法启动则杀死本机的 keepalived 进程, keepalied将虚拟 ip 绑定到 BACKUP 机器上。

    # vi /etc/keepalived/nginx_check.sh
    #!/bin/bash
    A=`ps -C nginx –no-header |wc -l`
    if [ $A -eq 0 ];then
    /usr/local/nginx/sbin/nginx
    sleep 2
    if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
    killall keepalived
    fi
    fi

    给脚本赋执行权限:

    chmod +x /etc/keepalived/nginx_check.sh
  4. 启动keepalived服务

    systemctl start keepalived

Nginx BACKUP配置

这是另一个机器。

  1. 下载、配置Nginx
    将MASTER的复制过来即可
    注意防火墙:firewall-cmd --permanent --add-port=80/tcpfirewall-cmd reload
  2. 下载安装keepalived
    见上文操作
  3. 配置keepalived

    需要改的地方应该是:
    router_id和state以及interface
    ! Configuration File for keepalived
    
    global_defs {
       notification_email {
         acassen@firewall.loc
         failover@firewall.loc
         sysadmin@firewall.loc
       }
       notification_email_from Alexandre.Cassen@firewall.loc
       #邮件服务器通知地址(暂不配置,默认即可)
       smtp_server 192.168.43.1
       #邮件服务器超时时间(暂不配置,默认即可)
       smtp_connect_timeout 30
       #当前虚拟机的IP地址
       router_id 192.168.43.10
    }
    
    vrrp_script Monitor_Nginx {
     script "/etc/keepalived/nginx_check.sh"    #检测脚本执行的路径
     interval 2                                 #检测脚本执行的间隔
     weight 2                                   #检测脚本执行的权重
    }
    
    vrrp_instance VI_1 {
        state BACKUP         #标识这个机器是MASTER还是BACKUP
        interface eth3       #当前机器的网卡名称  
        virtual_router_id 51 #虚拟路由的编号,主备必须一致
        priority 10          #主、备机取不同的优先级,主机值较大,备份机值较小
        advert_int 1         #(VRRP Multicast广播周期秒数)
        authentication {
            auth_type PASS   #(VRRP认证方式)
            auth_pass 1111   #(密码)
        }
        track_script {
            Monitor_Nginx    #(调用nginx进程检测脚本)
        }
        virtual_ipaddress {
            192.168.43.50   #虚拟IP地址
        }
    }
  4. 新增keepalived的检测脚本

    vim /etc/keepalived/nginx_check.sh

    如果 nginx 停止运行,尝试启动,如果无法启动则杀死本机的 keepalived 进程, keepalied将虚拟 ip 绑定到 BACKUP 机器上。

    # vi /etc/keepalived/nginx_check.sh
    #!/bin/bash
    A=`ps -C nginx –no-header |wc -l`
    if [ $A -eq 0 ];then
    /usr/local/nginx/sbin/nginx
    sleep 2
    if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
    killall keepalived
    fi
    fi

    给脚本赋执行权限:

    chmod +x /etc/keepalived/nginx_check.sh
  5. 启动keepalived服务

    systemctl start keepalived

原理分析

多进程单线程

Nginx 自己实现了对epoll的封装,是多进程单线程的典型代表。使用多进程模式,不仅能提高并发率,而且进程之间是相互独立的,一 个worker进程挂了不会影响到其他worker进程。当Nginx启动后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。

 title=
示意图

见此文Nginx工作原理(Master+Worker)

worker的工作模式

worker对于连接是采用争抢的模式,谁先抢到就先交给谁处理。如果想要重新更新配置,由于已经抢到任务的worker不会参与争抢,那些空闲的worker就会去争抢连接,拿到连接后会自动更新配置信息。而那些有任务的worker在完成任务后,会自动更新配置。这样就实现了无缝热部署。由于每个worker是独立的进程,如果有其中的一个worker出现问题,并不会影响其它worker继续进行争抢,在实现请求的过程,不会造成服务中断。worker数应该根据服务器的cpu的核心数量而定,建议设置为cup数(单核)或cup数*2(双核),详情见评论区的链接。

 title=
示意图

worker连接数和最大的并发数

如果只访问nginx的静态资源,一个发送请求会占用了 woker 的 2 个连接数,最大并发数量应该是:worker_connections * worker_processes / 2
而如果是作为反向代理服务器,一个发送请求会占用了 woker 的 4 个连接数,最大并发数量应该是:worker_connections * worker_processes / 4

来源:# Nginx入门,看此文即可

最后修改:2022 年 01 月 18 日
如果觉得我的文章对你有用,请随意赞赏