Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Nginx知识集锦

10
Jan
2016

Nginx知识集锦

By Alex
/ in Linux
/ tags LB, Nginx
0 Comments
Nginx简介

Nginx读作Engine X,在被广泛的用作:

  1. HTTP服务器
  2. 反向代理服务器
  3. 邮件代理服务器
  4. 通用的TCP/UDP代理

Nginx服务由一个Master进程和多个Worker进程构成。Master进程的主要职责是读取、解析配置文件,并维护工作进程。工作进程则负责实际的请求处理。为了高效的在Worker之间分发请求,Nginx引入了依赖于操作系统的、高效的事件驱动模型。Worker进程的数量常常根据CPU核心数设置。

对比Apache2
  Apache2 Nginx
Web服务器

适合处理动态请求

稳定,功能强

更强大的rewrite

超多的模块

更少的资源消耗:10000非活跃Keep-Alive连接仅仅消耗2.5M内存

更多的并发连接,理论上不受限制,取决于内存,10W没问题

静态处理性能比Apache2高3倍

简单,效率高

热部署:Master管理进程和Worker工作进程分离,可以在不停机的前提下升级二进制文件、修改配置、更换日志文件

负载均衡器 通常不作为NLB

作为反向代理抗住并发 —— 异步模型

通信模型 同步多进程模型,一个连接对应一个进程 异步模型,多个连接(万级别)可以对应一个进程 
安装
安装
Shell
1
2
3
4
5
6
7
8
9
10
sudo apt-get install libpcre3 libpcre3-dev
sudo apt-get install zlib1g zlib1g.dev
sudo apt-get install openssl libssl-dev
 
wget https://nginx.org/download/nginx-1.13.8.tar.gz
tar xzf nginx-1.13.8.tar.gz  && rm nginx-1.13.8.tar.gz
cd nginx-1.13.8/
./configure --prefix=/usr
make -j8
sudo make install
configure参数
参数 说明
prefix 安装前缀
sbin-path 二进制文件安装前缀,默认$prefix/sbin/nginx
conf-path Nginx主配置文件位置,默认$prefix/conf/nginx.conf.
pid-path PID文件位置
error-log-path 主错误日志位置,默认$prefix/logs/error.log
http-log-path 主请求日志位置,默认$prefix/logs/access.log
user
group
Nginx工作进程的用户、组
with-*_module
without-*_module

启用/禁用特定模块

--with-stream 启用第四层代理/LB,即ngx_stream_core_module模块

with-* 从指定位置寻找依赖库基础
add-module 安装第三方模块到Nginx的二进制文件中
add-dynamic-module 指定动态模块目录

配置示例:

Shell
1
2
3
4
5
6
7
./configure
    --sbin-path=/usr/bin/nginx
    --conf-path=/etc/nginx/nginx.conf
    --pid-path=/var/run/nginx/nginx.pid
    --with-http_ssl_module
    --with-pcre=../pcre-8.41
    --with-zlib=../zlib-1.2.11

如果前缀设置为/usr,则主要路径如下:

Shell
1
2
3
4
5
6
7
8
nginx path prefix: "/usr"
nginx binary file: "/usr/sbin/nginx"
nginx modules path: "/usr/modules"
nginx configuration prefix: "/usr/conf"
nginx configuration file: "/usr/conf/nginx.conf"
nginx pid file: "/usr/logs/nginx.pid"
nginx error log file: "/usr/logs/error.log"
nginx http access log file: "/usr/logs/access.log" 
启动
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
#  -V            : 显示版本和构建配置选项,然后退出
#  -t            : 测试配置然后退出
#  -T            : 测试并输出配置,然后退出
#  -q            : 测试配置时抑制非错误类信息
#  -s signal     : 向Master进程发送信号:
#                     stop     快速关闭
#                     quit     优雅关闭
#                     reopen   重新打开日志文件
#                     reload   重新载入配置文件
#  -c filename   :指定配置文件
#  -g directives : 设置全局指令
 
 
# 启动Nginx并在后台运行
sudo nginx
# 检查服务器是否成功启动
curl http://localhost:80
 
 
# 停止服务器
sudo nginx -s stop
添加模块
安装模块

可以在编译Nginx期间,把模块编译进来:

Shell
1
2
# 目录/path/to/echo-nginx-module中存放echo模块的源代码
./configure --prefix=/opt/nginx \ --add-module=/path/to/echo-nginx-module
动态模块

从1.9.11版本开始,Nginx支持动态模块。在编译Nginx时,你可以指定动态模块的存放目录: 

Shell
1
./configure --add-dynamic-module=/usr/local/nginx/modules

需要添加动态模块时,首先独立编译模块,然后在Nginx配置文件中指定:

Shell
1
2
# 加载动态模块
load_module /path/to/modules/ngx_http_echo_module.so;
理解配置文件
配置文件结构

Nginx由很多模块组成,这些模块由控制文件中的指令(Directives)控制。指令可以分为:简单指令、块指令:

  1. 简单指令格式: directive-name param1 param2;
  2.  块指令格式:
    Shell
    1
    2
    3
    4
    5
    6
    7
    # 跨指令体使用花括号包围
    directive param1 {
        # 块指令内部可以包含其它指令
        directive {
     
        }
    }

    块指令中可以包含其它指令,外部的指令称为上下文(Context)。不位于任何{}内的指令,可以认为是位于主上下文(Main Context)。例如events、http指令位于主上下文中,server可以位于http中,location可以位于server中

  3. 以#号开头的部分表示注释

变量

你可以在Nginx配置文件中读写变量,Nginx的变量只有一种类型 —— 字符串。

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建变量并赋值
set $a "hello world";
# 支持插值
set $b "$a, $a";
# 用花括号包围变量名,可以防止歧义
set $b "${first}world";
 
# 要使用$符号本身,可以使用ngx_geo模块的指令geo
geo $dollar {
    default "$";
}
 
server {
    location /test {
        # ngx_echo没看的echo指令可以将指定的内容输出为响应
        echo "This is a dollar sign: $dollar";
    }
}

注意:

  1. 变量的作用域是全局性的,所有Nginx配置共享之
  2. 每个HTTP请求都具有任何变量的独立副本,类似于线程本地变量
  3. 变量的创建发生在Nginx配置文件加载阶段,赋值则发生在实际请求处理阶段
  4. 即使发生内部跳转(即跳转到不同location处理),变量仍然是同一副本

多个Nginx模块提供了预定义变量。其中很多预定义变量都是只读的,尝试修改会导致意外的后果。

存取处理程序

在读写变量时,Nginx会执行一小段代码,分别称为读处理程序(get handler)、写处理程序(set handler)。不同的模块可能为它们的变量准备不同的处理程序,从而影响变量读写的行为。

ngx_map模块提供的map指令,可以设置用户定义变量之间的映射关系,并且自动缓存结果:

Shell
1
2
3
4
5
6
7
# 相当于为$foo变量注册取处理程序
# 该变量的默认值是0,如果$args=debug则该变量的值为1
# 仅仅在需要取$foo的值时,下面的指令才被计算,且一旦计算结果就被缓存,请求处理期间不会重新计算
map $args $foo {
    default     0;
    debug       1;
}
变量和父子请求

Nginx变量的生命周期是和请求绑定的。在Nginx中存在两种请求:

  1. 主请求:由HTTP客户端发给Nginx的请求
  2. 子请求:Nginx在处理主请求时,内部发起的请求。这些请求类似于HTTP请求但是不牵涉任何网络通信。任何主请求/子请求都可以串行、并行发起多个子请求,甚至递归的向自己发起子请求

子请求的示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http {
    server {
        listen 80;
        location /var {
            # 发起两个子请求
            echo_location /hello;
            echo_location /world "p1=v1&p2=v2";
        }
        location /hello {
            echo Greetings;
        }
        location /world {
            echo World;
        }
    }
}

关于父子请求中的变量,需要注意:

  1. 当前请求(不管是主、子请求)都具有变量的独立副本,通常不会相互干扰
  2. 大部分预定义变量都具有针对当前请求的副本。一些例外情况包括:$request_method总是返回主请求的HTTP方法

注意子请求和内部跳转(rewrite指令可以引发)不同,后者不会产生新的变量副本。

ngx_echo模块的echo_location可以产生子请求,ngx_auth_request模块也可以产生子请求。ngx_auth_request比较特殊的地方是共享父请求的变量。

指令执行阶段

Nginx配置文件虽然类似于编程语言,但是它在整体上是声明性的,而非过程性的。

处理每一个用户请求时,Nginx都是按照若干个不同阶段(phase)依次处理的,一般的配置指令仅仅会注册并运行在某一个阶段。这些阶段一共有11个。

即使在同一个阶段内,也不能对不同指令的执行顺序进行假设(通常先加载的模块,其指令先执行)。例如more_set_input_headers、rewrite_by_lua都在rewrite阶段的尾部执行,你不能假设其中哪个会先执行。

post-read

于Nginx读取并解析完请求头后执行此阶段。模块ngx_realip的指令 set_real_ip_from、real_ip_header运行在此阶段:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http {
    server {
        listen 80;
        # 如果请求来自于本机,则改写其$remote_addr。支持CIDR
        set_real_ip_from 127.0.0.1;
        # 该指令可以指定多次
        set_real_ip_from 127.0.0.0/24;
        # 将$remote_addr改写为自定义头中的值
        real_ip_header   X-Original-Addr;
        location /test {
            # rewrite阶段位于post-read之后,因此这里读取到的是篡改后的$remote_addr
            set $addr $remote_addr;
            echo "from: $addr";
        }
    }
}
 
# curl -H 'X-Original-Addr: 8.8.8.8' $url
# 输出:
# from: 8.8.8.8
server-rewrite

当ngx_rewrite模块的指令配置在server块中时,则这些指令在此阶段执行。

find-config

不支持Nginx模块在此阶段注册处理程序(指令)。在此阶段,Nginx 核心完成当前请求与 location 配置块之间的配对工作。 

在此阶段后,location中的指令才可能生效。

rewrite

这个阶段的配置指令一般用来对当前请求进行各种修改,例如修改URL或者请求参数。运行在该阶段的指令例如:set、set_by_lua、rewrite_by_lua等。

ngx_set_misc模块、ngx_encrypted_session模块提供的指令、 set_by_lua指令都在此阶段执行,且可以ngx_rewrite模块提供的指令(例如set、rewrite)混合使用,不需要担心执行顺序的问题。

rewrite_by_lua总是在rewrite阶段的最后执行。

post-rewrite

不支持Nginx模块在此阶段注册处理程序(指令)。在此阶段,Nginx 核心完成rewrite阶段所声明的内部跳转操作。

preaccess

标准模块ngx_limit_req、ngx_limit_zone运行在此阶段,前者控制访问的频度,后者控制访问的并发度。

access

这个阶段的配置指令一般用于进行访问控制,例如检查用户访问权限、检查来源IP地址。运行在该阶段的指令例如:allow、deny、ngx_auth_request、access_by_lua等。

标准模块ngx_access提供的allow和deny指令可以控制IP地址的访问权限:

Shell
1
2
3
4
5
6
7
8
location /hello {
    # ngx_access模块的指令按照配置顺序执行,遇到第一条满足条件的allow/deny指令就不再检查后续的allow/deny
    allow 169.200.179.4/24;
    allow 127.0.0.1;
    deny all;
 
    echo "hello world";
}

ngx_lua模块提供的指令access_by_lua,在此阶段的最后执行,可以在allow/deny指令检查之后执行更加复杂的验证逻辑。示例:

Shell
1
2
3
4
5
6
7
8
9
10
location /hello {
    access_by_lua '
        # 使用ngx.var前缀来访问Nginx变量
        if ngx.var.remote_addr == "127.0.0.1" then
            return
        end
 
        ngx.exit(403)
    ';
}
post-access

配合access阶段,实现ngx_http_core模块的 satisfy 指令的功能。如果在access阶段注册了多个处理程序,则satisfy可以取值为:

  1. all 必须所有处理程序都验证通过
  2. any 只需要一个处理程序验证通过
try-files

这个阶段实现try_files指令的功能。

content

这个阶段的配置指令负责响应内容的生成。运行在该阶段的指令例如:echo、content_by_lua、proxy_pass等。来自不同模块的content阶段指令通常不能声明在同一个location中。

echo指令支持调用多次,而content_by_lua则仅仅支持调用一次,这些细节由具体模块规定。

ngx_echo模块提供的指令echo_before_body、echo_after_body可以和其它运行在content阶段的指令协同工作,因为它工作在Nginx的输出过滤器(output filter,不属于11个请求处理阶段的特殊阶段)中。

如果一个location中没有任何content阶段指令,则Nginx把请求映射到静态资源服务模块。通常Nginx会配置三个静态资源服务模块,按照在content阶段的执行顺序,依次是ngx_index、ngx_autoindex、ngx_static。其中ngx_index、ngx_autoindex仅仅会处理以 / 结尾的URL,而ngx_static则相反,处理非/结尾的URL。

ngx_index主要用于在文件系统中自动查找首页文件,例如:

Shell
1
2
3
4
5
location / {
    # 自动在此目录下,依次寻找index.htm、index.html文件。如果找不到,由下一个content阶段指令处理
    root /var/www/;
    index index.htm index.html;
}
基础配置块
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 在前台运行
daemon off;
# 运行Nginx的用户
user  nobody;
# 工作进程数量
worker_processes  1;
 
# 错误日志的位置(相对于Prefix)
# error_log file [level];
# 日志级别:debug, info, notice, warn, error, crit, alert, emerg
# 其中,要使用debug,则必须以--with-debug构建Nginx
error_log  logs/error.log;
error_log  logs/error.log  notice;
# 将日志输出到标准错误
error_log stderr debug;
 
# PID文件位置
pid        logs/nginx.pid;
 
 
events {
    # 每个工作进程的最大客户端连接数
    # HTTP服务最大连接数 worker_processes * worker_connections
    # 反向代理最大连接数 worker_processes * worker_connections / 4
    worker_connections  1024;
    # 在接收到新连接通知后,让工作进程尽可能接受多的连接请求
    multi_accept on;
    # 明确指定连接处理方法
    # epoll为Linux 2.6+的高效方式;kqueue为FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, macOS的高效方式
    use epoll;
} 
第七层配置

作为HTTP服务器使用时,Nginx具有以下主要特性:

  1. 支持静态文件、自动索引、文件描述符缓存
  2. 支持缓存的反向代理、负载均衡、容错
  3. 支持缓存FastCGI, uwsgi, SCGI
  4. 模块化架构。支持gzipping、byte ranges、chunked responses、XSLT、SSI(Server Side Includes,服务器端包含)、图像转换等过滤器。单个页面中的多个SSI可以被并行的包含进来
  5. SSL和TLS支持
  6. 支持HTTP/2

其它特性包括:

  1. 基于名称或者IP的虚拟服务器
  2. Keep-alive和管道化连接(pipelined connections)支持
  3. 日志:定制格式、缓冲写入、快速轮换、syslog支持
  4. 3xx-5xx错误码重定向
  5. URL重写,支持基于正则式的重写
  6. 根据客户端地址执行不同函数
  7. 基于客户端地址、密码、子请求结果的身份验证
  8. 验证HTTP referer
  9. 支持PUT, DELETE, MKCOL, COPY, MOVE等HTTP方法
  10. FLV和MP4流媒体支持
  11. 响应限速
  12. 限制同一IP地址的并发连接数、请求数
  13. 基于IP地址获取地理位置信息
  14. 支持A/B测试
  15. 请求镜像
  16. 内嵌Perl
  17. nginScript,一个JavaScript子集语言
Web服务器配置
HTTP配置
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
http {
    # 在此处包含其它配置文件,mime.types包含了MIME类型和扩展名之间的映射关系
    include       mime.types;
    # 如果不匹配任何MIME类型,使用下面的默认值
    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 path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
    # access_log off;
    # 指定访问日志位置和格式
    access_log  logs/access.log  main;
    # 访问日志可以输出到标准输出
    access_log /dev/stdout;
 
 
    # 这个将为打开文件指定缓存,默认是没有启用的,max 指定缓存数量,
    # 建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存。
    open_file_cache max=204800 inactive=20s;
    # 在上一条配置的inactive时间段内,文件被使用的最少次数,不小于该次数,则认为是active
    open_file_cache_min_uses 1;
    # 多长时间检查一次缓存的有效信息
    open_file_cache_valid 30s;
 
 
    # 是否启用基于内核态完成的文件描述符之间的数据拷贝
    sendfile        on;
 
 
    # 是否启用SOCKET选项 TCP_CORK
    tcp_nopush     on;
    # 启用TCP_NODELAY,即禁用Nagle算法,适用于低延迟需求、小数据量场景
    tcp_nodelay on;
    # 客户端连接的保活时间
    keepalive_timeout  65;
 
    # 启用GZIP压缩,默认关闭,开启可以节约流量,但是消耗CPU
    gzip  on;
    # 插入Vary: Accept-Encoding响应头
    gzip_vary on;
    # 根据指定正则式匹配的UA,禁用压缩
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    # 启用GZIP的MIME类型
    gzip_types text/plain application/x-javascript text/css application/xml;
 
    # 启用服务器端包含
    ssi on;
    # 抑制SSI处理出错时输出的[an error occurred while processing the directive]信息
    ssi_silent_errors on;
    # 在text/html的基础上,附加的需要处理SSI命令的MIME类型
    ssi_types text/shtml;
 
    server {
        # 服务器名称 + 监听端口唯一的确定一个服务器
        # listen 127.0.0.1:8000;
        # listen 127.0.0.1;
        # listen 8000;
        # listen *:8000;
        # listen localhost:8000;
        listen       80;
        # 根据虚拟服务器(Virtual Server)的定义,Nginx会比对请求Host头和下面的配置项,选择第一个匹配的server
        # 比对按照如下优先级进行
        # 1、全限定的静态server_name
        # 2、前导通配,例如*.gmem.cc形式的server_name
        # 3、后缀通配,例如www.gmem.*形式的server_name配置
        # 4、正则式定义的server_name
        # 如果没有任何server.server_name匹配,则按下面的规则Fallback:
        # 1、寻找标记为默认服务器的server
        # 2、寻找第一个listen和请求端口匹配的server
        server_name  localhost;
 
        # 用于设置响应头Content-Type
        charset utf-8;
 
        # 为本服务器指定访问日志
        access_log  logs/host.access.log  main;
 
        # URL到文件系统的映射
        location / {
            # / 映射到html目录
            # 指定请求的根目录
            root   html;
            # Index文件列表
            index  index.html index.htm;
        }
 
        # 定义错误页面
        error_page  404              /404.html;
        error_page   500 502 503 504  /50x.html;
 
        # try_files file ... uri; 或者 try_files file ... =code;
        # 逐个判断文件是否存在,使用第一个找到的文件来处理请求
        # file的完整路径依赖于root和alias
        # 如果要检查目录的存在性,需要用 / 结尾,例如$uri/
        # 如果所有文件都不存在,则向uri发起一个内部跳转
        location /images/ {
            # 如果请求的URI不存在,则使用默认图片代替
            try_files $uri /images/default.gif;
        }
        # Wordpress伪静态配置
        try_files $uri $uri/ /index.php?$args;
 
        # URL可以精确到文件
        location = /50x.html {
            root   html;
        }
 
        location /i/ {
            # 请求/i/top.gif则返回/data/w3/images/top.gif这个文件
            # alias指令为指定的location定义一个代替的位置
            # alias必须以 / 结尾,且只能用于location块内部
            alias /data/w3/images/;
        }
    }
}
HTTPS配置
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
http {
    server {
        listen       443 ssl;
        server_name  localhost;
        # 数字证书
        ssl_certificate      cert.pem;
        # 私钥
        ssl_certificate_key  cert.key;
 
        # SSL会话缓存时间和超时时间
        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;
        }
    }
 
}
预定义变量

模块ngx_http_core定义了以下变量:

变量 说明
$uri

经过解码,不包含请求参数的URL

当你请求http://localhost:8080/media/时$uri为/media/

$request_uri 原始URL,未经解码
$args 请求的URL参数部分,支持写: set $args "a=3&b=4";
$arg_XXX 名字为XXX(大小写不敏感)的未经解码的URL参数
$cookie_XXX 名字为XXX的Cookie
$http_XXX 名字为XXX的请求头
$sent_http_XXX 名字为XXX的响应头
$request_method 请求使用的HTTP方法
$remote_addr 请求客户端的IP地址
location

到底哪个location匹配请求,优先级如下:

  1. 检查具有 = 前缀的location,如果找到匹配,停止搜索
  2. 检查具有 ^~ 前缀的location,如果找到匹配,停止搜索
  3. 按照声明顺序检查 ~ ~*前缀的location,如果多个匹配,选取正则式最长的那个
  4. 常规匹配,也就是URL前缀匹配

各种Pattern的说明如下:

模式 说明
location = /uri = 表示精确匹配,只有完全匹配上才能生效
location ^~ /uri ^~ 开头对URL路径进行前缀匹配,并且优先于正则式匹配
location ~ pattern 表示区分大小写的正则匹配
location ~* pattern 表示不区分大小写的正则匹配
location /uri 不带任何修饰符,也表示前缀匹配,但是优先级比正则式低
location / 默认匹配,任何未匹配到其它location的请求都会匹配到这里
access_log

日志格式中可用的变量如下表:

变量 说明
$remote_addr $http_x_forwarded_for 记录客户端IP地址
$remote_user 记录客户端用户名称
$request 记录请求的URI和HTTP协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小
$bytes_sent 发送给客户端的总字节数
$connection 连接的序列号
$connection_requests 当前通过一个连接获得的请求数量
$msec 日志写入时间。单位为秒,精度是毫秒
$pipe 如果请求是通过HTTP流水线发送
$http_referer 记录从哪个页面链接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)
$request_time 请求处理时间,单位为秒,精度毫秒
$time_iso8601 ISO8601标准格式下的本地时间
$time_local 记录访问时间与时区
反向代理

反向代理功能主要由模块ngx_http_proxy_module提供。反向代理的很多指令以在http块中直接声明。

简单代理
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
http {
    server {
 
        # 反向代理,默认所有请求转发给 8080端口处理
        location / {
            proxy_pass http://localhost:8080;
        }
 
        # 反向代理,将PHP请求转发给8000端口
        location ~ \.php {
            proxy_pass http://127.0.0.1:8000;
        }
 
        # gif/jpg/png文件不走反向代理
        # =  前缀表示精确匹配
        # ~  前缀表示大小写敏感的正则式匹配
        # ~* 前缀表示大小写不敏感的正则式匹配
        location ~ \.(gif|jpg|png)$ {
            root /data/images;
        }
 
        # 联用Rewrite
        # http://localhost/v2/xxx 重写为 http://localhost/v2/google_containers/xxx
        # 然后转发给 https://gcr.azk8s.cn/v2/google_containers/xxx
        location /v2/ {
            rewrite /v2/(.*) /v2/google_containers/$1 break;
            proxy_pass https://gcr.azk8s.cn;
            proxy_set_header Host gcr.azk8s.cn;
        }
    }
 
}
非Web服务器

要将请求转发给非HTTP的被代理服务器,选择适当的***_pass指令:

指令 说明
fastcgi_pass 将请求转发给FastCGI服务器
uwsgi_pass 将请求转发给uwsgi服务器
scgi_pass 将请求转发给SCGI服务器
memcached_pass 将请求转发给Memcached服务器
传递请求头

默认情况下Nginx会修改两个请求头:Host、Connection并清除值为空的请求头,然后再转发给被代理服务器。

Host默认被设置为$proxy_host变量,Connection默认被设置为Close。你可以改变这些默认行为:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
location /some/path/ {
    # 改写、添加转发给被代理服务器的请求头
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    # X-Forwarded-Fo不是标准请求头
    # 格式:X-Forwarded-For: client1, proxy1, proxy2,第一个是真实客户端IP,后续的是经过的代理
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 
    # 要阻止某个请求头被转发,将其设置为空
    proxy_set_header Accept-Encoding "";
 
    proxy_pass http://localhost:8000;
}
传递响应头

默认情况下Nginx不会把Date、Server、X-Pad、X-Accel-*响应头转发给客户端,使用proxy_hide_header指令可以指定额外的需要隐藏的响应头。示例:

Shell
1
proxy_hide_header X-Powered-By;
缓冲配置

默认情况下,Nginx接收被代理服务器的响应并放入内部缓冲,直到整个响应都接收到了才发送给客户端。这可以避免同步传输时因为客户端网速太慢,而拖累被代理服务器。

Shell
1
2
3
4
5
6
7
location / {
    # 为单个请求分配的缓冲的大小和数量
    proxy_buffers 16 4k;
    # 被代理服务器返回的第一部分被存放在下面的缓冲中
    proxy_buffer_size 2k;
    proxy_pass http://localhost:8000;
}

如果禁用缓冲,则响应被同步的发送给客户端。在某些交互式应用场景下可能需要禁用缓冲以获得最快响应时间:

Shell
1
2
3
4
location / {
    proxy_buffering off;
    proxy_pass http://localhost:8000;
}
选择出口IP

如果Nginx有多重网络可以到达被代理服务器,你可能需要明确指定使用哪个网络接口:

Shell
1
2
3
4
5
6
7
8
9
10
11
location /app1/ {
    # 通过127.0.0.1向被代理服务器转发请求
    proxy_bind 127.0.0.1;
    proxy_pass http://gmem.cc/app1/;
}
 
location /app2/ {
    # 变量$server_addr为接收原始请求的网络接口的IP地址
    proxy_bind $server_addr;
    proxy_pass http://gmem.cc/app2/;
} 
常用指令
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 代理的HTTP协议版本,默认1.0,如果需要使用Keepalive则设置为1.1
proxy_http_version 1.1;
# 连接到被代理服务器的超时时间
proxy_connect_timeout 50;
# 从被代理服务器读取响应的超时时间,仅仅用于两次连续的读操作,而不是整个响应内容的传输所消耗的时间
proxy_read_timeout 20;
# 向被代理服务器发送请求的超时时间,仅仅用于两次连续的写操作,如果在此时间内被代理服务器每有接收任何数据,则关闭连接
proxy_send_timeout 20;
# 是否缓冲来自被代理服务器的响应
proxy_buffering on;
# 响应的一部分缓冲缓冲的大小,
proxy_buffer_size 32k;
# 缓冲区数量和大小
proxy_buffers 4 32k;
# busy_buffers是缓冲的一部分,通常设置为单个缓冲区的2倍大小
# 这部分缓冲用于存放向客户端返回的响应数据,满了则写入到临时文件
proxy_busy_buffers_size 64k;
# 临时文件(硬盘缓冲)的大小,默认是内存缓冲总大小的2倍,设置为0则关闭硬盘缓冲
proxy_temp_file_write_size 256k;
# 用于修改被代理服务器的响应的location头
proxy_redirect ~^http://10.0.0.1:8080(.*) http://zircon.gmem.cc$1;
proxy_redirect off;
# 设置proxy_hide_header、proxy_set_header指令使用的哈希表的大小
proxy_headers_hash_max_size 51200;
proxy_headers_hash_bucket_size 6400;
预定义变量

模块ngx_http_proxy_module定义了以下变量:

变量 说明
$proxy_host 被代理服务器的名字和端口
$proxy_port 被代理服务器的端口
$proxy_add_x_forwarded_for 客户端的请求头X-Forwarded-For后缀以$remote_addr,如果请求头没有X-Forwarded-For字段则该变量等于$remote_addr
静态缓存

模块ngx_http_proxy_module提供了基础的缓存功能:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# /path/to/cache/ 用于缓存的本地磁盘目录
# levels=1:2 设置一个两级层次结构的目录,大量的文件放置在单个目录中会导致访问缓慢,推荐使用两级目录
# keys_zone 设置一个共享内存区,该内存区用于存储缓存键和元数据。可以让Nginx不需要读取磁盘即确定缓存HIT or MISS
#     10MB 为共享内存区大小,平均1MB可以存放8000键
# max_size=10g 缓存占用磁盘的最大空间,如果不指定则无限增长
# inactive=60m 文件多长时间不被访问,则从内存移除
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m
# 默认情况下,Nginx先把缓存写到一个临时区域,然后再拷贝到缓存目录
# 建议关闭临时区域,避免不必要的拷贝
use_temp_path=off;
 
server {
    location / {
        # 此命令启动匹配此location的URL的缓存,其键存放到my_cache
        proxy_cache my_cache;
        # 被代理的上游服务器
        proxy_pass http://my_upstream;
        # 无法从原始服务器获取最新的内容时,Nginx 可以分发缓存中的过期(Stale)数据给客户端
        # 当上游服务器返回错误、超时、50X状态码时,即使缓存陈旧了,仍然使用
        # 所谓陈旧,依据上游服务器响应中设置的过期时间
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
    }
 
}

你可以把缓存文件分发到多个磁盘上:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
proxy_cache_path /path/to/hdd1 levels=1:2 keys_zone=my_cache_hdd1:10m max_size=10g;
proxy_cache_path /path/to/hdd2 levels=1:2 keys_zone=my_cache_hdd2:10m max_size=10g;
split_clients $request_uri $my_cache {
    # 两个缓存区域分别负担一半缓存文件
    50% "my_cache_hdd1";
    50% "my_cache_hdd2";
}
 
server {
    location / {
        proxy_cache $my_cache;
    }
}
正向代理

Nginx本身不支持正向代理,可以使用第三方模块:ngx_http_proxy_connect_module。

下载模块源码后,需要对Nginx本身源码进行patch:

Shell
1
2
3
4
# 注意选择和Nginx版本匹配的patch
patch -p1 <  /root/CPP/tools/ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_1018.patch
./configure  --add-module=/root/CPP/tools/ngx_http_proxy_connect_module
make

下面是将Nginx作为一个HTTPS代理的配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http {
    server {
        listen 8088 ssl;
        resolver 8.8.8.8 ipv6=off;
        server_name proxy.gmem.cc;
 
        ssl_certificate_key /etc/nginx/certs/proxy.gmem.cc.key;
        ssl_certificate /etc/nginx/certs/proxy.gmem.cc_bundle.pem;
 
        proxy_connect;
        proxy_connect_allow            all;
        proxy_connect_connect_timeout  10s;
        proxy_connect_read_timeout     10s;
        proxy_connect_send_timeout     10s;
    }
}
负载均衡

模块ngx_http_upstream_module为Nginx提供了负载均衡的支持。使用该模块,你可以定义一个服务器组,并在proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass,memcached_pass等指令中引用这些组。

upstream

定义一个服务器组,服务器可以监听不同的端口,甚至可以监听UNIX域套接字。

默认情况下,请求会根据权重,使用round-robin方式在组内各服务器之间分发。如果和一个服务器通信失败,会转而尝试下一个服务器,如果所有服务器都不能获得正确响应,则最后一个服务器的结果被发送给客户端。

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
resolver 10.96.0.10;
 
upstream backend {
    # upstream指令中可以包含多个server指令,其格式为:server address [parameters];
    # 此服务器的权重为5
    server backend1.example.com       weight=5;
    # 此服务器的权重为默认值1,30s内最多失败3次,最多允许100个同时的连接
    server 127.0.0.1:8080       max_fails=3 fail_timeout=30s max_conns=100;
    server unix:/tmp/backend3;
    # 此服务器为备份服务器,仅当主服务器不可用时,请求才发送到备份服务器
    server backup1.example.com:8080   backup;
    # 此服务器被标记为永久不可用
    server down.example.com           down;
    # 访问DNS服务器,监控服务器域名对应的IP地址,自动更新服务器对应的IP地址
    # 必须在http块中添加resolver指令才能生效
    # slow_start,当服务从不健康变为健康、不可用变为可用时,其权重恢复正常值所需要的时间
    # drain,此模式下的服务器,仅仅bound到该服务器的请求才发送给它
    # route,上游服务器的route名,用于会话绑定
    server media-api.dev.svc.k8s.gmem.cc resolve slow_start=10 drain route=string;
 
    # 指定存放动态配置的组的配置信息的文件
    state /var/lib/nginx/state/servers.conf;
 
    # 缓存到上游服务器的TCP连接,每个工作进程缓存32个连接,如果超过此数量则使用LRU算法清除
    # 需要配合proxy_http_version 1.1使用
    keepalive 32;
 
    # 如果处理请求时,不能立刻选取适用的上游服务器,可以让请求排队
    # 如果排队满了,或者超时,则返回502给客户端
    queue number [timeout=time];
 
    # 定义一个名为name,大小为size的共享内存区域。此区域存放此服务器组的配置、运行时状态
    # 所有Worker进程共享此内存区域。多个服务器组可以共享单个区域(name相同),这种情况下size只需要声明一次
    zone name [size];
}
负载均衡算法

ngx_http_upstream_module支持多种负载均衡算法,这些算法对应不同的指令。所有指令均必须位于upstream块中:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 负载均衡算法:客户端和上游服务器的对应关系依赖于key的哈希值
# key可以包含文本、变量,或者两者的组合
# 默认情况下不使用一致性哈希,添加服务器节点后可能导致大量的key重新映射到不同服务器
# 如果指定consistent参数,则采用一致性哈希算法。添加服务器后,仅少量key重新映射,可以保证缓存服务器的命中率
hash key [consistent];
 
# 负载均衡算法:基于客户端的IP地址来映射请求到上游服务器
# 可以保证来自一个客户端的请求,总是由同一服务器处理,除非目标服务器不可用
ip_hash;
 
# 负载即均衡算法:将请求发送给具有最少活动连接数的上游服务器,同时考虑服务器的权重
least_conn;
 
# 负载均衡算法:将请求转发给具有最短响应时间+最少活动连接数的服务器,同时考虑服务器的权重
# 格式:least_time header | last_byte [inflight];
# header以收到请求头时间计,last_byte以收到完整响应时间计
least_time;
sticky

该指令用于实现会话绑定(session affinity),必须位于upstream块中。指令格式:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 基于Cookie的绑定,会话绑定到的目标服务器信息,由Nginx自动产生的Cookie传递
# 对于尚未绑定到特定服务器的请求,Nginx会使用负载均衡算法为其选取一个上游服务器,并种植Cookie
sticky cookie name [expires=time] [domain=domain] [httponly] [secure] [path=path];
# 示例:
sticky cookie srv_id expires=1h domain=.example.com path=/;
 
# 基于Route的绑定
# 被代理服务器第一次接收到客户端请求时,会为客户端分配一个route信息,此客户端的所有后续请求均在
# Cookie或者URI中附带route信息。此信息和server的route参数进行比较,从而确定哪个服务器负
# 责处理请求
# 如果没有为server指定route参数,则其route为IP和端口的MD5的HEX形式
sticky route $variable ...;
# 示例:
map $cookie_jsessionid $route_cookie {
    ~.+\.(?P\w+)$ $route;
}
map $request_uri $route_uri {
    ~jsessionid=.+\.(?P\w+)$ $route;
}
upstream backend {
    server backend1.example.com route=a;
    server backend2.example.com route=b;
    # 优先从Cookie JSESSIONID中获取route信息,如果没有则从URI参数jsessionid中获取
    sticky route $route_cookie $route_uri;
}
 
 
# 由Nginx来分析上游服务器的响应,从中学习到服务器初始化的会话(通常以Cookie的形式传递给客户端)
# create,lookup分别用于获取服务器创建的会话ID、客户端传递的会话ID。这两个指令都可以声明多次
# 第一个不为空的变量生效
# 会话和上游服务器的对应关系,被存放在共享内存区域zone中。1MB的共享内存在64bit机器上可以存放8000会话
# 连续timeout没有被访问的会话,自动重共享内存区域中清除,默认timeout=10分钟
# header用于从上游服务器接收到指定的响应头后,创建一个session
sticky learn create=$variable lookup=$variable zone=name:size [timeout=time] [header];
# 示例:
upstream backend {
   server backend1.example.com:8080;
   server backend2.example.com:8081;
 
   sticky learn
          # 上游服务器将会话ID存放在Cookie EXAMPLECOOKIE中
          create=$upstream_cookie_examplecookie
          # 读取客户端请求的Cookie EXAMPLECOOKIE来确定其上游服务器
          lookup=$cookie_examplecookie
          # 会话-服务器对应关系存放在名为client_essions的共享内存中
          zone=client_sessions:1m;
}
预定义变量
变量 说明
$upstream_addr

上游服务器的地址端口。如果处理请求过程中牵涉到多个上游服务器,则用逗号分隔,例如

192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock

如果发生跨越多组服务器的内部跳转,则不同组的服务器用分号分隔,例如

192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80

$upstream_bytes_received 从上游服务器接收到的字节数
$upstream_cache_status 存放响应缓存的状态,取值:MISS、BYPASS、EXPIRED、STALE、UPDATING、REVALIDATED、HIT
$upstream_connect_time 与上游服务器创建连接所消耗的时间
$upstream_cookie_name 上游服务器通过Set-Cookie响应头种植的名为name的Cookie的值
$upstream_header_time 上游服务器通过响应头设置的时间
$upstream_response_length 上游服务器的响应长度
$upstream_response_time 接收上游服务器响应所消耗的时间
$upstream_status 上游服务器的响应状态码
完整示例

下面是一个L7负载均衡配合SSL Termination的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http {
    upstream ceph-dashboard {
        server ceph-1.gmem.cc:8443;
        server ceph-2.gmem.cc:8443 backup;
    }
    server {
        listen                443 ssl;
        server_name           ceph.gmem.cc;
        ssl_certificate       /etc/ssl/gmem.cc/fullchain.pem;
        ssl_certificate_key   /etc/ssl/gmem.cc/privkey.pem;
        location / {
            proxy_pass https://ceph-dashboard;
            proxy_ssl_verify off;
        }
    }
} 
URL重写

模块ngx_http_rewrite_module提供了URL重写功能。该模块的指令的执行顺序如下:

  1. 位于server块的指令依次顺序执行
  2. 循环执行:
    1. 根据请求URL找到匹配的location
    2. 顺序执行location块中的指令
    3. 如果URL被重写,则返回到2.1重复执行,但是最多不超过10次
break

停止继续为当前请求处理ngx_http_rewrite_module模块的指令。

rewrite

执行URL重写: rewrite regex replacement [flag];

如果regex匹配请求URL(去除http://host:port剩余的部分),则将URL改写为replacement。rewrite指令按照其声明的顺序依次执行。flag可以是:

flag 说明
last 停止处理当前location中的ngx_http_rewrite_module指令。并且,如果URL被改写了,寻找匹配的新location进行处理
break 停止处理当前location中的ngx_http_rewrite_module指令。行为类似于break指令
redirect

使用302状态码返回一个临时的重定向(24-48小时之内临时发生的网页转移),replacement必须以http://、https://或$scheme开头

permanent 使用301状态码返回永久重定向

如果replacement以http://、https://或$scheme开头,则表示进行重定向。示例:

Shell
1
2
3
4
5
6
7
http {
    # 永久重定向
    server {
        listen       80;
        rewrite / https://blog.gmem.cc permanent;
    }
}
if

在满足条件的情况下,执行花括号内的指令: if (condition) { ... }

应当仅仅在花括号内使用return、rewrite、last等rewrite指令,否则可能导致意外行为,甚至出现段错误SIGSEGV。

其中condition可以是以下之一:

  1. $变量名,如果变量值为0或空串则condition为false
  2. 基于 = 或 != 操作符比较变量和字符串
  3. 基于 ~ 或 *~操作符(以及 !~或 !~*)来匹配变量和正则式,*~表示大小写不敏感。正则式中可以包含捕获,并在随后通过$1..$9引用
  4. 基于  -f 或  !-f 操作符检查文件是否存在
  5. 基于  -x 或  !-x 操作符检查可执行文件是否存在
  6. 基于  -d 或  !-d 操作符检查目录是否存在
  7. 基于  -e 或  !-e 操作符检查目录、文件、符号连接是否存在

示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 如果UA为IE
if ($http_user_agent ~ MSIE) {
    # 则将URL前缀以/msie
    rewrite ^(.*)$ /msie/$1 break;
    # 可以重写为外部URL
    rewrite ^(.*)$ https://nginx.gmem.cc/grafana/avatar.png;
}
 
# 如果Cookie中有id则设置变量
if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}
# 如果HTTP方法为POST则返回405状态码
if ($request_method = POST) {
    return 405;
}
# 如果缓慢则限速
if ($slow) {
    limit_rate 10k;
}
 
# 根据扩展名来设置缓存过期时间
location ~* \.(js|css|jpg|jpeg|gif|png|swf)$ {
    # 如果文件存在,则设置过期时间为1小时
    if (-f $request_filename) {
        expires 1h;
        break;
    }
}
 
# 防盗链
location ~* \.(gif|jpg|swf)$ {
    valid_referers none blocked www.gmem.cc;
    if ($invalid_referer) {
       rewrite ^/ http://$host/logo.png;
    }
}
return

停止处理并返回状态码给客户端,格式:

Shell
1
2
3
return code [text];
return code URL;
return URL;
rewrite_log 

如果设置为on则记录URL重写日志

TLS Termination
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
http {
    client_max_body_size 4g;
    server {
        listen                443 ssl;
        server_name           git.gmem.cc;
        ssl_certificate       /etc/letsencrypt/live/gmem.cc/fullchain.pem;
        ssl_certificate_key   /etc/letsencrypt/live/gmem.cc/privkey.pem;
        location / {
            proxy_pass https://127.0.0.1:2443;
            proxy_ssl_verify off;
        }
    }
 
    server {
        listen                443 ssl;
        server_name           harbor.gmem.cc;
        ssl_certificate       /etc/letsencrypt/live/gmem.cc/fullchain.pem;
        ssl_certificate_key   /etc/letsencrypt/live/gmem.cc/privkey.pem;
        client_max_body_size  0;
        location / {
            proxy_pass https://127.0.0.1:4443;
            proxy_ssl_verify off;
            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_set_header X-Forwarded-Proto $scheme;
            proxy_buffering off;
            proxy_request_buffering off;
        }
    }
    server {
        listen                443 ssl default_server;
        ssl_certificate       /etc/letsencrypt/live/gmem.cc/fullchain.pem;
        ssl_certificate_key   /etc/letsencrypt/live/gmem.cc/privkey.pem;
        return 404;
    }
}
最佳实践
root放到server

而不是放到location中,这样可以让所有location使用相同的root

避免重复的index
Shell
1
2
3
http {
    index index.php index.htm index.html;
}
避免滥用if

要检查文件是否存在,使用try_files。if仅仅用于配合rewrite模块的指令使用。 

避免代理一切
Shell
1
2
3
4
5
6
7
8
9
10
11
server {
    location / {
        # 先尝试本地静态文件,然后才发送给代理
        try_files $uri $uri/ @proxy;
    }
    location @proxy {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/tmp/phpcgi.socket;
    }
}
第四层配置
简介

作为TCP/UDP代理服务器时,Ngin具有以下特性:

  1. 可以作为通用的TCP/UDP代理
  2. 为TCP添加SSL和TLS SNI支持
  3. 负载均衡和容错
  4. 基于客户端地址的访问控制
  5. 根据客户端地址执行不同函数
  6. 限制同一IP地址的并发连接数
  7. 日志:定制格式、缓冲写入、快速轮换、syslog支持
  8. 基于IP地址获取地理位置信息
  9. 支持A/B测试
  10. nginScript

在1.9.0之前的版本,基于TCP的代理和负载均衡需要依赖第三方补丁nginx_tcp_proxy_module。

从1.9.0开始,可以在构建时指定 --with-stream选项,启用内置第四层代理/负载均衡。第四层代理的功能由ngx_stream_core_module模块提供。

工作流程

Nginx处理客户端的TCP/UDP会话,由以下阶段组成。

Post-accept

此阶段,Nginx刚刚接受了客户端连接。ngx_stream_realip_module模块参与到此阶段。

Pre-access

访问预检查。ngx_stream_limit_conn_module 模块参与到此阶段。

Access

访问权限检查。ngx_stream_access_module模块参与到此阶段。

SSL

TLS/SSL termination。ngx_stream_ssl_module模块参与到此阶段。

Preread

预读取,读取客户端数据的初始字节到预读取缓冲中。允许ngx_stream_ssl_preread_module等模块对数据进行分析。

Content

实际处理数据的阶段,调用被代理服务器,或者返回一个值给客户端。

Log

记录客户端会话的处理结果,ngx_stream_log_module模块参与此阶段。

配置项解释
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 定义一个第四层代理
stream {
 
    # TCP_NODELAY
    tcp_nodelay on;
 
    upstream mysqld {
        hash $remote_addr consistent;
        server 192.168.1.42:3306 weight=5 max_fails=1 fail_timeout=10s;
        server 192.168.1.43:3306 weight=5 max_fails=1 fail_timeout=10s;
        # 可以通过UNIX Domain Socket连接上游
        server unix:/var/run/mysql.sock;
    }
 
    server {
        # 该指令可以指定多次
        #
        # listen address:port [ssl] [udp] [proxy_protocol] [backlog=number] [rcvbuf=size]
        #                       [sndbuf=size] [bind] [ipv6only=on|off] [reuseport]
        #                       [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
        #
        # address:port 代理服务器的监听端口,可以指定 *:3306、10.0.0.1:3306等形式
        #
        # ssl 指定此端口上所有的连接必须工作在SSL模式下
        #
        # udp 指定此端口工作在UDP协议下
        #
        # proxy_protocol 指定此端口上的所有连接必须使用PROXY协议
        # 关于此协议,参考http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
        # PROXY 2 从1.13.11开始被支持
        #
        # backlog 最大排队等待被接受的连接数
        #
        # rcvbuf SO_RCVBUF
        # sndbuf SO_SNDBUF
        #
        # bind 提示针对每个给出的address:port,分别执行执行bind()调用
        #
        # ipv6only IPV6_V6ONLY
        # reuseport 基于SO_REUSEPORT选项,为每个Worker创建独立的监听套接字。内核将会分别入站请求
        #           给每个Worker,要求Linux 3.9+
        #
        # so_keepalive TCP保活选项:TCP_KEEPIDLE、TCP_KEEPINTVL、TCP_KEEPCNT
        #
        listen 3306;
 
        # 预读缓冲大小
        preread_buffer_size 16k;
        # 预读超时
        preread_timeout 30s;
 
        # 读取PROXY协议头的超时时间,如果超过此时间仍然没有传输完整的PROXY头则连接关闭
        proxy_protocol_timeout 30s;
 
        # 用来解析Upstream的DNS服务器,DNS的缓存有效期
        resolver 127.0.0.1 [::1]:5353 valid=30s;
        # DNS解析超时
        resolver_timeout 5s;
 
        proxy_connect_timeout 1s;
        proxy_timeout 3s;
        proxy_pass mysqld;
 
        # 来自模块ngx_stream_limit_conn_module的配置
        # limit_conn zone number;
        # 限制每个地址仅仅允许同时1个连接
        limit_conn addr 1;
        limit_conn_log_level error;
    }
 
}
变量列表
变量 说明
$binary_remote_addr 二进制形式的客户端地址
$bytes_received 从客户端接收到的字节数
$bytes_sent 发送到客户端的字节数
$connection 连接串号
$hostname 主机名
$msec 当前时间,毫秒精度
$pid 当前工作进程PID
$protocol 当前使用的协议,TCP或UDP
$proxy_protocol_addr PROXY协议头中的客户端地址
$proxy_protocol_port PROXY协议头中的客户端端口
$remote_addr 客户端地址、端口
$remote_port
$server_addr 接受连接的服务地址、端口
$server_port
$session_time 会话持续时间,毫秒精度
$status

会话状态,取值:

200 会话成功完成
400 客户端数据无法解析,例如PROXY头无法无法识别
403 访问被拒绝
500 内部服务器错误
502 网关失败,例如上游服务器无法访问
503 服务不可用,例如超过连接数限制

$time_iso8601  ISO 8601格式的本地时间
$time_local 本地时间 
配置示例
SSH转发
Shell
1
2
3
4
5
6
7
8
9
10
11
stream {
    upstream ssh {
        hash $remote_addr consistent;
        server 192.168.1.42:22 weight=5;
    }
 
    server {
        listen 2222;
        proxy_pass ssh;
    }
}
MySQL负载均衡
Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
stream {
    upstream mysqld {
        hash $remote_addr consistent;
        server 192.168.1.42:3306 weight=5 max_fails=1 fail_timeout=10s;
        server 192.168.1.43:3306 weight=5 max_fails=1 fail_timeout=10s;
    }
 
    server {
        listen 3306;
        proxy_connect_timeout 1s;
        proxy_timeout 3s;
        proxy_pass mysqld;
    }
} 
SSL后端

需要额外编译配置项: --with-stream_ssl_preread_module。示例:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
daemon off;
worker_processes 4;
pid /var/run/nginx/nginx.pid;
worker_rlimit_nofile 74363;
worker_shutdown_timeout 10s ;
events {
    multi_accept        on;
    worker_connections  16384;
    use                 epoll;
}
 
stream {
    # 连接日志
    log_format log_stream '$remote_addr [$time_local] $protocol [$ssl_preread_server_name] [$ssl_preread_alpn_protocols] [$name] $status $bytes_sent $bytes_received $session_time';      
    access_log  /usr/logs/stream.log log_stream;
    map $ssl_preread_server_name $name {
        blog.gmem.cc apache;
        git.gmem.cc  gitea;
        default apache;
    }
 
    upstream apache {
        server 127.0.0.1:8443;
    }
 
    upstream gitea {
        server ::1:3443;
    }
 
    server {
        listen 0.0.0.0:443;
        proxy_pass $name;
        ssl_preread on;
    }
}
真实客户端IP

L4配置,上游服务器看到的客户端地址是127.0.0.1,要让上游服务器看到真实客户端IP,有几种方法。

代理协议

这种方式,需要后端支持代理协议(作为协议的服务器端)。对于Apache,启用代理协议的方法参考Apache HTTP Server知识集锦。

Nginx的配置如下:

1
2
3
4
5
6
7
8
stream {
    server {
        listen 0.0.0.0:443;
        proxy_protocol on;
        proxy_pass $name;
        ssl_preread on;
    }
}
常见问题
静态转发

在被代理服务中,设置响应头X-Accel-Redirect,可以提示Nginx进行静态转发,例如:

1
X-Accel-Redirect: /media/encryp//10/57/1497089.html

Nginx配置:

Shell
1
2
3
server {
    root  /datastore/;
}

则Nginx自动从 /datastore/media/encryp//10/57/1497089.html获取静态文件。

 

← 基于AngularJS开发Web应用
CLion知识集锦 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • IPVS和Keepalived
  • OpenResty学习笔记
  • HAProxy知识集锦
  • GNU Make学习笔记
  • Ubuntu下安装MySQL

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2