20171025 Nginx

  • I/O模型
  • Nginx介绍
  • Nginx的安装和目录结构
  • Nginx的配置
  • Nginx的编译安装

一、I/O模型

(一)I/O的基本概念

  • I/O过程的两个阶段

    • 第一步:将数据从硬件加载至内核内存空间(缓冲区),等待数据准备完成,时间较长
    • 第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
  • 同步与异步:消息通知机制

    • 同步(synchronous):调用者主动等待被调用者返回其运行状态信息
    • 异步(asynchronous):被调用者通过状态、通知、回调机制主动通知调用者其运行状态
  • 阻塞与非阻塞:调用者等待结果返回前的状态

    • 阻塞(blocking):I/O操作彻底完成后才返回用户空间,调用结果返回之前,调用者被挂起
    • 非阻塞(nonblocking):调用I/O操作后立即返回用户一个状态值,最终调用结果返回之前,调用者不会被挂起

(二)I/O模型

  • 分类:阻塞型、非阻塞型、复用型、信号驱动型、异步型
(1)同步阻塞I/O模型
  • 用户线程在内核进行I/O操作时被阻塞
  • 整个I/O请求的过程中,用户线程是被阻塞的,这导致用户在发起I/O请求时,不能做任何事情,对CPU的资源利用率不够
(2)同步非阻塞I/O模型
  • 用户线程需要不断地发起I/O请求,直到数据到达后,才真正读取到数据,继续执行。即“轮询”机制
  • 比较浪费CPU的方式,一般很少直接使用这种模型
(3)I/O多路复用模型
  • 进程阻塞在select或者poll这两个系统调用上,而不是阻塞在真正的I/O操作上
  • 由于使用了会阻塞线程的select系统调用,因此I/O多路复用只能称为异步阻塞I/O模型,而非真正异步I/O
  • I/O多路复用是最常使用的I/O模型
(4)信号驱动I/O模型
  • 等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知
  • 该模型并不常用
(5)异步I/O模型
  • 内核通知用户进程I/O操作完成
  • 信号驱动I/O当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到用户空间缓冲区这个阶段,而异步IO直接是在第二个阶段完成后内核直接通知可以进行后续操作了
  • 该模型并不常用

(6)五种I/O模型对比

(三)I/O模型的具体实现

(1)Linux系统实现
  • Select:实现I/O复用模型,BSD 4.2最早实现
  • Poll:实现I/O复用模型,System V最早实现
  • Epoll:实现I/O复用模型,具有信号驱动I/O模型的某些特性

(2)Select

  • POSIX规定,良好的跨平台支持
  • 监听端口的数量有限:cat /proc/sys/fs/file-max
  • 对socket是线性扫描,即采用轮询的方法,效率较低
  • 采取了内存拷贝方法来实现内核将FD消息通知给用户空间

(3)Poll

  • 本质上和select没有区别
  • 没有最大连接数的限制
  • 水平触发

(4)Epoll

  • 在Linux 2.6内核中提出的select和poll的增强版本
  • 没有最大并发连接的限制
  • 非轮询的方式,只管理“活跃”的连接,而跟连接总数无关
  • 使用mmap减少复制开销

二、Nginx介绍

(一)Nginx提供的功能:

  • 静态资源的web服务器
  • 结合FastCGI/uWSGI/SCGI等协议反向代理动态资源请求
  • http协议反向代理服务器
  • pop3/imap4协议反向代理服务器
  • tcp/udp协议的请求转发

(二)Nginx服务器的架构

(1)Nginx程序架构:Master/Worker结构
  • 一个master进程
    负载加载和分析配置文件、管理worker进程、平滑升级
  • 一个或多个worker进程
    处理并响应用户请求
(2)Nginx模块
  • 高度模块化,新版本支持DSO机制,实现动态加载/卸载模块
  • 模块分类:
    • 核心模块:core module
    • 标准模块:
      • HTTP 模块:ngx_http_*
        HTTP Core modules 默认功能
        HTTP Optional modules 需编译时指定
      • Mail模块:ngx_mail_*
      • Stream 模块:ngx_stream_*
    • 第三方模块

三、Nginx的安装和目录结构

(一)Nginx的安装:三种方法

  • 官网上下载rpm包,使用yum安装
  • 通过EPEL源安装
  • 编译安装

(二)Nginx的目录结构

  • /etc/nginx/nginx.conf:主配置文件
  • /usr/lib/systemd/system/nginx.service:Nginx服务
  • /usr/lib64/nginx/modules:存放模块
  • /usr/sbin/nginx:主程序
  • /usr/share/nginx/html/:web服务文件的默认存放位置

(三)nginx指令:

  • 格式:nginx [options]
    默认没有选项为启动nginx服务
    -h:查看帮助选项
    -t:测试nginx语法错误
    -c filename:指定配置文件(default: /etc/nginx/nginx.conf)
    -s signal:发送信号给master进程,signal可为:stop, quit, reopen, reload
    -g directives:在命令行中指明全局指令

四、Nginx的配置

(一)配置文件的组成部分

  • 主配置文件:/etc/nginx/nginx.conf
  • 子配置文件:/etc/nginx/conf.d/*.conf
    在主配置文件中使用include 子配置文件路径;的格式加载其设置
  • fastcgi, uwsgi, scgi等协议相关的配置文件
  • mime.types:支持的mime类型
  • 配置文件的配置指令格式:
    directive value [value2 ...];,指令必须以分号结尾
  • 配置指令支持使用配置变量
    • 内建变量:由Nginx模块引入,可直接引用
    • 自定义变量:由用户使用set命令定义
      set variable_namevalue;
    • 引用变量:$variable_name

(二)配置文件的结构

  • 主配置文件结构:四部分
// 主配置段,即全局配置段,对http,mail都有效
main block    
event {
...     // 事件驱动相关的配置
}      
// http, https 协议相关配置段
http {
...
}
// mail 协议相关配置段
mail {
...
}
//  stream 服务器相关配置段
stream {
...
} 

(三)全局配置段

(1)正常运行必备的配置
  1. user user [group];
    指定worker进程的运行身份,如组不指定,默认和用户名同名
    默认为nginx

  2. pid /PATH/TO/PID_FILE;
    指定存储nginx主进程PID的文件路径,服务关闭时文件消失

  3. include file | mask;
    指明包含进来的其它配置文件片断

  4. load_module file;
    模块加载配置文件:/usr/share/nginx/modules/*.conf
    指明要装载的动态模块路径: /usr/lib64/nginx/modules

(2)性能优化相关的配置
  1. worker_processes number | auto;
    worker进程的数量,通常应该为当前主机的cpu的物理核心数

  2. worker_cpu_affinity cpumask...;
    worker_cpu_affinity auto [cpumask] 减少进程切换,提高缓存命中率
    CPU MASK:以右端为开始从0数起命名CPU,如果启用则该位为1,不启用则该位为0,例如“00101010”代表系统共有8颗CPU,启用第1, 3, 5号CPU

    • 测试:系统共有4颗CPU,现在要求worker进程绑定第0,2颗CPU
vim /etc/nginx/nginx.conf
worker_processes 2;
worker_cpu_affinity 0100 0001;
systemctl restart nginx
watch -n 0.5 'ps axo pid,cmd,psr,ni | grep nginx'
  1. worker_priority number;
    指定worker进程的nice值,设定worker进程优先级:[-20,20]
  1. worker_rlimit_nofile number;
    worker进程所能够打开的文件数量上限
(3)事件驱动相关的配置
  • 格式:
    events {
    ...
    }
  1. worker_connections number;
    每个worker进程所能够打开的最大并发连接数数量
    总最大并发数:worker_processes* worker_connections

  2. use method;
    指明并发连接请求的处理方法,默认自动选择最优方法
    use epoll;

  3. accept_mutex on | off;
    处理新的连接请求的方法;on指由各个worker轮流处理新请求,off指每个新请求的到达都会通知所有的worker进程,但只有一个进程可获得连接,造成“惊群”,影响性能,默认on

(4)调试和定位配置
  1. daemon on | off;
    是否以守护进程方式运行nignx,默认是守护进程方式

  2. master_process on | off;
    是否以master/worker模型运行nginx;默认为on,off 将不启动worker

  3. error_log file [level];
    错误日志文件及其级别

(四)http协议配置段

(1)ngx_http_core_module模块相关配置

  1. server { ... }:配置虚拟机;
    一般在括号内添加listen, server_name, root等配置

  2. listen PORT|address[:port] [default_server];
    监听不同端口或者不同IP地址,并且可以设置为默认服务器

  3. server_name name ...;

    • 虚拟主机的主机名称后可跟多个由空白字符分隔的字符串

    • 支持*通配任意长度的任意字符
      e.g. server_name *.magedu.com www.magedu.*

    • 支持~起始的字符做正则表达式模式匹配,性能原因慎用
      e.g. server_name~^www\d+\.magedu\.com$:\d 表示[0-9]

    • 匹配优先级机制从高到低:
      (1) 首先是字符串精确匹配如:www.magedu.com
      (2) 左侧*通配符如:*.magedu.com
      (3) 右侧*通配符如:www.magedu.*
      (4) 正则表达式如:~^.*\.magedu\.com$
      (5) default_server

    • 需要配合dns使用,实际中很少如此使用

  4. tcp_nodelay on | off;
    在keepalived模式下的连接是否启用TCP_NODELAY选项
    当为off时,延迟发送,合并多个请求后再发送
    默认on时,不延迟发送,推荐采用默认设置
    可用于:http, server, location

  5. sendfile on | off;
    是否启用sendfile功能,在内核中封装报文直接发送
    默认off,推荐采用on

  6. server_tokens on | off | build | string;
    是否在响应报文的Server首部显示nginx版本

  7. root
    设置web资源的路径映射;用于指明请求的URL所对应的文档的目录路径,可用于http, server, location, if in location

  • 实验4-1:配置两个虚拟主机www.a.comwww.b.com
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80;
        server_name www.a.com;
        root /app/website1;
}

server {
        listen 80;
        server_name www.b.com;
        root /app/website2;
}

mkdir -p /app/website{1,2}
echo /app/website1/index.html > /app/website1/index.html
echo /app/website2/index.html > /app/website2/index.html
nginx -t
systemctl reload nginx
  • 配置DNS或在测试机上修改hosts文件后,访问指定网址成功
  1. location [ = | ~ | ~* | ^~ ] uri{ ... }
    location @name { ... }
    在一个server中location配置段可存在多个,用于实现从uri到文件系统的路径映射;ngnix会根据用户请求的URI来检查定义的所有location,并找出一个最佳匹配,而后应用其配置
  • 匹配符号
    =:对URI做精确匹配;
    ^~:对URI的最左边部分做匹配检查,不区分字符大小写
    ~:对URI做正则表达式模式匹配,区分字符大小写
    ~*:对URI做正则表达式模式匹配,不区分字符大小写
    不带符号:匹配起始于此uri的所有的uri

  • 匹配优先级从高到低:
    =, ^~, ~/~*, 不带符号

  • 实验4-2:在实验4-1的基础上,实现当访问www.a.com/admin/时指向的磁盘目录为/app/website3/admin

vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80;
        server_name www.a.com;
        root /app/website1;
        location /admin {
                root /app/website3;
        }
}

server {
        listen 80;
        server_name www.b.com;
        root /app/website2;
}

mkdir -p /app/website3/admin
echo /app/website3/admin/index.html > /app/website3/admin/index.html
systemctl reload nginx
  1. alias path;
    路径别名,文档映射的另一种机制;仅能用于location上下文
  • 注意:location中使用root指令和alias指令的意义不同
    (a) root:给定的路径对应于location中的/uri/左侧的/
    (b) alias:给定的路径对应于location中的/uri/右侧的/

  • 实验4-3:在实验4-2的基础上,实现当访问www.b.com/admin/时指向的磁盘目录为/app/website3

vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com;
        root /app/website1;
        location /admin {
                root /app/website3;
        }
}

server {
        listen 80;
        server_name www.b.com;
        root /app/website2;
        location /admin {
                alias /app/website3;
        }
}

echo /app/website3/index.html > /app/website3/index.html
systemctl reload nginx
  1. index file ...;
    指定默认网页资源,注意位于ngx_http_index_module模块

  2. error_pagecode ... [=[response]] uri;
    模块:ngx_http_core_module
    定义错误页,以指定的响应状态码进行响应
    可用位置:http, server, location, if in location

  • 实验4-4:在实验4-3的基础上,实现当访问www.a.com不存在的地址时,返回指定的404错误文件,并且响应码应为200
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com;
        root /app/website1;
        location /admin {
                root /app/website3;
        }
        error_page 404 =200 /404.html;
        location /404.html {
                root /app/website1/error_page;
        }
}

server {
        listen 80;
        server_name www.b.com;
        root /app/website2;
        location /admin {
                alias /app/website3;
        }
}

mkdir  /app/website1/error_page
echo /app/website1/error_page/404.html > /app/website1/error_page/404.html
systemctl restart nginx

  1. try_files file ... uri;
    try_files file ... =code;
    按顺序检查文件是否存在,返回第一个找到的文件或文件夹(结尾加斜线表示为文件夹),如果所有的文件或文件夹都找不到,会进行一个内部重定向到最后一个参数。只有最后一个参数可以引起一个内部重定向,之前的参数只设置内部URI的指向。最后一个参数是回退URI且必须存在,否则会出现内部500错误
  • 实验4-5:在实验4-4的基础上,实现访问www.a.com/txt时,依次在/txt, /txt/index.html, /txt.html路径查询是否有相应文件,若都没有,则返回404错误
vim /etc/nginx/conf.d/vhost.conf 
server {
        listen 80 ;
        server_name www.a.com;
        root /app/website1;
        location /admin {
                root /app/website3;
        }
        location /txt {
                try_files $uri $uri/index.html $uri.html =404;
        }
        error_page 404 =200 /404.html;
        location /404.html {
                root /app/website1/error_page;
        }
}

nginx -t
systemctl reload nginx
echo /app/website1/txt > /app/website1/txt
echo /app/website1/txt.html > /app/website1/txt.html
// 第一次登录www.a.com/txt
rm -rf /app/website1/txt
mkdir /app/website1/txt
echo /app/website1/txt/index.html > /app/website1/txt/index.html
// 第二次登录www.a.com/txt
 rm -rf /app/website1/txt
// 第三次登录www.a.com/txt
rm -rf /app/website1/txt.html
// 第四次登录www.a.com/txt
  1. keepalive_timeout timeout [header_timeout];
    设定保持连接超时时长,0表示禁止长连接,默认为75s

  2. keepalive_requests number;
    在一次长连接上所允许请求的资源的最大数量
    默认为100

  3. keepalive_disable none | browser ...
    对哪种浏览器禁用长连接

  4. send_timeout time;
    向客户端发送响应报文的超时时长,此处是指两次写操作之间的间隔时长,而非整个响应过程的传输时长

  5. client_body_buffer_size size;
    用于接收每个客户端请求报文的body部分的缓冲区大小;默认为16k;超出此大小时,其将被暂存到磁盘上的由client_body_temp_path指令所定义的位置

  6. client_body_temp_path path [level1 [level2 [level3]]];
    设定用于存储客户端请求报文的body部分的临时存储路径及子目录结构和数量
    目录名为16进制的数字;
    client_body_temp_path /var/tmp/client_body 1 2 2
    1:1级目录占1位16进制,即2^4=16个目录0-f
    2 :2级目录占2位16进制,即2^8=256个目录00-ff
    2 :3级目录占2位16进制,即2^8=256个目录00-ff

  7. limit_rate rate;
    限制响应给客户端的传输速率,单位是bytes/second
    默认值0表示无限制

  8. limit_except method ... { ... },仅用于location
    限制客户端使用除了指定的请求方法之外的其它方法
    method: GET, HEAD, POST, PUT, DELETE, MKCOL, COPY, MOVE, OPTIONS, PROPFIND, PROPPATCH, LOCK, UNLOCK, PATCH

  • 实验4-6:实现只有192.168.136.229主机可以使用除了GET和HEAD之外的其他方法访问www.a.com/admin
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com;
        root /app/website1;
        location /admin {
                root /app/website3;
                limit_except GET {
                        allow 192.168.136.229;
                        deny all;
                }
        }
}

nginx -s reload

来自192.168.136.229的主机可以使用POST方法

来自192.168.136.129的主机不可以使用POST方法

  1. aio on | off | threads[=pool];
    是否启用aio功能

  2. directio size | off;
    是否同步(直接)写磁盘,而非写缓存,在Linux主机启用O_DIRECT标记,则文件大于等于给定大小时使用,例如directio 4m

  3. open_file_cache off;
    open_file_cache max=N [inactive=time];
    nginx可以缓存以下三种信息:
    (1) 文件元数据:文件的描述符、文件大小和最近一次的修改时间
    (2) 打开的目录结构
    (3) 没有找到的或者没有权限访问的文件的相关信息
    max=N:可缓存的缓存项上限;达到上限后会使用LRU算法实现管理
    inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项,将被删除

  4. open_file_cache_errors on | off;
    是否缓存查找时发生错误的文件一类的信息
    默认值为off

  5. open_file_cache_min_uses number;
    open_file_cache指令的inactive参数指定的时长内,至少被命中此处指定的次数方可被归类为活动项
    默认值为1

  6. open_file_cache_valid time;
    缓存项有效性的检查频率
    默认值为60s

(2)ngx_http_access_module模块相关配置

  • 实现基于ip的访问控制功能
  1. allow address | CIDR | unix: | all;
  2. deny address | CIDR | unix: | all;
    上下文:http, server, location, limit_except
    自上而下检查,一旦匹配,将生效,条件严格的置前
  • 实验4-7:实现只有192.168.136.229主机可以登录www.a.com
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com;
        root /app/website1;
        allow 192.168.136.229;
        deny all;        
}
nginx -s reload

(3)ngx_http_auth_basic_module模块相关配置

  • 实现基于用户的访问控制,使用basic机制进行用户认证
  1. auth_basic string | off;
  2. auth_basic_user_file file;
    由htpasswd命令(httpd-tools提供)实现加密文本文件
  • 实验4-8:实现使用basic机制进行用户认证
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location /admin {
                root /app/website3;
                auth_basic "admin auth";
                auth_basic_user_file /etc/nginx/conf.d/.auth_passwd;
                }
        error_page 404 =200 /404.html;
        location /404.html {
                root /app/website1/error_page;
        }
}
nginx -s reload
htpasswd -c /etc/nginx/conf.d/.auth_passwd user1

(4)ngx_http_stub_status_module模块

  • 用于输出nginx的基本状态信息
location /status {
      stub_status;
}
  • 输出信息:
    Active connections:当前状态,活动状态的连接数
    accepts:统计总值,已经接受的客户端请求的总数
    handled:统计总值,已经处理完成的客户端请求的总数
    requests:统计总值,客户端发来的总的请求数
    Reading:当前状态,正在读取客户端请求报文首部的连接的连接数
    Writing:当前状态,正在向客户端发送响应报文过程中的连接数
    Waiting:当前状态,正在等待客户端发出请求的空闲连接数

(5)ngx_http_log_module模块

指定日志格式记录请求

  1. log_format name string ...;
    string可以使用nginx核心模块及其它模块内嵌的变量

  2. access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
    access_log off;
    访问日志文件路径,格式及相关的缓冲的配置

  • 实验4-9:指定访问日志文件的路径/var/lib/nginx/tmp/nginx-access.log,缓冲区大小16k
vim /etc/nginx/nginx.conf
// log_format必须在http上下文中
log_format  compression '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';  
access_log  /var/lib/nginx/tmp/nginx-access.log compression buffer=16k;

nginx -s reload;
  1. open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
    open_log_file_cache off;
    缓存各日志文件相关的元数据信息
    max:缓存的最大文件描述符数量
    min_uses:在inactive指定的时长内访问大于等于此值方可被当作活动项
    inactive:非活动时长
    valid:验证缓存中各缓存项是否为活动项的时间间隔

(6)ngx_http_gzip_module模块

用gzip方法压缩响应数据,节约带宽

  1. gzip on | off;
    启用或禁用gzip压缩

  2. gzip_comp_level level;
    压缩比由低到高:1 到9
    默认:1

  3. gzip_disable regex ...;
    匹配到客户端浏览器不执行压缩

  4. gzip_min_length length;
    启用压缩功能的响应报文大小阈值

  5. gzip_http_version 1.0 | 1.1;
    设定启用压缩功能时,协议的最小版本
    默认:1.1

  6. gzip_buffers number size;
    支持实现压缩功能时缓冲区数量及每个缓存区的大小
    默认:32 4k 或16 8k

  7. gzip_types mime-type ...;
    指明仅对哪些类型的资源执行压缩操作;即压缩过滤器
    默认包含有text/html,不用显式指定,否则出错

  8. gzip_vary on | off;
    如果启用压缩,是否在响应报文首部插入“Vary: Accept-Encoding”

  9. gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;
    nginx对于代理服务器请求的响应报文,在何种条件下启用压缩功能
    off:对被代理的请求不启用压缩
    expired, no-cache, no-store, private:对代理服务器请求的响应报文首部Cache-Control值任何一个,启用压缩功能

  • 实验4-10 开启压缩,响应报文首部插入“Vary: Accept-Encoding”,支持压缩html, js, css, txt文件
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        gzip on;
        gzip_comp_level 9;
        gzip_min_length 32;
        gzip_types text/css application/javascript text/plain;
        gzip_proxied any;
        gzip_vary on;
}
nginx -s reload

(7)ngx_http_ssl_module模块

  1. ssl on | off;
    为指定虚拟机启用HTTPS protocol,建议用listen指令代替

  2. ssl_certificate file;
    当前虚拟主机使用PEM格式的证书文件

  3. ssl_certificate_key file;
    当前虚拟主机上与其证书匹配的私钥文件

  4. ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
    支持ssl协议版本,默认为后三个

  5. ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
    builtin[:size]:使用OpenSSL内建缓存,为每worker进程私有
    [shared:name:size]:在各worker之间使用一个共享的缓存

  6. ssl_session_timeout time;
    客户端连接可以复用ssl session cache中缓存的ssl参数的有效时长,默认5m

server {
        listen 443 ssl ;
        server_name www.a.com ;
        root /app/website1;
        ssl_certificate /etc/nginx/conf.d/ssl/nginx1.crt;
        ssl_certificate_key /etc/nginx/conf.d/ssl/nginx1.key;
        ssl_session_cache shared:ssl_cache:5m;
        ssl_session_timeout 5m;
}

server {
        listen 443 ssl ;
        server_name www.b.com ;
        root /app/website2;
        ssl_certificate /etc/nginx/conf.d/ssl/nginx2.crt;
        ssl_certificate_key /etc/nginx/conf.d/ssl/nginx2.key;
        ssl_session_cache shared:ssl_cache:5m;
        ssl_session_timeout 5m;
}
// 建立自签名证书
cd /etc/pki/tls/certs/
make nginx1.crt
make nginx2.crt
openssl rsa -in nginx1.key -out nginx1.key 
openssl rsa -in nginx2.key -out nginx2.key 
mkdir -p /etc/nginx/conf.d/ssl
cp nginx* /etc/nginx/conf.d/ssl
nginx -s reload

(8)ngx_http_rewrite_module模块

  1. rewrite regex replacement [flag]
  • 将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为replacement指定的新的URI

  • 注意:如果在同一级配置块中存在多个rewrite规则,那么会自上而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查;
    隐含有循环机制,但不超过10次;如果超过,提示500响应码,[flag]所表示的标志位用于控制此循环机制
    如果replacement是以http://或https://开头,则替换结果会直接以重向返回给客户端

  • [flag]

    • last:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URI启动新一轮重写检查;提前重启新一轮循环,不建议在location中使用
    • break:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环,建议在location中使用
    • redirect:临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;不能以http://或https://开头,使用相对路径,状态码:302
    • permanent:重写完成后以永久重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求,状态码:301
  1. return
    return code [text];
    return code URL;
    return URL;
    停止处理,并返回给客户端指定的响应码

  2. rewrite_log on | off;
    是否开启重写日志, 发送至error_log(notice level)

  3. set $variable value;
    用户自定义变量
    注意:变量定义和调用都要以$开头

  4. if (condition) { ... }
    引入新的上下文,条件满足时,执行配置块中的配置指令;server, location

  • condition:

    • 比较操作符:
      ==:相同
      !=:不同
      ~:模式匹配,区分字符大小写
      ~*:模式匹配,不区分字符大小写
      !~:模式不匹配,区分字符大小写
      !~*:模式不匹配,不区分字符大小写
    • 文件及目录存在性判断:
      -e, !-e:存在(包括文件,目录,软链接)
      -f, !-f:文件
      -d, !-d:目录
      -x, !-x:执行
  • 实验4-12:实现当访问www.a.com/zz时自动跳转至www.a.com/zhengzhou

vim /etc/nginx/conf.d/vhost.conf 
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location /zz {
                rewrite ^/zz/(.*)$ /zhengzhou/$1 permanent;
        }
        location /zhengzhou{
                alias /app/website1/zhengzhou;
        }
}

mkdir /app/website1/zhengzhou
echo /app/website1/zhengzhou/index.html > /app/website1/zhengzhou/index.html
nginx -s reload

(9)ngx_http_referer_module模块

用来阻止referer首部无有效值的请求访问,可防止盗链

  1. valid_referers none | blocked | server_names | string...;
    定义referer首部的合法可用值,不能匹配的将是非法值
    none:请求报文首部没有referer首部
    blocked:请求报文有referer首部,但无有效值
    server_names:参数,其可以有值作为主机名或主机名模式
    arbitrary_string:任意字符串,但可使用*作通配符
    regular expression:被指定的正则表达式模式匹配到的字符串,要使用~开头
vim /app/website2/index.html
/app/website2/index.html
![](http://www.a.com/a.jpg)
// 第一次测试,登录www.b.com,应该能看到图片
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        valid_referers none block server_names *.a.com  ~\.baidu\.;
        if ($invalid_referer) {
                return 403;
        }
}
server {
        listen 80;
        server_name www.b.com;
        root /app/website2;
}
nginx -s reload
// 第二次测试,登录www.b.com,无法看到图片

第一次测试

第二次测试

(10)ngx_http_proxy_module模块

  • 转发请求至另一台主机
  1. proxy_pass URL;
    Context: location, if in location, limit_except
  • proxy_pass后面路径不带uri时,会将location的uri传递(附加)给后端主机
server {
...
server_name HOSTNAME;
location /uri/ {
    proxy_pass http://host[:port];    // 最后没有/
    }
...
}

上面示例:http://HOSTNAME/uri --> http://host/uri

  • proxy_pass后面的路径是一个uri时,其会将location的uri替换为proxy_pass的uri
server {
...
server_name HOSTNAME;
location /uri/ {
    proxy_pass http://host/new_uri/;
    }
...
}

上面示例:http://HOSTNAME/uri/ --> http://host/new_uri/

  • 如果location定义其uri时使用了正则表达式的模式,则proxy_pass之后必须不能使用uri; 用户请求时传递的uri将直接附加至后端服务器之后
server {
...
server_name HOSTNAME;
location ~|~* /uri/ {
    proxy_pass http://host;    // 不能加/
    }
...
}

上面示例:http://HOSTNAME/uri/ --> http://host/uri/

  • 实验4-14:将访问www.a.com网址下图片的请求调度至一台服务器,将访问www.a.com网址下txt目录的请求调度至另一台服务器

    • 实验环境:
      反向代理服务器:192.168.136.230,nginx服务器
      图片服务器:192.168.136.229,httpd服务器
      txt目录服务器:192.168.136.129,httpd服务器
    • 实验过程
    // 192.168.136.230的配置
    server {
          listen 80 ;
          server_name www.a.com ;
          root /app/website1;
          location ~ \.(jpg|gif|png)$ {
                  proxy_pass http://192.168.136.229;
          }
          location /txt {
                  proxy_pass http://192.168.136.129;
          }
    }
    nginx -s reload
    
    // 192.168.136.229的配置
    cp /usr/share/pixmaps/faces/leaf.jpg /var/www/html/
    cp /usr/share/pixmaps/faces/chess.jpg /var/www/html/images
    
    // 192.168.136.129的配置
    echo /var/www/html/index.html > /var/www/html/index.html
    echo /var/www/html/txt/index.html > /var/www/html/txt/index.html
    
    • 测试

    访问www.a.com/images/chess.jpg成功,是因为chess.jpg位于192.168.136.229网页根目录的images子目录下。访问www.a.com/images/leaf.jpg失败,是因为leaf.jpg位于192.168.136.229网页根目录下。访问www.a.com/txt成功,从返回结果看,实际访问位置位于192.168.136.129网页根目录的txt子目录下。

    • 修改配置文件如下:
    // 192.168.136.230的配置
    server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location ~ \.(jpg|gif|png)$ {
                proxy_pass http://192.168.136.229;
        }
        location /txt {
                proxy_pass http://192.168.136.129/;     // 多了/
        }
    }
    nginx -s reload
    
    • 测试

    从返回结果看,将192.168.136.230的转发配置URL加/符号后,转发位置被完全替换,直接转发至192.168.136.129的网页根目录下。至于图片转发设置中location用到了正则表达式,则URL不能再增加/符号了,否则语法检查会报错。

  1. proxy_set_header field value;
    设定发往后端主机的请求报文的请求首部的值
    Context: http, server, location
    proxy_set_header X-Real-IP $remote_addr;
  • 实验4-15:实验环境继承4-14,实现在后端主机的日志中显示客户端的ip地址
// 192.168.136.230配置
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        proxy_set_header X-Real-IP $remote_addr;
        location ~ \.(jpg|gif|png)$ {
                proxy_pass http://192.168.136.229;
        }
        location /txt {
                proxy_pass http://192.168.136.129/;
        }
}
nginx -s reload

// 192.168.136.229配置
vim /etc/httpd/conf/httpd.conf
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{X-Real-IP}i\" \"%{User-Agent}i\"" combined
CustomLog logs/access_log combined
service httpd reload

左边红框对应%h,指代反向代理服务器IP;右边红框对应%{X-Real-IP}i,指代客户端IP

  1. proxy_cache_path;
    定义可用于proxy功能的缓存;context: http
    proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time] [max_size=size];

  2. proxy_cache zone | off; 默认off
    指明调用的缓存,或关闭缓存机制;Context: http, server, location

  3. proxy_cache_key string;
    缓存中用于“键”的内容
    默认值:proxy_cache_key $scheme$proxy_host$request_uri;

  4. proxy_cache_valid [code ...] time;
    定义对特定响应码的响应内容的缓存时长
    定义在http{...}中

  • 实验4-16:实现反向代理服务器缓存
vim /etc/nginx/nginx.conf
proxy_cache_path /var/lib/nginx/tmp/proxy_cache levels=1:2:2 keys_zone=proxycache:20m inactive=120s max_size=1g; 
// 定义缓存路径和参数,位置只能在http上下文中

vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        proxy_set_header X-Real-IP $remote_addr;
        location ~ \.(jpg|gif|png)$ {
                proxy_pass http://192.168.136.229;
        }
        location /txt {
                proxy_pass http://192.168.136.129;
        }
        proxy_cache proxycache;              // 引用定义的缓存名称
        proxy_cache_key $request_uri;        // 计算哈希表的key,此处为请求的uri
        proxy_cache_valid 200 301 302 1h;    // 指定不同响应结果缓存的生命期
        proxy_cache_valid any 1m;
}

设置缓存前的ab测试结果

设置缓存后的ab测试结果

  1. proxy_cache_use_stale;
    proxy_cache_use_stale error | timeout | invalid_header| updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ...
    在被代理的后端服务器出现哪种情况下,可以真接使用过期的缓存响应客户端

  2. proxy_cache_methods GET | HEAD | POST ...;
    对哪些客户端请求方法对应的响应进行缓存,GET和HEAD方法总是被缓存

  3. proxy_hide_header field;
    默认nginx在响应报文不传递后端服务器的首部字段Date, Server, X-Pad, X-Accel-等,用于隐藏后端服务器特定的响应首部

  4. proxy_connect_timeout time;
    定义与后端服务器建立连接的超时时长,如超时会出现502错误,默认为60s,一般不建议超出75s,

  5. proxy_send_timeout time;
    将请求发送给后端服务器的超时时长;默认为60s

  6. proxy_read_timeout time;
    等待后端服务器发送响应报文的超时时长,默认为60s

(11)ngx_http_headers_module模块

  • 向由代理服务器响应给客户端的响应报文添加自定义首部,或修改指定首部的值
  1. add_header name value [always];
    添加自定义首部
    add_header X-Via $server_addr;
    add_header X-Cache $upstream_cache_status;
    add_header X-Accel $server_name;

  2. add_trailer name value [always];
    添加自定义响应信息的尾部

(12)ngx_http_fastcgi_module模块

  • 转发请求到FastCGI服务器,不支持php模块方式
  1. fastcgi_pass address;
    address为后端的fastcgi server的地址
    可用位置:location, if in location

  2. fastcgi_index name;
    fastcgi默认的主页资源

  3. fastcgi_param parameter value [if_not_empty];
    设置传递给FastCGI服务器的参数值,可以是文本,变量或组合

  4. fastcgi_cache_path path [levels=levels] keys_zone=name:size[inactive=time] [max_size=size];
    定义fastcgi的缓存;context: http

  5. fastcgi_cache zone | off;
    调用指定的缓存空间来缓存数据
    可用位置:http, server, location

  6. fastcgi_cache_key string;
    定义用作缓存项的key的字符串

  7. fastcgi_cache_methods GET | HEAD | POST ...;
    为哪些请求方法使用缓存

  8. fastcgi_cache_min_uses number;
    缓存空间中的缓存项在inactive定义的非活动时间内至少要被访问到此处所指定的次数方可被认作活动项

  9. fastcgi_keep_conn on | off;
    收到后端服务器响应后,fastcgi服务器是否关闭连接,建议启用长连接

  10. fastcgi_cache_valid [code ...] time;
    不同的响应码各自的缓存时长

  • 实验4-17:实现lnmp,并设置php fastcgi模式缓存
    主机1:nginx服务器,ip: 192.168.136.230
    主机2:php-fpm服务器,ip: 192.168.136.229
    主机3:MariaDB服务器,ip: 192.168.136.130
// 主机2:安装php-fpm, php-mysql
yum install php-fpm php-mysql
vim /etc/php-fpm.d/www.conf
listen = 9000
listen.allowed_clients = 127.0.0.1,192.168.136.230
pm.status_path = /status     // 显示php状态,测试用
ping.path = /ping            // 测试用
ping.response = pong

service php-fpm start
mkdir -p /app/php
vim /app/php/index.php       // 建立php测试文件
<?php
phpinfo();
?>
// 主机1:配置fastcgi
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location ~ \.php$ {
                fastcgi_pass 192.168.136.229:9000;
                fastcgi_index index.php;
                include fastcgi.conf;
        }
        location ~ ^/(status|ping)$ {
                fastcgi_pass 192.168.136.229:9000;
                fastcgi_index index.php;
                include fastcgi.conf;
        }
}
nginx -t 
nginx -s reload
  • 测试php是否成功连接
// 主机3:安装mariadb
yum install mariadb-server
systemctl start mariadb
mysql_secure_installation
mysql -uroot -pmagedu
// 建立数据库和用户
MariaDB [(none)]> create database wpdb;
MariaDB [(none)]> grant all on wpdb.* to 'wpuser'@'192.168.136.229' identified by 'centos';
// 主机2:编辑php文件,测试是否成功连接mysql
vim /app/php/index.php
<?php
$mysqli=new mysqli("192.168.136.130","wpuser","centos");
if(mysqli_connect_errno()){
echo "Failed";
$mysqli=null;
exit;
}
echo "Success";
$mysqli->close();
?>
  • 测试php是否成功连接数据库
  • 测试一下php的性能
  • 设置php的fastcgi模式缓存
vim /etc/nginx/nginx.conf
fastcgi_cache_path /var/lib/nginx/tmp/fastcgi_cache levels=1:2:2 keys_zone=fastcgicache:20m inactive=120s max_size=1g;
// 定义缓存路径和参数,位置只能在http上下文中
vim /etc/nginx/conf.d/vhost.conf
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location ~ \.php$ {
                fastcgi_pass 192.168.136.229:9000;
                fastcgi_index index.php;
                include fastcgi.conf;
                fastcgi_cache fastcgicache;
                fastcgi_cache_key $request_uri;
                fastcgi_cache_valid 200 302 10m;
                fastcgi_cache_valid 301 1h;
                fastcgi_cache_valid any 1m;
        }
}
  • 在fastcgi的缓存模式下测试性能,有明显提高

(13)ngx_http_upstream_module模块

  • 用于将多个服务器定义成服务器组,而由proxy_pass, fastcgi_pass等指令进行引用
  1. upstream name { ... }
    定义后端服务器组,会引入一个新的上下文;context: http
    默认调度算法是wrr
  • 格式:
    upstream httpdsrvs{
    server ...
    server...
    ...
    }
  1. server address [parameters];
    在upstream上下文中server成员,以及相关的参数;context: upstream
  • address的表示格式:
    unix:/PATH/TO/SOME_SOCK_FILE
    IP[:PORT]
    HOSTNAME[:PORT]
  • parameters:
    weight=number:权重,默认为1
    max_conns:连接后端报务器最大并发活动连接数,1.11.5后支持
    max_fails=number:失败尝试最大次数;超出此处指定的次数时,server将被标记为不可用,默认为1
    fail_timeout=time:后端服务器标记为不可用状态的连接超时时长,默认10s
    backup:将服务器标记为“备用”,即所有服务器均不可用时才启用
    down:标记为“不可用”,配合ip_hash使用,实现灰度发布
  1. ip_hash
    源地址hash调度方法

  2. least_conn
    最少连接调度算法,当server拥有不同的权重时其为wlc,当所有后端主机连接数相同时,则使用wrr,适用于长连接

  3. hash key [consistent]
    基于指定的key的hash表来实现对请求的调度,此处的key可以直接文本、变量或二者组合
    作用:将请求分类,同一类请求将发往同一个upstream server,使用consistent参数,将使用ketama一致性hash算法,适用于后端是cache服务器(如varnish)时使用
    hash $request_uri consistent;
    hash $remote_addr;

  4. keepalive连接数N;
    为每个worker进程保留的空闲的长连接数量,可节约nginx端口,并减少连接管理的消耗

  • 实验4-18:实现负载均衡
    主机1:nginx负载均衡服务器,ip: 192.168.136.230
    主机2:httpd服务器,ip: 192.168.136.229
    主机3:httpd服务器,ip: 192.168.136.129
// 主机1配置
vim /etc/nginx/nginx.conf            // upstream定义必须在http上下文中
upstream websrvs {
        server 192.168.136.229 weight=2;     // wrr算法调度,权重为2
        server 192.168.136.129 weight=1;     // wrr算法调度,权重为1
        server 127.0.0.1:8080 backup;
    }
vim /etc/nginx/conf.d/vhost.conf     // 设置proxy转发
server {
        listen 80 ;
        server_name www.a.com ;
        root /app/website1;
        location /  {
                proxy_pass http://websrvs;
        }
}

server {
        listen 8080;
        root /app/website1;
}
// 设置相关网页文件
echo sorry page > /app/website1/index.html
nginx -t
nginx -s reload

// 主机2, 3的配置
service httpd start
echo homepage on 192.168.136.229 > /var/www/html/index.html     // 设置主机2默认主页
echo homepage on 192.168.136.129 > /var/www/html/index.html     // 设置主机3默认主页
  • 测试调度结果

可以看到按照wrr算法的相应权重调度页面

当关闭主机2的httpd服务后,自动进行健康性检查,全部调度至主机3

当关闭主机3的httpd服务后,自动调度至本机的sorry page

  • 在此基础上,实现将相同请求url调度至同一台服务器(模拟调度至缓存服务器)
// 修改主机1设置
vim /etc/nginx/nginx.conf
upstream websrvs {
        server 192.168.136.229 weight=2;
        server 192.168.136.129 weight=1;
        server 127.0.0.1:8080 backup;
        hash $request_uri consistent;     // 添加此行
    }
nginx -t
nginx -s reload

// 主机2, 3创建网页文件
for i in {1..10}; do echo "page$i on 192.168.136.229" > /var/www/html/page$i.html;done     // 主机2上创建文件
for i in {1..10}; do echo "page$i on 192.168.136.129" > /var/www/html/page$i.html;done     // 主机3上创建文件
  • 测试相同请求的调度绑定

不同的网页请求可能被调度到不同的主机,但相同的请求调度的主机不会发生变化

(五)nginx的传输层调度/反向代理配置

(1)ngx_stream_core_module模块

模拟基于tcp或udp服务连接的反向代理,即工作于传输层的反向代理或调度器

  1. stream { ... }
    定义stream相关的服务;context: main

  2. listen
    listen address:port [ssl] [udp] [proxy_protocol];context: server

(2)ngx_stream_proxy_module模块

可实现代理基于TCP, UDP (1.9.13), UNIX-domain sockets的数据流

  1. proxy_pass address;
    指定后端服务器地址

  2. proxy_timeout timeout;
    无数据传输时,保持连接状态的超时时长
    默认为10m

  3. proxy_connect_timeout time;
    设置nginx与被代理的服务器尝试建立连接的超时时长
    默认为60s

  • 实验5-1:实现传输层转发
    主机1:nginx负载均衡服务器,ip: 192.168.136.230
    主机2:mysql服务器,ip: 192.168.136.130,CentOS 7
    主机3:mysql服务器,ip: 192.168.136.129,CentOS 6
// 主机2, 3的mysql配置,为方便观察调度结果,主机2安装MariaDB 5.5,主机3安装Mysql 5.1
// 主机2
yum install mariadb-server
systemctl start mariadb
mysql_secure_installation
mysql -uroot -pmagedu
MariaDB [(none)]> create user 'testuser'@'192.168.136.%' identified by 'centos';
mysql -utestuser -pcentos -h192.168.136.130     // 测试账号testuser能否正常登陆
// 主机3
yum install mysql-server
service mysqld start
mysql_secure_installation
mysql -uroot -pmagedu
mysql> create user 'testuser'@'192.168.136.%' identified by 'centos';
mysql -utestuser -pcentos -h192.168.136.129     // 测试账号testuser能否正常登陆

// 主机1配置,stream必须在main中,不能在http的括号中
vim /etc/nginx/nginx.conf
stream {
        upstream mysqlsrvs {
                server 192.168.136.130:3306;
                server 192.168.136.129:3306;
                least_conn;     // 最少连接调度算法
        }

        server {
                listen 192.168.136.230:3306;
                proxy_pass mysqlsrvs;
        }
}
nginx -t
nginx -s reload
  • 在其他主机测试,根据登陆数据库的版本号确定成功调度

五、Nginx的编译安装

  • 编译实验:实现nginx的编译安装,并且通过修改源代码实现
    显示服务器名称与版本号时,显示信息为"zengine/5.5.5"
    只显示服务器名称时,显示信息为"wanginx"

(一)安装环境准备

yum groupinstall "development tools"
yum install pcre-devel openssl-devel
useradd -r nginx -s /sbin/nologin

(二)修改源代码实现服务器名称和版本信息的定制化

tar xvf nginx-1.12.2.tar.gz -C /usr/local/src/
cd /usr/local/src/nginx-1.12.2/
vim src/http/ngx_http_header_filter_module.c     // 修改只显示服务器名称时的信息
static u_char ngx_http_server_string[] = "Server: wanginx" CRLF;
vim src/core/nginx.h                             // 修改服务器和版本号信息均显示时的信息
#define NGINX_VERSION      "5.5.5"
#define NGINX_VER          "zengine/" NGINX_VERSION

(三)编译安装

./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_dav_module --with-http_stub_status_module --with-threads --with-file-aio
make && make install

(四)配置nginx,启动服务

vim /etc/profile.d/nginx.sh
PATH=/usr/local/nginx/sbin:$PATH
source /etc/profile.d/nginx.sh
nginx 

(五)关闭服务器版本信息显示

vim /etc/nginx/nginx.conf
server_tokens off;     // 在http括号中添加此行
nginx -t
nginx -s reload

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 71,083评论 12 116
  • 上一篇《WEB请求处理一:浏览器请求发起处理》,我们讲述了浏览器端请求发起过程,通过DNS域名解析服务器IP,并建...
    猿码道阅读 66,241评论 21 315
  • Nginx简介 解决基于进程模型产生的C10K问题,请求时即使无状态连接如web服务都无法达到并发响应量级一万的现...
    魏镇坪阅读 940评论 0 9
  • 1.简介:  Nginx:engine X ,2002年,开源,商业版 http协议:web服务器(类似于ht...
    尛尛大尹阅读 461评论 0 3
  • 因為自己一個人 才能靜下來思考 因為自己一個人 才能清楚看見 自己存在的位置 因為自己一個人 所以事物 變得更加簡...
    蔡振源阅读 162评论 2 2