Nginx 简介

[TOC]

Nginx 脑图

简介

nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server

Nginx 是一款开源的轻量级的 Web 服务器,可以接受响应 HTTP 请求,可以作为 反向代理服务器,邮件代理服务器和通用 TCP/UDP 代理服务器。

Nginx 采用 epoll(Linux 2.6 内核)、kqueue(FreeBSD)、evenport(Solaris 10)作为网络 I/O 模型,因此具备极高的性能。在高并发连接请求的场景下,Nginx 能够支持高达 50000 个并发连接数的响应,而同时对 CPU,内存等系统资源的消耗却非常低,运行非常稳定,几乎可以做到 7x24 小时不间断运行。

Nginx 通常用在 反向代理负载均衡HTTP 缓存。其特点是高并发,高性能和低内存。

特性

  • 高并发,高性能,高可靠和低内存占用。
  • 轻量级:功能模块少(Nginx 仅保留 HTTP 需要的模块,其他模块可以插件模式进行添加),代码模块化。
  • 采用 io多路复用机制,事件驱动,异步非阻塞处理请求。
  • 单主多从(one Master,several Worker processes)模式,Workder 进程以普通用户身份运行。
  • 支持热部署。
    ...

应用场景

  • 静态资源服务器
  • 正向代理服务器
  • 反向代理服务器(负载均衡)
  • 邮件代理服务器
  • 安全防御
    ...

几个名词释义

  • 正向代理(proxy):通常意义上的代理,指的就是正向代理。
    其一般的流程为:个人客户端通过将请求发送给到一个代理服务器(eg: Nginx),由代理服务器接收请求并真正去目的网站请求响应,最后将响应回传给到个人客户端。
    可以看到,正向代理的整个过程中,网站是看不到真正请求响应的客户端的,因此,正向代理可以隐藏真实的客户端。如下图所示:

    正向代理
  • 反向代理(reverse proxy):与正向代理代理客户端请求不同,反向代理代理的是服务器。
    其一般流程为:客户端请求资源,反向代理服务器(eg:Nginx)接受到请求后,从应用服务器集群中选取一个响应该请求。
    可以看到,在反向代理的整个过程中,客户端只需发送请求,而无需关心是哪一台服务器进行响应。因此,反向代理隐藏了具体进行请求响应的服务器。如下图所示:

    反向代理

    Nginx 作为反向代理服务器的典型场景就是使用 负载均衡 来应对有高并发请求的网站。

  • Web 服务器(Web Server):也称为 WWW(World Wide Web)服务器,其主要功能就是让用户可以通过浏览器进行访问,其关注的焦点在于 HTTP 协议层面的传输和访问控制。严格意义上来讲,Web 服务器只负责处理 HTTP 协议,只能发送静态页面的内容,而对于一些动态内容请求,就需要通过 CGI、FastCGI、ISAPI 等接口将请求透传(transparently forwards)给到给到特定应用服务器处理。常见的 Nginx,Apache,IIS 等都属于 Web 服务器。

  • 应用服务器(Application Server):是一个应用执行的容器,所以其核心就是能够运行一些应用程序,本质跟 HTTP 请求并没有关系,只是孤立地运行一些自己的业务逻辑(所谓的动态内容)。但是当前主流的 Web 应用体系使用的是 B/S(Browser-Server)架构,因此,应用服务器通常也会集成 HTTP Server 功能,能够接受和响应 HTTP 请求,但是不如专业的 Web 服务器功能那么强大。常见的 Tomcat,Weblogic,Jboss 等都属于应用服务器。

    :一般来说,开发阶段直接使用应用服务器进行调试即可(因为其提供了 HTTP Server 功能),而在项目部署阶段,由于 Web 服务器性能更加优秀,因此通常将 HTTP 请求的静态资源直接交由其处理,而对于无法处理的请求,则通过反向代理等方式交由特定应用服务器进行处理。

  • 动静分离:就是将静态资源的请求和动态资源的请求进行区分,对于静态资源(比如:*.html/*.png/*.jpg...)的请求,Web 服务器直接进行响应,而对于动态资源的请求,Web 服务器透传给到应用服务器进行处理,并转发响应给到客户端。

  • 负载均衡(Load Balance):当网站的并发量很大时,可以通过水平扩展服务器(集群)来分配负载,HTTP 请求按照一定的策略分发给到集群中的各个服务器,以达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的。

安装

这里我们采用源码安装,具体步骤如下(:本人的运行环境为:Windows Subsystem for Linux 2 - Ubuntu 18.04):

  1. 安装编译工具及一些库文件:
$ sudo apt install make gcc gcc-c++ libtool 

# 或者:Ubuntu 可直接使用以下命令安装平台编译环境
$ sudo apt install build-essential
$ sudo apt install libtool
  1. 安装 PCRE 库,让 Nginx 支持 Rewrite 功能:
$ cd /usr/local/src
$ wget -c "https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz"
$ tar -zxvf pcre-8.44.tar.gz
$ cd pcre-8.44
# 配置
$ ./configure
# 编译
$ make
# 安装
$ make install

:安装的是pcre而不是pcre2
:Rewrite 功能即能够根据域名、URL 的不同,将 HTTP 请求转发到不同的后端服务器群组中。

  1. 安装 zlib 库,让 Nginx 具备 gzip 压缩功能:
$ cd /usr/local/src
$ wget -c http://zlib.net/zlib-1.2.11.tar.gz
$ tar -zxvf zlib-1.2.11.tar.gz
$ cd zlib-1.2.11
$ ./configure
$ make
$ make install
  1. 安装 ssl,让 Nginx 支持 https:
$ cd /usr/local/src
$ wget -c "https://www.openssl.org/source/openssl-1.1.1g.tar.gz"
$ tar -zxvf openssl-1.1.1g.tar.gz
$ cd openssl-1.1.1g
$ ./config
$ make
$ make install

:安装完openssl后,如果使用wget等工具遇到错误:Unable to locally verify the issuer's authority,此时设置export SSL_CERT_DIR=/etc/ssl/certs即可。

  1. 下载 Nginx 源码,官网下载地址为:download
$ cd /usr/local/src
$ wget -c https://nginx.org/download/nginx-1.18.0.tar.gz
$ tar -zxvf nginx-1.18.0.tar.gz
$ cd nginx-1.18.0
$ ./configure --sbin-path=/usr/local/nginx/nginx \
     --conf-path=/usr/local/nginx/nginx.conf     \
     --pid-path=/usr/local/nginx/nginx.pid       \
     --with-http_gzip_static_module              \
     --with-http_stub_status_module              \
     --with-file-aio                             \
     --with-http_realip_module                   \
     --with-http_ssl_module                      \
     --with-pcre=/usr/local/src/pcre-8.44        \
     --with-zlib=/usr/local/src/zlib-1.2.11      \
 --with-openssl=/usr/local/src/openssl-1.1.1g
$ make -j4
$ make install

其中:
--with-pcre=/usr/local/src/pcre-8.44:表示 PCRE 库源码路径。
--with-zlib=/usr/local/src/zlib-1.2.11:表示 zlib 库源码路径。
--with-openssl=/usr/local/src/openssl-1.1.1g:表示 openssl 库源码路径。

更多配置选项,请查看:configure

  1. 上述过程完成后,Nginx 就已经成功安装到/usr/local/nginx中,下面将其添加到系统环境变量中:
$ cd /usr/local/nginx
$ ln -rs nginx /usr/local/bin/
  1. 此时,就可以使用命令sudo nginx来启动 Nginx,然后浏览器输入:http://localhost,就可以看到 Nginx 欢迎界面了:
Welcome to nginx

Nginx 进程架构/执行模型

Nginx 进程的架构组成是:一个主进程(master process)和多个工作进程(worker process)。主进程主要用于读取和检测配置文件,以及维护各个工作进程。Nginx 工作进程具备 CPU 亲和性,每个工作进程都固定在一个 CPU 上执行,减少了切换 CPU 的 cache miss,从而提供了性能保障,其主要责职就是对实际请求进行处理。

Nginx 的执行模型为:当主进程接受到一个客户端请求时,会将该请求通知到所有的工作进程,此时所有工作进程竞争该请求,最终由某个工作进程负责处理该请求。工作进程如果发现该请求请求的是静态资源的话,则自己进行响应。如果发现该请求请求的是动态资源,则会将请求转发给相应的应用服务器,等待应用服务器处理该请求后,最后由该工作进程将结果转发给到客户端。

Nginx 依据事件驱动模型和操作系统平台独特性实现请求快速分发给到各工作进程。工作进程的数量由配置文件定义决定,同时可以由给定配置进行重置或者根据 CPU 核数进行动态更改。一般将工作进程数设置为服务器 CPU 核心数是最适宜的,因为 Nginx 采用了 io多路复用 机制,每个工作进程都是一个独立的进程,并且每个进程只有一个主线程,对请求的处理都是通过异步非阻塞方式,对 CPU 亲和性很高,可以将 CPU 性能发挥到极致。

Nginx 常用命令

  • nginx -s reload:重新载入配置文件

  • nginx -s reopen:重启 Nginx

  • nginx -s stop:停止 Nginx

  • nginx -c file:指定配置文件(不使用默认配置文件)

  • nginx -t:检查配置文件 nginx.conf

更多命令行参数详情,请参考:nginx -hCommand-line parameters

Nginx 模块

Nginx 主要由两部分内容构成:内核 + 模块(module)

Nginx 是典型的微内核设计,其内核简洁优雅,且具备极高扩展性。内核提供了 Web 服务的基本功能,如启用网络协议,创建运行环境,接收和分配客户端请求,处理模块之间的交互...

同时 Nginx 又采用模块化的开发方式,Nginx 的各种功能和操作都交由对应模块进行实现。

官网上,主要介绍了 Nginx 4 大模块:

  • Core functionality:核心功能模块:ngx_core_module,主要介绍了 Nginx 核心模块配置的一些指令,eg:userpid...

  • ngx_http_module:HTTP 模块,主要是对 HTTP 进行操作的各个功能模块组合,其包含的子模块如下图所示:

ngx_http_module
  • ngx_mail_module:MAIL 模块,邮件代理模块,其包含的子模块如下图所示:
ngx_mail_module
  • ngx_stream_module:STREAM 模块,是基于 tcp 协议的一些模块,其包含的子模块如下图所示:
ngx_stream_module

:各模块的具体详情及其配置语法,请参考:Modules reference

Nginx 的模块默认编译进 Nginx 中(可通过nginx -V查看编译的具体模块),如果需要增加或删除模块,需要重新编译 Nginx。

Nginx 模块化的设计使得 Nginx 具备极佳的扩展性,越来越多的第三方模块开发也使得 Nginx 的功能越来越强大。

Nginx 基本配置

Nginx 及其各个模块的工作方式都由配置文件进行控制。默认的配置文件可能存放于以下路径:

  • /usr/local/nginx/conf/nginx.conf
  • /etc/nginx/nginx.conf
  • usr/local/etc/nginx/nginx.conf

实际上,我们可以通过前面介绍的配置文件检测命令nginx -t,该命令会打印出默认的配置文件路径,如下图所示:

nginx -t

可以看到,本人机器上 Nginx 默认的配置文件路径为:/usr/local/nginx/nginx.conf(其实是源码安装的时候手动进行指定的)

知道 Nginx 的配置文件路径后,我们就可以对其进行配置。但在此之前,需先了解一下 Nginx 配置文件组织架构:

  • 配置文件架构:Nginx 由多个模块(module)组合而成,每个模块的配置都由相应的指令(directive)进行设置。
    在 Nginx 配置文件中,有两种 指令 类型:

    • 简单指令(simple directives):简单指令由名称和参数组成,以空格进行区分,以分号;作为结束。其格式如下所示:

      name parameter;
      
      name parameter1 parameter2;
      
      name parameter1 parameter2 ...;
      
    • 块指令(block directives):块指令也是由名称和参数组成,只是相对与简单指令以分号;进行结束,块指令以大括号作为结束,并且大括号内可以添加一些额外指令。其格式如下所示:

      name {
          name1 parameter1;
          name2 {
              ...
          }
          ...
      }
      
      name parameter {
          name1 ...;
          name2 {
              ...
          }
          ...
      }
      
      name parameter1 parameter2 ... {
          name1 ...;
          name2 {
              ...
          }
          ...
      }
      

    :如果块指令内部有其他一些指令,则该块指令称之为 context(比如:eventshttpserverlocation...)
    配置文件内的独立于任何 context 之外的指令被称为 main context,比如:eventshttp 指令就属于 main context,server 就属于 http contextlocation 也属于 http context

了解完配置文件的指令格式后,我们就可以进行具体配置了:以下是一个基本的配置结构:

main                         # 全局配置(main context)

events {                     # 事件模块配置
    ...
}

http {                       # http模块设置
    ....

    server {                 # 虚拟服务器
        ....
        location {           # 路由配置
            ....
        }

        location path {
            ....
        }

        location otherpath {
            ....
        }
    }

    server {
        ....

        location {
            ....
        }
    }

    upstream name {          # 负载均衡配置
        ....
    }
}

下面配置几个实用的例子,促进理解:

  • 静态资源服务器:将 Nginx 配置为一个能提供静态资源下载的服务器,具体配置如下:

    1. 假设静态资源存放位置为:/data/www,现在我们创建该目录,并且放入一个index.html文件:
    $ sudo mkdir -p /data/www
    $ echo '<h>Hello Nginx</h1>' | sudo tee /data/www/index.html
    
    1. 配置 Nginx 静态资源目录为:/data/www
    # nginx.conf
    
    http {                      # http 模块配置
        server {                # 虚拟主机配置
    
            location / {        # 路由映射:/
                root /data/www; # 静态资源路径
            }
        }
    }
    
    1. 上述简单几行配置,其实就已经完成了静态服务器功能了,但是还未生效,我们还需通过nginx -s reload命令来让 Nginx 重新加载配置文件。

    2. 经过以上步骤,此时浏览器访问http://localhost/index.html,就可以看到index.html显示的内容了。

    :当访问出错的时候,可以查看下日志:access.logerror.log,一个记录了访问日志,一个记录了访问出错日志。这两个日志的位置位于:/usr/local/nginx/logs/var/log/nginx

    :上述示例是极简配置,以下配置与上述极简配置功能一致,但配置内容相对全面,更好理解。具体配置步骤如下:

    1. 首先创建一个普通用户用于执行 Nginx 进程,假设该新用户为:nginx,所属组为:www,创建方式如下:
    # 创建用户 nginx,-d:指定用户主目录 -m:当用户主目录不存在时,创建主目录
    $ sudo useradd -d /home/nginx -m nginx  # 也可直接使用 sudo useradd nginx
    # 创建组 www
    $ sudo groupadd www
    # 将用户 nginx 添加进组 www,-a:append 到组(不删除本身所属组)-G {groupname} username
    $ sudo usermod -a -G www nginx
    # 查看用户 nginx 信息
    $ id nginx
    
    1. 配置文件配置如下内容:
    user nginx www;                             # 以 www 组的用户 nginx 运行
    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 {                                      # http 模块配置
        include       mime.types;               # include:表示导入配置文件 mime.types
        default_type  application/octet-stream; # response 的默认 MIME 类型
    
        sendfile        on;                     # 启动高效传输文件模式
    
        keepalive_timeout  65;                  # 保持连接时间
    
        # gzip  on;
    
        server {                                # 虚拟主机配置
    
            listen 80;                          # 监听的 http 端口号(默认为 80)
            server_name  localhost;             # 虚拟主机对应的域名/ip
    
            location / {                        # 路由映射:/
                root /data/www;                 # 静态资源路径
                index  index.html index.htm;    # 访问根目录对应的默认请求文件
            }
    
            location /images/ {                 # 路由映射:/images/
                root /data/;                    # 静态资源路径
            }
    
            #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;
            }
        }
    }
    

    这里面我们主要来讲一下location的配置选项,因为location的配置可能咋看起来没问题,但实际运行却一直无法访问,因此这里需要着重介绍下:

    location的作用是配置请求 URI。Nginx 会从请求的 URI 中的path路径解析到其对应的location,然后从该location指定的配置中获取到请求资源的真实路径...其格式如下:

    Syntax: location [ = | ~ | ~* | ^~ ] uri { ... }
            location @name { ... }
    Default:    —
    Context:    server, location
    

    location 的路由映射是由:前缀 + 路由字符串 组合而成。从 location 的语法知道,其 前缀 字符有 4 种:

    • =:表示精确匹配(原意匹配)。
    • ~:表示正则匹配区分大小写。
    • ~*:表示正则匹配不区分大小写。
    • ^~:表示普通字符串匹配(原意匹配)
    • @:定义一个命名 location,用于内部重定向(@用于命名当前 location,不是前缀)。

    从上述描述可以看到,location 的前缀中,只有~~*会执行正则匹配,这里我们将其称之为 正则匹配
    =^~以及无任何前缀的都按原意进行匹配(不执行正则匹配),我们将之称其为 普通匹配

    • 匹配规则:对于普通匹配,其遵循以下两个规则:

      • URI 前缀匹配:只要请求 URI 前缀含有当前 location 指定的 URI,则匹配成功。

      • URI 最长匹配:请求 URI 优先匹配指定了最长 URI 的 location。比如:

        location = /images/ {...}             # location A
        location  /images/ {...}              # location B
        location ^~ /images/volleyball/ {...} # location C
        

        如果此时请求为:http://localhost/images/,则匹配前缀为=的 location,即 location A。
        如果此时请求为:http://localhost/images/voleeyball/..,由于 location B 和 location C 同时满足匹配请求 URI,但是 location C 指定的 URI 比 location B 更长,因此最终的匹配为 location C(优先选择最长匹配)。

        =执行的是精确匹配,因此只能匹配对根目录的请求。而^~执行的单纯的原意匹配,会遵顼最长匹配规则,因此后面可携带子路径。

    • 匹配顺序:Nginx 会先进行普通匹配,然后再执行正则匹配。具体过程如下:

      1. 先执行精确匹配(=),如果找到,匹配完成,停止搜索。
      2. 对带前缀修饰符^~和未带前缀修饰符的 location 进行匹配,如果找到最长匹配,且最长匹配 location 带有前缀^~,则匹配完成,停止搜索。否则,记录该匹配。
      3. 对前缀~~*的 location 进行匹配(以配置文件声明的顺序依次进行匹配),找到第一个匹配时,则匹配完成,停止搜索。
      4. 如果第 3 步匹配失败,则结果为第 2 步的匹配结果。

      :上述过程中,第 1,2 步为普通匹配,第 3 步为正则匹配。

      举个例子:

      server {
      
          listen 80;
          server_name  localhost;
      
          default_type text/html;                 # 响应默认返回类型
      
          location / {                            # location A
              return 200 A;
          }
      
          location = /images/ {                   # location B
              return 200 B;
          }
      
          location /images/volleyball/test {      # location C
              return 200 C;
          }
          location /images/volleyball/test.html { # location D
              return 200 D;
          }
      
          location /images/volleyball/ {          # location E
              return 200 E;
          }
          location ^~ /images/volleyball/te {     # location F
              return 200 F;
          }
      
          location ~ /images/volleyball/te {      # location G
              return 200 G;
          }
      }
      

      以下分几种情况进行请求:

      • curl localhost/images/:精确匹配,结果为B

      • curl localhost/images/xxx:过程如下:

        1. 精确匹配:/images/,不满足
        2. 带前缀^~和普通未带前缀的正则匹配:images/volleball/te//images/volleyball/test/images/volleyball/test.html/images/volleyball/,都没有最长匹配(即都不包含请求全路径/images/xxx
        3. ~/~*进行匹配:/images/volleyball/te,不匹配
        4. 对未带前缀的正则进行匹配,只有 location A 满足,所以结果为A
      • curl localhost/images/volleyball/xxx:过程如下:

        1. 同上,不匹配
        2. 同上,得到最长匹配为 location E,没有带前缀^~,则记录 location E 即可
        3. 进行前缀~/~*匹配,不满足
        4. 由于第 3 步匹配不到,故结果为第 2 步的最长匹配:location E,所以结果为E
      • curl localhost/images/volleyball/tes:过程如下:

        1. 同上,不匹配
        2. 同上,得到的最长匹配为:location F,因为该最长匹配的 location 带有前缀^~,因此结果为F,搜索结束
      • curl localhost/images/volleyball/test:过程如下:

        1. 同上,不匹配
        2. 同上,location C 为最长匹配,未带前缀^~,保留记录
        3. 进行前缀~/~*匹配,location G 匹配,结果为G
      • curl localhost/images/volleyball/testxxx:过程如下:

        1. 同上,不匹配
        2. 同上,location C 为最长匹配,未带前缀^~,保留记录
        3. 进行前缀~/~*匹配,location G 匹配,结果为G
      • curl localhost/images/volleyball/test.html

        1. 同上,不匹配
        2. 同上,location D 为最长匹配,未带前缀^~,保留记录
        3. 进行前缀~/~*匹配,location G 匹配,结果为G

    然后 location 内部用于指定路由对应的实际目录有如下两个指令:

    • root:设置路由映射的根目录,比如:
    location /i/ {
        root /data/www;
    }
    

    当我们请求/i/1.jpg的时候,对应的资源的实际路径就为:/data/www/i/1.jpg,这里需要注意的一个点就是请求携带的资源路径必须存在root指定的目录下,比如当前这个例子的请求 URI 的path/i/1.jpg,因此/data/www内就必须对应有一个子目录i,其内有一张图片1.jpg,也即:/data/www/i/1.jpg。也即:请求资源实际路径 = root+location

    • alias:使用root指定的资源目录,请求路径必须存在于root指定的目录中,资源是相对于root指定的路径的。如果请求 URI 需要进行变动,则应当使用alias指令来将请求path映射到一个绝对路径,比如:
    location /i/ {
        alias /data/www/images/; # alias 必须以 / 结尾,表示一个目录
    }
    

    当我们请求/i/1.jpg时,实际对应的资源路径为:/data/www/images/1.jpg,可以看到,不再是相对于root目录,而是使用了绝对路径。简单理解就是:请求资源路径 = alias路径替换location

  • 正向代理服务器:Nginx 中一个最常见的场景之一就是将其作为代理服务器,可以将客户端的请求转发到目标服务器,再将结果转发给到客户端,让客户端不必直接访问目标网站也能访问网站内容。其具体配置如下所示:

    http {                                              # http 模块配置
        ...
        server {                                        # 代理服务器
            resolver 114.114.114.114;                   # 指定DNS服务器IP地址
            listen 80;
            server_name localhost;
    
            location / {
                proxy_pass $scheme://$host$request_uri; # 正向代理,转发到目标服务器
            }
        }
    }
    

    上述代码中我们配置了一个 HTTP 代理服务器http://localhost:80,它会将客户端请求转发给到目标服务器,$scheme://$host$request_uri就是目标服务器地址(网址),这里以$开头的字符串是 Nginx 内置的变量,可用于获取请求的一些信息,更多变量内容,请查看:Nginx 内置变量
    此时,我们可以通过curl指令指定代理代理服务器访问目标网址:

    $ curl -I --proxy http://localhost:80 http://www.baidu.com             # 请求成功后,可以看到响应头`Server: nginx/1.18.0`,表示我们的确是通过 Nginx 代理进行访问的
    

    :Nginx 默认不支持正向代理 HTTPS 请求,但是我们可以通过添加第三方模块 ngx_http_proxy_connect_module 来使能 HTTPS 正向代理。
    具体搭建步骤如下所示:

    1. 下载扩展模块:如下所示:
    $ cd /usr/local/src
    $ git clone https://github.com/chobits/ngx_http_proxy_connect_module.git
    
    1. 安装 pathch:如下所示:
    $ sudo apt install patch
    
    1. 选择补丁包:根据自己 Nginx 的版本选择合适的补丁包,对应版本请查询:[select-patch]
    $ patch -p1 < /usr/local/src/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1018.patch
    
    1. 安装扩展模块到 Nginx:如下所示:
    $ cd /usr/local/src/nginx-1.18.0
    $ sudo ./configure --sbin-path=/usr/local/nginx/nginx \
    --conf-path=/usr/local/nginx/nginx.conf     \
    --pid-path=/usr/local/nginx/nginx.pid       \
    --with-http_gzip_static_module              \
    --with-http_stub_status_module              \
    --with-file-aio                             \
    --with-http_realip_module                   \
    --with-http_ssl_module                      \
    --with-pcre=/usr/local/src/pcre-8.44        \
    --with-zlib=/usr/local/src/zlib-1.2.11      \
    --with-openssl=/usr/local/src/openssl-1.1.1g \
    --add-module=/usr/local/src/ngx_http_proxy_connect_module
    $ sudo make j4
    $ sudo make install
    

    以上,就完成了第三方扩展模块 ngx_http_proxy_connect_module 的安装。

    下面就可以配置 Nginx 支持 HTTPS 正向代理,如下所示:

    server  {
        resolver 114.114.114.114; # DNS 服务器可根据实际情况单独配置
        listen 80;
        server_name localhost;
        default_type text/html;
    
        underscores_in_headers on;
    
        proxy_connect;
        proxy_connect_allow all;
        proxy_connect_connect_timeout 10s;
        proxy_connect_read_timeout 10s;
        proxy_connect_send_timeout 10s;
    
        location / {
            proxy_pass $scheme://$host$request_uri; # 正向代理,转发到目标服务器
        }
    }
    

    重新加载 Nginx 后,访问 HTTPS 应该就可以成功了,如下所示:

    $ curl -X GET 'https://www.baidu.com' --proxy 'http://localhost:80' -i
    

    关于 Nginx 无法直接支持 HTTPS 正向代理的具体原因,可参考:使用 Nginx 搭建 HTTPS 正向代理服务

  • 反向代理服务器:反向代理就是将客户端请求转发到后台应用服务器集群中的一个进行处理,其具体配置如下:

    
    http {                                        # http 模块配置
        ...
        server {                                  # 目标服务器
            listen 8080;
            root /data/realweb/;                  # 这里直接将 root 设置到 server 块中,表示该 server 块中所有 location 默认使用该 root,
                                                  # location 内部配置了 root 会覆盖此处的 root
    
            default_type text/html;               # 响应默认返回类型
    
            location / {
                return 200 "chosen to handler requests in cluster";
            }
        }
    
        server {                                  # 反向代理服务器
            listen 80;
            server_name localhost;
    
            location / {
                proxy_pass http://localhost:8080; # 反向代理配置,转发到 8080 端口
                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_set_header  X-Forwarded-Proto $scheme;
            }
        }
    }
    

    其实反向代理的配置和正向代理的配置都是采用proxy_pass指令,两者的配置形式上几乎是一样的,只是在概念上存在区别:正向代理知道要访问的目标服务器地址,但是无法直接访问,因此通过代理进行访问,而 Nginx 需要通过内置变量提取出目标服务器地址,进行访问;而反向代理可以直接访问目标服务器,只是不知道具体进行请求处理的是哪一个应用服务器,但对 Nginx 而言,就需要具体对集群服务器进行配置了。

  • 动静分离:动静分离 其实与 反向代理 在配置上异曲同工,只不过 动静分离 把请求识别为对静态资源和动态资源的请求,对于静态资源的请求,Nginx 会直接响应这些请求,对于动态资源,Nginx 会扮演代理角色,将请求转发给到对应的应用服务器。具体配置如下:

    http {                                        # http 模块配置
        ...
        server {                                  # 目标服务器
            listen 8080;
            server_name  localhost;
            root /data/realweb/;                  # 这里直接将 root 设置到 server 块中,表示该 server 块中所有 location 默认使用该 root,
                                                  # location 内部配置了 root 会覆盖此处的 root
    
            default_type text/html;               # 响应默认返回类型
    
            location / {
                return 200 "dynamic content";
            }
        }
    
        server {                                  # 代理服务器
            listen 80;
            server_name  localhost;
    
            location / {                          # 动态请求
                proxy_pass http://localhost:8080; # 代理,转发请求给到 http://localhost:8080
            }
    
            location ~ \.(gif|jpg|png)$ {         # 静态资源请求
                root /data/images/;               # 图片静态资源目录
            }
        }
    }
    

    这里localhost:80的服务器作为动静分离服务器,对于静态资源\.(gif|jpg|png)$的请求,直接进行响应。对于其他请求,代理给到目标服务器localhost:8080

  • 负载均衡:当我们网站的并发量比较大的时候,这时候可能就需要搭建服务器集群,然后采用 负载均衡 分摊服务器集群压力。具体配置如下:

    http { 
        ...
        upstream localserver { # 集群服务器
            # 默认采用轮询机制
            server 1xx.2x.1xx.2xx:9000; # 远程服务器一
            server 1xx.2x.1xx.2xx:9001; # 远程服务器二
        }
    
        server { # 网站
            listen 8080;
            server_name localhost;
    
            location / {
            proxy_pass http://localserver; # 请求转向集群 localserver
            }
        }
    }
    

    这里我们采用upstream定义服务器集群,内部我配置了两个远程服务器,Nginx 默认采用的集群策略是轮询机制,即第一次请求分发到第一台服务器,第二次请求分发到第二台服务器...以此类推。然后对我们的网站采用了proxy_pass将请求反向代理到了upstream定义的集群,现在,进行如下访问:

    $ curl http://localhost:8080 # 9000 端口的服务器会响应
    $ curl http://localhost:8080 # 9001 端口的服务器会响应
    

    :负载均衡策略有很多种,比如 轮询权重ip_hashfairurl_hash...这里就不进行展开了。

  • Nginx 支持 HTTPS 传输:Nginx 可以配置开启 HTTPS 服务,具体配置如下:

    server {
        #ssl参数
        listen              443 ssl;
        server_name         example.com;
        #证书文件
        ssl_certificate     example.com.crt;
        #私钥文件
        ssl_certificate_key example.com.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        #...
    }
    

    上述配置中,私钥文件*.key和证书文件*.crt可以通过openssl命令生产,比如:

    # C:Country ,单位所在国家,为两位数的国家缩写,如: CN 就是中国
    # ST 字段: State/Province ,单位所在州或省
    # L 字段: Locality ,单位所在城市 / 或县区
    # O 字段: Organization ,此网站的单位名称;
    # OU 字段: Organization Unit,下属部门名称;也常常用于显示其他证书相关信息,如证书类型,证书产品名称或身份验证类型或验证内容等;
    # CN 字段: Common Name ,网站的域名;
    
    openssl req -new -newkey rsa:2048 -sha256 -nodes -out example_com.csr -keyout example_com.key -subj "/C=CN/ST=ShenZhen/L=ShenZhen/O=Example Inc./OU=Web Security/CN=example.com"
    

    上述命令生产的证书文件*.crt还需要相关 CA 机构进行签署,成功后会得到一个新的证书文件,然后才可以在 Nginx 中配置 HTTPS 服务。
    网上有证书的免费申请方式,这里就不加赘述了。

附录

这是在网上看到的一份比较详细的 Nginx 配置文件释义:

#安全问题,建议用nobody,不要用root.
#user  nobody;

#worker数和服务器的cpu数相等是最为适宜
worker_processes  4;

#work绑定cpu(4 work绑定4cpu) 这个配置项目前在我本机上有问题,先注释掉
#worker_cpu_inffinity 0001 0010 0100 1000

#work绑定cpu (4 work绑定8cpu中的4个) 。
worker_cpu_inffinity 0000001 00000010 00000100 00001000

#全局错误日志定义类型,[debug | info | notice | warn | error | crit]
#error_log  /data/nginx/logs/error.log;
#error_log  /data/nginx/logs/error.log  notice;

#日志文件存放路径 access_log path [format [buffer=size | off]]
access_log /data/nginx/logs/lazyegg.com/web/access.log combinedio;

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

#指定进程可以打开的最大描述符:数目
#工作模式与连接数上限
##这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。
#这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。
worker_rlimit_nofile 65535;

#################################  events  ###############################
events {
    #参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型
    use epoll
    #单个进程最大连接数(最大连接数=连接数+进程数)
    worker_connections  1024;
    
    #keepalive 超时时间
    keepalive_timeout 60;
    
    #客户端请求头部的缓冲区大小。
    client_header_buffer_size 4k;
    
    #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。
    open_file_cache max=65535 inactive=60s;
    #这个是指多长时间检查一次缓存的有效信息。
    open_file_cache_valid 80s;
        #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。
    open_file_cache_min_uses 1;
    
    #语法:open_file_cache_errors on | off 默认值:open_file_cache_errors off 使用字段:http, server, location 这个指令指定是否在搜索一个文件是记录cache错误.
    open_file_cache_errors on;
}

##############################   http    ##################################

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http{
    #文件扩展名与文件类型映射表
    include mime.types;
    
    #默认文件类型
    default_type application/octet-stream;
    
    #默认编码
    charset utf-8;
    
    #服务器名字的hash表大小
    server_names_hash_bucket_size 128;
    
    #客户端请求头部的缓冲区大小。
    client_header_buffer_size 32k;
    
    #客户请求头缓冲大小。
    large_client_header_buffers 4 64k;
    
    #允许客户端请求的最大单个文件字节数
    client_max_body_size 8m;
    
    #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行视频下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
    sendfile on;
    
    #开启目录列表访问,适合下载服务器,默认关闭。
    autoindex on;
    
    #此选项允许或禁止使用socke的TCP_CORK的选项,此选项仅在使用sendfile的时候使用
    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;    #压缩类型,默认就已经包含textml,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
    gzip_vary on;

    #开启限制IP连接数的时候需要使用
    #limit_zone crawler $binary_remote_addr 10m;
    
        #负载均衡配置
    upstream lazyegg.net {
  
        #upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。
        server 192.168.80.121:80 weight=3;
        server 192.168.80.122:80 weight=2;
        server 192.168.80.123:80 weight=3;

        #nginx的upstream目前支持4种方式的分配
        #1、轮询(默认)
        #每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
        #2、weight
        #指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
        #例如:
        #upstream bakend {
        #    server 192.168.0.14 weight=10;
        #    server 192.168.0.15 weight=10;
        #}
        #2、ip_hash
        #每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
        #例如:
        #upstream bakend {
        #    ip_hash;
        #    server 192.168.0.14:88;
        #    server 192.168.0.15:80;
        #}
        #3、fair(第三方)
        #按后端服务器的响应时间来分配请求,响应时间短的优先分配。
        #upstream backend {
        #    server server1;
        #    server server2;
        #    fair;
        #}
        #4、url_hash(第三方)
        #按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
        #例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
        #upstream backend {
        #    server squid1:3128;
        #    server squid2:3128;
        #    hash $request_uri;
        #    hash_method crc32;
        #}

        #tips:
        #upstream bakend{#定义负载均衡设备的Ip及设备状态}{
        #    ip_hash;
        #    server 127.0.0.1:9090 down;
        #    server 127.0.0.1:8080 weight=2;
        #    server 127.0.0.1:6060;
        #    server 127.0.0.1:7070 backup;
        #}
        #在需要使用负载均衡的server中增加 proxy_pass http://bakend/;

        #每个设备的状态设置为:
        #1.down表示单前的server暂时不参与负载
        #2.weight为weight越大,负载的权重就越大。
        #3.max_fails:允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream模块定义的错误
        #4.fail_timeout:max_fails次失败后,暂停的时间。
        #5.backup:其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。

        #nginx支持同时设置多组的负载均衡,用来给不用的server来使用。
        #client_body_in_file_only设置为On 可以讲client post过来的数据记录到文件中用来做debug
        #client_body_temp_path设置记录文件的目录 可以设置最多3层目录
        #location对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡
    }
    
       #虚拟主机的配置
    server {
        #监听端口
        listen 80;

        #域名可以有多个,用空格隔开
        server_name lazyegg.net;
        #默认入口文件名称
        index index.html index.htm index.php;
        root /data/www/lazyegg;

        #对******进行负载均衡
        location ~ .*.(php|php5)?$
        {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi.conf;
        }
         
        #图片缓存时间设置
        location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$
        {
            expires 10d;
        }
         
        #JS和CSS缓存时间设置
        location ~ .*.(js|css)?$
        {
            expires 1h;
        }
         
        #日志格式设定
        #$remote_addr与$http_x_forwarded_for用以记录客户端的ip地址;
        #$remote_user:用来记录客户端用户名称;
        #$time_local:用来记录访问时间与时区;
        #$request:用来记录请求的url与http协议;
        #$status:用来记录请求状态;成功是200,
        #$body_bytes_sent :记录发送给客户端文件主体内容大小;
        #$http_referer:用来记录从那个页面链接访问过来的;
        #$http_user_agent:记录客户浏览器的相关信息;
        #通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。
        log_format access '$remote_addr - $remote_user [$time_local] "$request" '
        '$status $body_bytes_sent "$http_referer" '
        '"$http_user_agent" $http_x_forwarded_for';
         
        #定义本虚拟主机的访问日志
        access_log  /usr/local/nginx/logs/host.access.log  main;
        access_log  /usr/local/nginx/logs/host.access.404.log  log404;
         
        #对 "/connect-controller" 启用反向代理
        location /connect-controller {
            proxy_pass http://127.0.0.1:88; #请注意此处端口号不能与虚拟主机监听的端口号一样(也就是server监听的端口)
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
             
            #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             
            #以下是一些反向代理的配置,可选。
            proxy_set_header Host $host;

            #允许客户端请求的最大单文件字节数
            client_max_body_size 10m;

            #缓冲区代理缓冲用户端请求的最大字节数,
            #如果把它设置为比较大的数值,例如256k,那么,无论使用firefox还是IE浏览器,来提交任意小于256k的图片,都很正常。如果注释该指令,使用默认的client_body_buffer_size设置,也就是操作系统页面大小的两倍,8k或者16k,问题就出现了。
            #无论使用firefox4.0还是IE8.0,提交一个比较大,200k左右的图片,都返回500 Internal Server Error错误
            client_body_buffer_size 128k;

            #表示使nginx阻止HTTP应答代码为400或者更高的应答。
            proxy_intercept_errors on;

            #后端服务器连接的超时时间_发起握手等候响应超时时间
            #nginx跟后端服务器连接超时时间(代理连接超时)
            proxy_connect_timeout 90;

            #后端服务器数据回传时间(代理发送超时)
            #后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据
            proxy_send_timeout 90;

            #连接成功后,后端服务器响应时间(代理接收超时)
            #连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间)
            proxy_read_timeout 90;

            #设置代理服务器(nginx)保存用户头信息的缓冲区大小
            #设置从被代理服务器读取的第一部分应答的缓冲区大小,通常情况下这部分应答中包含一个小的应答头,默认情况下这个值的大小为指令proxy_buffers中指定的一个缓冲区的大小,不过可以将其设置为更小
            proxy_buffer_size 4k;

            #proxy_buffers缓冲区,网页平均在32k以下的设置
            #设置用于读取应答(来自被代理服务器)的缓冲区数目和大小,默认情况也为分页大小,根据操作系统的不同可能是4k或者8k
            proxy_buffers 4 32k;

            #高负荷下缓冲大小(proxy_buffers*2)
            proxy_busy_buffers_size 64k;

            #设置在写入proxy_temp_path时数据的大小,预防一个工作进程在传递文件时阻塞太长
            #设定缓存文件夹大小,大于这个值,将从upstream服务器传
            proxy_temp_file_write_size 64k;
        }
        
        #本地动静分离反向代理配置
        #所有jsp的页面均交由tomcat或resin处理
        location ~ .(jsp|jspx|do)?$ {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:8080;
        }
    }
}

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266