nginx 核心知识100讲笔记(一)

Posted on Posted in nginx

nginx 核心知识100讲笔记

课件地址

主要应用场景

  1. 静态资源服务
    • 本地文件系统
  2. 反向代理服务
    • 缓存
    • 负载均衡
  3. API 服务
    • OpenRestry

32核64G, 数千万并发链接

nginx 组成

  1. 二进制可执行文件
  2. 配置文件 nginx.conf
  3. access.log , 记录每一条 http 请求信息
  4. error.log , 定位问题

版本

nginx-1.14.0 14 双数版本, 为稳定版, 奇数为不稳定版

nginx 编译

nginx-1.14.1
├── CHANGES
├── CHANGES.ru
├── LICENSE
├── README
├── auto  # 提供编译的包, 识别操作系统
├── conf  # 实例文件
├── configure  # 编译脚本
├── contrib  #  提供 vim 语法. cp -r contrib/vim/* ~/.vim/
├── html  # 50x.html, index.html
├── man  # nginx 帮助文件
└── src  # 源代码

编译

./configure --help
  1. 文件目录. 主要使用 --prefix
  2. 参数, with 表示默认不包括, 需要手动添加. without 表示默认包括, 需要手动排除
  3. 特殊参数
./configure  # 会生成中间文件, 路径 objs/, 里面有个 ngx_modules.c, 内容是编辑中包含的模块
make  # 生成中间文件, 和运行的二进制文件, 路径 objs/ . !!!做版本升级的时候, 把二进制文件拷贝到目录, 不需要执行 make install !!!
make install  # 首次安装时使用的命令

nginx 配置语法

  1. 配置文件由指令与指令快构成, http {} 指令快
  2. 每条指令以 ; 结尾, 指令与参数之间以空格分割
  3. 指令快以 {} 将多个指令组织在一起
  4. include 语句允许组合多个配置文件以提升可维护性
  5. # 注释
  6. $ 使用变量
  7. 支持正则

时间单位

  • ms 毫秒
  • s 秒
  • m 分
  • h 小时
  • d 天
  • w 周
  • M 月
  • y 年

空间单位

  • 不加表示 bytes 字节
  • k/K 千字节
  • m/M 兆
  • g/G

http 指令快

  • http
  • server
  • location
  • upstream

nginx 命令行

  • nginx -s reload
  • -? -h
  • -c 配置文件
  • -g 配置指令
  • -p 运行目录
  • -s 发送信号 stop/quit(优雅停止)/reload/reopen(重新开始记录日志文件)
  • -t -T
  • -v -V

热部署, 版本升级

替换 nginx 的二进制执行文件

kill -USR2 12345 # 12345 是 nginx 运行的主进程号

这时会有两个 nginx 进程, 一个老的, 一个新的

kill -WINCH 12345 # 优雅的关闭老的所有进程

这时还是有两个 nginx 进程

旧的会保留在那里, 用作版本回退, 把老的 nginx 恢复回去, 然后kill -USR1 12345来重新启用.

如果没问题, 用 kill -QUIT 把老进程号杀掉

lsof -p 进程号 # 可以看到进程打开的句柄, 也包括监听的端口

日志切割

mv  access.log  access.log.bak
nginx -s reopen

简单服务器

# 命名为 main
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                            '$status $body_bytes_sent "$http_referer" ';
                            '"$http_user_agent" "$http_x_forwarded_for"'
gzip on;
gzip_min_length 1; # 小于1字节就不再压缩了
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css applicatioin/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 要压缩的文件类型

server {
    listen 8080;
    location / {
        alias dlib/; # 相对路径
        autoindex on; # 显示目录
        set $limit_rate 1k; # 限制速度, 每秒传输
    }
    access_log logs/access.log main;
}

具有缓存的反向代理

# 设置缓存目录, 10M 共享内存
proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m user_temp_path=off;
server {
    listen 127.0.0.1:8080;
}
upstream local {
    server 127.0.0.1:8080;
}
server {
    server_name abc.com;
    listen 80;
    location / {
        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_cache my_cache;
        proxy_cache_key $host$uri$is_args$args; # 设定 key
        proxy_cache_valid 200 304 302 1d;
        proxy_pass http://local;
    }
}

goaccess

官网

goaccess access.log -o report.html --log-format=COMBINED

Let's Encrypt

官网

详细参考官方文档

yum install python2-certbot-nginx
certbot --nginx --nginx-server-root=/usr/loca/nginx/conf -d www.abc.com
# 该命令自动增加了
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/www.abc.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.abc.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;  #  表示非对称加密使用的参数

cat /etc/letsencrypt/options-ssl-nginx.conf
ssl_session_cache shared:le_nginx_SSL:1m;  #  ssl 最消耗是握手, 增加 cache 1m, 大约是4000个连接
ssl_session_timeout 1440m;  #  每个握手第一次连接, 如果断开了, 在1440分(一天)时间内是可以复用的

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "这里一堆安全套件名称";

OpenResty

官网

下载最新版

openresty-1.13.6.2
├── COPYRIGHT
├── README-windows.txt
├── README.markdown
├── bundle  # 模块
├── configure
├── patches
└── util

编译

./configure
make && make install

简单示例, 把用户代理显示到浏览器中

location /lua {
    default_type text/html;
    content_by_lua 'ngx.say("User-Agent: ", ngx.req.get_headers()["User-Agent"])';
}

nginx 进程结构

多进程结构

nginx -s reload # 关掉 work 和 cache 进程, 重新启用, 不用进程号,  父进程号一样
kill -SIGHUP 9170 # 父进程号,  同 reload

kill -SIGTERM 16982 # 子进程号, 杀掉子进程, 父进程会自动重新启用一个子进程

nginx 信号

master 进程

监控 worker 进程

  • CHLD # 子进程终止的时候, 会向父进程发送这个信号

管理 worker 进程

接受信号

  • TERM, INT # 立刻停止进程
  • QUIT # 优雅停止
  • HUP # 重载配置文件
  • USR1 # 重新打开日志文件, 做日志文件切割
  • # 上面4个可以通过 nginx 命令行运行, 下面两个只能通过 kill 直接向父进程发送信号
  • USR2 # 这两个热部署
  • WINCH

worker 进程

接受信号, 通常不对 work 进程发送信号

  • TERM, INT
  • QUIT-
  • USR1
  • WINCH

nginx 命令行

  • reload : HUP
  • reopen : USR1
  • stop : TERM
  • quit : QUIT

热升级流程

编译选项, 路径必须一致

  1. 将旧 nginx 文件换成新 nginx 文件(注意备份)
  2. 向 master 进程发送 USR2 信号
  3. master 进程修改 pid 文件名, 加后缀 .oldbin
  4. master 进程用新 nginx 文件启动新 master 进程
  5. 向老 master 进程发送 WINCH 信号, 关闭老 worker
  6. 回滚: 向老 master发送 HUP, 向新 master 发送 quit

共享内存工具 , ngx_slab_stat 模块

官网

下载一个 tengine , 在里面找这个模块

./configure --add-module=../tengine-2.2.2/modules/ngx_slab_stat/

location = /slab_stat {
    slab_stat;
}

#######################

connection_pool_size 512; # 连接内存池, 512 字节
request_pool_size 4k; # 请求内存池, 通常是 connection_pool_size 8倍

nginx 请求

  • 精准匹配
  • 处理 *.liuhonghe.me
  • * 在后的泛域名
  • 按文件的顺序匹配正则表达式域名
  • default . listen 后面指定的 default
server {
    server_name first.liuhonghe.me second.liuhonghe.me;
    server_name_in_redirect on; # 默认是 off,  表示, 当访问 second.liuhonghe.me/redirect, 会转到 first.liuhonghe.me/redirect

    return 302 /redirect;
}
server {
    server_name .liuhonghe.me; # 匹配 liuhonghe.me  *.liuhonghe.me
    server_name _; # 匹配所有
    server_name ""; # 匹配没有传递 Host 头部的请求
}

nginx 处理的 11 个阶段

  1. post read. realip
  2. server_rewrite. rewrite
  3. find_config
  4. rewrite. rewrite
  5. post_rewrite
  6. preaccess. limit_conn, limit_req
  7. access. auth_basic, access, auth_request
  8. post_access
  9. precontent. try_files
  10. content. index, autoindex. concat
  11. log. access_log

realip 模块, 用户真实 ip

修改客户端地址, --with-http_realip_module, 提供realip_remote_addrreal_remote_port两个变量, 指令set_real_ip_from real_ip_header real_ip_recursive

  • X-Forwarded-For 可以多个 IP
  • X-Real-IP 一个 IP

使用: 拿到真实 ip 后, 基于变量使用, 如 binary_remote_addr remote_addr 这样的的变量

───────┬────────────────────
       │ File: realip.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name realip.liuhonghe.me;
   3   │
   4   │         error_log logs/myerror.log debug;
   5   │         set_real_ip_from  192.168.1.107;  #  如果是这个 IP 连过来的, 进行以下操作
   6   │         #real_ip_header X-Real-IP;
   7   │         #real_ip_recursive off;
   8   │         real_ip_recursive on;
   9   │         real_ip_header    X-Forwarded-For;
  10   │
  11   │         location /{
  12   │                 return 200 "Client real ip: $remote_addr\n";
  13   │         }
  14   │ }
───────┴────────────────────

rewrite 模块

  • 444 关闭连接
  • 301 http1.0 永久重定向
  • 302 http1.0 临时重定向, 禁止被缓存
  • 303 http1.1 临时重定向, 允许改变方法, 禁止被缓存
  • 307 http1.1 临时重定向, 不允许改变方法, 禁止被缓存
  • 308 http1.1 永久重定向, 不允许改变方法

语法: rewrite regex replacement [flag];

  • regex 指定的 url 替换成 replacement 这个新的 url, 可以使用正则表达式及变量提取
  • replacementhttp:// 或者 https:// 或者 $schema 开头, 则返回302重定向
  • 替换后的url根据flag指定的方式进行处理
    • last 用 replacement这个 URI 进行新的 location 匹配
    • break 指定当前脚本指令的执行, 等价于独立的 break 指令
    • redirect 返回 302 重定向
    • permanent 返回 301 重定向
───────┬────────────────────
       │ File: rewrite.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name rewrite.liuhonghe.me;
   3   │         rewrite_log on;
   4   │         error_log logs/rewrite_error.log notice;
   5   │
   6   │         root html/;
   7   │         location /first {
   8   │                 rewrite /first(.*) /second$1 last; # 这条会到 /second 中处理
   9   │                 return 200 'first!\n';
  10   │         }
  11   │
  12   │         location /second {
  13   │                 rewrite /second(.*) /third$1 break;
  14   │                 #rewrite /second(.*) /third$1;
  15   │                 return 200 'second!\n';
  16   │         }
  17   │
  18   │         location /third {
  19   │                 return 200 'third!\n';
  20   │         }
  21   │
  22   │
  23   │         location /redirect1 {
  24   │                 rewrite /redirect1(.*) $1 permanent;
  25   │         }
  26   │
  27   │         location /redirect2 {
  28   │                 rewrite /redirect2(.*) $1 redirect;
  29   │         }
  30   │
  31   │         location /redirect3 {
  32   │                 rewrite /redirect3(.*) http://rewrite.taohui.tech$1;
  33   │         }
  34   │
  35   │         location /redirect4 {
  36   │                 rewrite /redirect4(.*) http://rewrite.taohui.tech$1 permanent;
  37   │         }
  38   │
  39   │ }
───────┴────────────────────
rewrite if 指令

条件表达式

  • 检查变量为或者值是否为0, 直接使用
  • 将变量与字符串做匹配, 使用 = 或者 !=
  • 将变量与正则表达式做匹配
    • 大小写敏感,~ 或者 !~
    • 大小写不敏感, ~* 或者 !~*
  • 检查文件是否存在, 使用 -f!-f
  • 检查目录是否存在, 使用 -d!-d
  • 检查文件/目录/软连接是否存在, 使用 -e!-e
  • 检查是否为可执行文件, 使用-x!-x

error_page

  1. error_page 404 /404.html;
  2. error_page 500 502 503 505 /50x.html
  3. error_page 404 =200 /empty.gif;
  4. error_page 404 = /404.php;
  5. location / { error_page 404 = @fallback; } location @fallback {proxy_pass http://backend;}
  6. error_page 403 http://example.com/forbidden.html;
  7. error_page 404 =301 http://example.com/notfound.htm;
───────┬────────────────────
       │ File: return.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name return.liuhonghe.me;
   3   │         listen 8080;
   4   │
   5   │         root html/;
   6   │         error_page 404 /403.html;
   7   │         return 405; # 最先执行
   8   │         location /{
   9   │                 return 404 "find nothing!\n"; # 后 return 执行, 不会执行上面的404
  10   │         }
  11   │ }
───────┴────────────────────

location 匹配规则

仅匹配 URI, 忽略参数

  • 常规
  • = 精准匹配
  • ^~ 匹配上后, 则不再进行正则表达式的匹配
  • merge_slashed on; 合并连续的斜杠/符号
  • @ 用于内部跳转的命名 location
  • ~ 大小写敏感的正则匹配
  • ~* 忽略大小写的正则匹配

匹配规则

= > ^~ > 顺序依次匹配正则表达式 location(如果没有, 就匹配最长的) >最长匹配的 location

下面有 6个规则

  • /Test1 匹配第六条
  • /Test1/ 比第一个长, 所以匹配第三条
  • /Test1/Test2 匹配第二条
  • /Test1/Test2/ 第四条
  • /test1/Test2 第二条
───────┬────────────────────
       │ File: locations.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name location.liuhonghe.me;
   3   │         error_log  logs/error.log  debug;
   4   │         #root html/;
   5   │         default_type text/plain;
   6   │         merge_slashes off;
   7   │
   8   │         location ~ /Test1/$ { # 第一条
   9   │                 return 200 'first regular expressions match!\n';
  10   │         }
  11   │
  12   │         location ~* /Test1/(\w+)$ { # 第二条
  13   │                 return 200 'longest regular expressions match!\n';
  14   │         }
  15   │
  16   │         location ^~ /Test1/ { # 第三条
  17   │                 return 200 'stop regular expressions match!\n';
  18   │         }
  19   │
  20   │         location /Test1/Test2 { # 第四条
  21   │                 return 200 'longest prefix string match!\n';
  22   │         }
  23   │
  24   │         location /Test1 { # 第五条
  25   │                 return 200 'prefix string match!\n';
  26   │         }
  27   │
  28   │
  29   │         location = /Test1 { # 第六条
  30   │                 return 200 'exact match!\n';
  31   │         }
  32   │
  33   │ }
───────┴─────────────────────────

限制每个客户端并发连接数 && 限制每个客户端每秒请求数

───────┬────────────────────
       │ File: limit_conn.conf
───────┼────────────────────
   1   │ limit_conn_zone $binary_remote_addr zone=addr:10m; # 定义一个10m的共享内存, 用二进制IP 地址
   2   │ limit_req_zone $binary_remote_addr zone=one:10m rate=2r/m; # 定义10m 共享内存和速率
   3   │
   4   │ server {
   5   │         server_name limit.liuhonghe.me;
   6   │         root html/;
   7   │         error_log logs/myerror.log info;
   8   │
   9   │         location /{
  10   │                 limit_conn_status 500; # 定义返回的 code
  11   │                 limit_conn_log_level  warn;
  12   │                 limit_rate 50; # 限制向用户返回的速度, 每秒钟 50个字节
  13   │                 limit_conn addr 1; # 限制并发连接数
  14   │                 #limit_req zone=one burst=3 nodelay;
  15   │                 limit_req zone=one;
  16   │         }
  17   │ }
───────┴─────────────────────

access 对 ip 地址做限制

location / {
    deny 192.168.1.100; # 顺序匹配执行
    allow 192.168.1.0/24;
    deny all;
}

用户名密码访问 auth_basic

生成账号密码文件
install httpd-tools htpasswd -c file -b user pass

───────┬────────────────────
       │ File: access.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name access.liuhonghe.me;
   3   │         error_log  logs/error.log  debug;
   4   │         #root html/;
   5   │         default_type text/plain;
   6   │         location /auth_basic {
   7   │                 satisfy any;
   8   │                 auth_basic "test auth_basic"; # 提示串
   9   │                 auth_basic_user_file examples/auth.pass; # 用户名密码文件
  10   │                 deny all;
  11   │         }
  12   │
  13   │         location / {
  14   │                 auth_request /test_auth;  # 生成一个子请求, 访问 /test_auth, 权限由这个自路由同一配置
  15   │         }
  16   │
  17   │         location = /test_auth {
  18   │                 proxy_pass http://127.0.0.1:8090/auth_upstream; # 反向代理
  19   │                 proxy_pass_request_body off;
  20   │                 proxy_set_header Content-Length "";
  21   │                 proxy_set_header X-Original-URI $request_uri;
  22   │         }
  23   │ }
───────┴────────────────────

auth_request

--with-http_auth_request_module 例子在上

satisfy

all | any

  • all 都满足条件
  • any 满足一个

try_files

───────┬────────────────────
       │ File: tryfiles.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name tryfiles.liuhonghe.me;
   3   │         error_log  logs/myerror.log  info;
   4   │         root html/;
   5   │         default_type text/plain;
   6   │
   7   │         location /first {
   8   │                 try_files /system/maintenance.html
   9   │                         $uri $uri/index.html $uri.html
  10   │                         @lasturl; # 依次找这几个文件, 如果没有, 范文 lasturl
  11   │         }
  12   │
  13   │         location @lasturl {
  14   │                 return 200 'lasturl!\n';
  15   │         }
  16   │
  17   │         location /second {
  18   │                 try_files $uri $uri/index.html $uri.html =404; # 都找不到时返回 404
  19   │         }
  20   │
  21   │ }
───────┴────────────────────

mirror 模块

处理请求时, 生成子请求访问其他服务, 对子请求的返回值不做处理

server {
    listen 800;
    error_log logs/error.log debug;

    location / {
        mirror /mirror; # copy 一份流量到 /mirror
        mirror_request_body off; # 测试, 不复制 body
    }

    location = /mirror {
        internal; # 只能内部请求
        proxy_pass http://127.0.0.1:10020$request_uri;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URL $request_uri;
    }
}
───────┬────────────────────
       │ File: mirror.conf
───────┼────────────────────
   1   │ server {
   2   │         listen 10020;
   3   │         location / {
   4   │                 return 200 'mirror response!';
   5   │         }
   6   │ }
───────┴────────────────────

alias & root

  • request_filename 访问文件的完整路径
  • document_root 由URI 和 root/alias 规则生成的文件夹路径
  • realpath_root 将 document_root 中的软连接等换成真实路径
───────┬────────────────────
       │ File: static.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name static.liuhonghe.me;
   3   │         error_log  logs/myerror.log  info;
   4   │
   5   │         location /root { # /html/root
   6   │                 root html;
   7   │         }
   8   │
   9   │         location /alias {
  10   │                 alias html;
  11   │         }
  12   │
  13   │         location ~ /root/(\w+\.txt) {
  14   │                 root html/first/$1;
  15   │         }
  16   │
  17   │         location ~ /alias/(\w+\.txt) {
  18   │                 alias html/first/$1;
  19   │         }
  20   │
  21   │         location  /RealPath/ {
  22   │                 alias html/realpath/;
  23   │                 return 200 '$request_filename:$document_root:$realpath_root\n';
  24   │         }
  25   │
  26   │ }
───────┴─────────────────────

static 模块对 url 不以斜杠结尾却访问目录的做法

nginx 访问一个 url, url 末尾没有加 /, 会返回 301

  • absolute_redirect on;
  • server_name_in_redirect off;
  • port_in_redirect on;
───────┬────────────────────
       │ File: dirredirect.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name return.liuhonghe.me dir.liuhonghe.me;
   3   │         server_name_in_redirect on;
   4   │         listen 8088;
   5   │         port_in_redirect on;
   6   │         #absolute_redirect off;
   7   │
   8   │         root html/;
   9   │ }
───────┴─────────────────────

index 和 autoindex

───────┬────────────────────
       │ File: autoindex.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name autoindex.liuhonghe.me;
   3   │         listen 8080;
   4   │         location / {
   5   │                 alias html/;
   6   │                 autoindex on;
   7   │                 index a.html;
   8   │                 autoindex_exact_size off; 
   9   │                 autoindex_format html; # 格式
  10   │                 autoindex_localtime on;
  11   │         }
  12   │ }
───────┴────────────────────

提升多个小文件性能的 concat

在一次请求, 访问多个小文件

alibaba https://github.com/alibaba/nginx-http-concat

--add-module=../nginx-http-concat/

使用, 在 URI 后加两个问好 ?? https://liuhonghe.me/??file01.js,js/file02.js,css/style.css

  • concat: on | off; 开关
  • concat_delimiter: string 分隔符
  • concat_types: MIME types 类型
  • concat_unique: on | off; 是否只对某一种类型进行合并
  • concat_ignore_file_error on | off; 发现某些文件不存在, 继续返回其他文件
  • concat_max_files: numberp 最多合并多少个文件
───────┬────────────────────
       │ File: concat.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name concat.liuhonghe.me;
   3   │
   4   │         error_log logs/myerror.log debug;
   5   │         concat on;
   6   │         root html;
   7   │
   8   │         location /concat {
   9   │                 concat_max_files 20;
  10   │                 concat_types text/plain;
  11   │                 concat_unique on;
  12   │                 concat_delimiter ':::';
  13   │                 concat_ignore_file_error on;
  14   │         }
  15   │
  16   │ }
───────┴────────────────────

access 日志模块 log

默认 combined 格式

log_format combined '$remote_addr - $remote_user [$time_local]' '"$request" $status $body_bytes_sent' '"$http_referer" "$http_user_agent"';

语法

  • access_log off;
  • access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];

过滤模块

sub

--with-http_sub_filter_module

  • sub_filter string replacement
  • sub_filter_last_modified off | on;
  • sub_filter_once on | off;
  • sub_filter_types mime-type;
───────┬────────────────────
       │ File: sub.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name sub.liuhonghe.me;
   3   │         error_log  logs/myerror.log  info;
   4   │
   5   │         location / {
   6   │                 sub_filter 'Nginx.oRg'  '$host/nginx';  # 忽略大小写的, 替换 html 字符串内容
   7   │                 sub_filter 'nginX.cOm' '$host/nginx';
   8   │                 #sub_filter_once on; # 只替换一个
   9   │                 sub_filter_once off;
  10   │                 #sub_filter_last_modified off;
  11   │                 sub_filter_last_modified on; # 头部的 last_modified
  12   │         }
  13   │ }
───────┴────────────────────

addition

可以在响应前后添加

--with-http_addition_module

  • add_before body
  • add_after_body
  • addition_types mime-type
───────┬────────────────────
       │ File: addition.conf
───────┼────────────────────
   1   │ server {
   2   │         server_name addition.liuhonghe.me;
   3   │         error_log logs/myerror.log info;
   4   │
   5   │         location / {
   6   │                 add_before_body /before_action;
   7   │                 add_after_body  /after_action;
   8   │                 addition_types *;
   9   │         }
  10   │         location /before_action {
  11   │                 return 200 'new content before\n';
  12   │         }
  13   │         location /after_action {
  14   │                 return 200 'new content after\n';
  15   │         }
  16   │
  17   │         location /testhost {
  18   │                 uninitialized_variable_warn on;
  19   │                 set $foo 'testhost';
  20   │                 return 200 '$gzip_ratio\n';
  21   │         }
  22   │
  23   │ }
───────┴─────────────────────

http 框架提供的请求相关变量

  • http 请求相关的变量
  • tcp 连接相关的变量
  • nginx 处理请求过程中产生的变量
  • 发送http 相应相关的变量
  • nginx 系统变量
curl -H 'Content-Length: 0' -H 'Cookie: a=c1' 'localhost:9000?a=123&b=456'
log_format  vartest  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status bytes_sent=$bytes_sent body_bytes_sent=$body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$sent_http_abc"';

server {
    server_name var.liuhonghe.me localhost;
    #error_log logs/myerror.log debug;
    access_log logs/vartest.log vartest;
    listen 9090;
    
    location / {
        set $limit_rate 10k;
        return 200 '
arg_a: $arg_a,arg_b: $arg_b,args: $args
connection: $connection,connection_requests: $connection_requests
cookie_a: $cookie_a
uri: $uri,document_uri: $document_uri, request_uri: $request_uri
request: $request
request_id: $request_id
server: $server_addr,$server_name,$server_port,$server_protocol
tcpinfo:  $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd, $tcpinfo_rcv_space 
host: $host,server_name: $server_name,http_host: $http_host
limit_rate: $limit_rate
hostname: $hostname
content_length: $content_length
status: $status
body_bytes_sent: $body_bytes_sent,bytes_sent: $bytes_sent
time: $request_time,$msec,$time_iso8601,$time_local
';
    }   
}

http 请求相关的变量

  • arg_参数名: URL 中某个具体参数的值
  • query_string: 与 args 变量完全相同
  • args: 全部 url 参数
  • is_args: 如果请求url中有参数则返回 ? 否则返回空
  • content_length: http 请求中标识包体长度的 Content_Length 头部的值
  • content_type: 标识请求包体类型的Content-Type头部的值
  • uri: 请求的uri(不同于url,不包括?后面的参数)
  • document_uri: 与uri完全相同
  • request_uri: 请求的url, 包括uri以及完整的参数
  • scheme: 协议名, 例如http/https
  • request_method: 方法
  • request_length: 所有请求内容的大小, 包括请求行/头部/包体
  • remote_user: 由 HTTP Basic Authentication 协议传入的用户名
  • request_body_file: 临时存放请球包体的文件, 如果包非常小则不会存文件, client_body_in_file_only 强制所有包体存入文件, 且可决定是否删除
  • requets_body: 请求中的包体, 这个变量当且仅当使用反向代理, 且设定用内存暂存包体时才有效
  • request: 原始的url请求, 含有方法和协议版本, 例如 GET /?a=1&b=2 HTTP/1.1
  • host: 1. 先从请求行中获取. 2. 如果含有 Host 头部, 则用其值替换掉请求行中的主机名. 3. 如果前两者都取不到, 则使用匹配上的 server_name
  • http_头部名字: 返回一个具体的请求头部的值. 有例外的情况, http_host/http_user_agent/http_referen/http_via/http_x_forwarded_for/http_cookie

referer 防盗链

server {
    server_name referer.liuhonghe.me;

    error_log logs/myerror.log debug;
    root html;
    location /{
        valid_referers none blocked server_names
                    *.liuhonghe.me www.liuhonghe.com/nginx/
                    ~\.google\.;
        if ($invalid_referer) { # 匹配不到上面的规则, 返回403
                return 403;
        }
        return 200 'valid\n';
    }
}

secure_link 防盗链

--with-http_secure_link_module

server {
    server_name securelink.liuhonghe.me;
    error_log  logs/myerror.log  info;
    default_type text/plain;
    location /{
            secure_link $arg_md5,$arg_expires;
            secure_link_md5 "$secure_link_expires$uri$remote_addr secret"; # 密钥名是secret

            if ($secure_link = "") {
                return 403;
            }

            if ($secure_link = "0") {
                return 410;
            }

        return 200 '$secure_link:$secure_link_expires\n';
    }
    location /p/ {
            secure_link_secret mysecret2;

            if ($secure_link = "") {
                return 403;
            }
            rewrite ^ /secure/$secure_link;
    }
    location /secure/ {
        alias html/;
            internal;
    }
}

变量值以及带过期时间的配置示例

可以对用户,时间戳进行区分

  • 原请求: /test1.txt?md5=md5生成值&expires=时间戳(如1545712316)
  • 生成md5: echo -n '时间戳url客户端IP密钥' | openssl md5 -binary | openssl base 64 | tr +/ -_ | tr -d =
# 生成密钥
echo -n '1545712316/test1.txt10.13.0.36 secret' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
====> 22L9jvsCRsJpror_D-vkSg
curl 'securelink.liuhonghe.me/test1.txt?md5=22L9jvsCRsJpror_D-vkSg&expires=1545712316'

仅对 uri 进行哈希的简单方式

只能对访问的资源 uri 进行区分

  • 原始请求 link
  • 生成的安全请求 /prefix/md5/link
  • 生成 md5 echo -n 'linksecret | openssl md5 -hex'
# 生成密钥
echo -n 'test1.txtsecret2' | openssl md5 -hex
(stdin)= a4eafbe7c9112f9a279295de99b933a7
curl 'securelink.liuhonghe.me/p/a4eafbe7c9112f9a279295de99b933a7/text1.txt'

为复杂的业务生成新的变量: map 模块

优先级

完全匹配(字符串) > 泛域名前缀 > 泛域名后缀 > 正则表达式

map $http_host $name {
    hostnames; # 使用这个指令, 可以对域名使用前后*泛域名匹配

    default       0; # 没有default指令时, 返回空字符串

    ~map\.tao\w+\.org.cn 1;
    *.taohui.org.cn   2;
    map.taohui.tech   3;
    map.taohui.*    4;
}

map $http_user_agent $mobile {
    default       0;
    "~Opera Mini" 1; # 匹配到"~Opera Mini", $mobile 就等于1
}

server {
    listen 10001;
    default_type text/plain;
    location /{
        return 200 '$name:$mobile\n'; # 返回 name 和 mobile 值
    }
}

aplit_client 模块

可以按照变量值, 按照百分比的方式生成新的变量, 主要实现ab测试

split_clients "${http_testcli}" $variant {  # 获取http头 http_testcli
         0.51%          .one; # # 总体不能大于 100%
         20.0%          .two;
         50.5%          .three;
         #40%           .four;
         *              "";
}
server {
        server_name split_clients.liuhonghe.me;
        error_log  logs/error.log  debug;
        default_type text/plain;
        # curl -H 'testcli: qwertyuiop' split_clients.liuhonghe.me
        location /{
                return 200 'ABtestfile$variant\n'; # 返回variant这个变量
        }
}

geo 模块

如果 geo 指令后不输入 $address, 那么默认使用 $remote_addr 变量作为 IP 地址

  • 优先最长匹配
  • 通过IP地址以及子网掩码的方式, 定义IP范围, 当IP地址在范围内时新变量使用其后的参数值
  • default指定了当以上范围都未匹配上时,新变量的默认值
  • 通过proxy指令指定可信地址(参考realip模块), 此时remote_addr的值为X-Forwarded-For头部值中最后一个地址
  • proxy_recursive 允许循环地址搜索
  • include 优化可读性
  • delete 删除指定网络
curl -H 'X-Forwarded-For: 10.1.0.0,127.0.0.1,192.168.1.123' geo.liuhonghe.me
geo $country {
    default        ZZ;
    #include        conf/geo.conf;
    proxy          116.62.160.193;

    127.0.0.0/24   US;
    127.0.0.1/32   RU;
    10.1.0.0/16    RU;
    192.168.1.0/24 UK;
}
server {
    server_name geo.liuhonghe.me;
    location /{
                return 200 '$country\n';
        }
}

geoip 模块

--with-http_geoip_module

下载C开发库https://dev.maxmind.com/geoip/legacy/downloadable/ C (see note)

下载完成后进行 ./configure && make && make install

  • $geoip_country_code 两个字母的国家代码, 比如 CN 或 US
  • $geoip_country_code3 三个字母的国家代码, 比如 USA
  • $geoip_country_name 国家名字, 比如 China
geoip_country         /usr/local/share/GeoIP/GeoIP.dat;
geoip_city            /usr/local/share/GeoIP/GeoLiteCity.dat;
geoip_proxy           116.62.160.193/32; # 把本地公网增加为可信地址
geoip_proxy_recursive on;

server {
    server_name geoip.liuhonghe.me;
    error_log logs/myerror.log info;
    keepalive_requests 2;
    keepalive_timeout 75s 20;
    location /{
        return 200 'country:$geoip_country_code,$geoip_country_code3,$geoip_country_name
country from city:$geoip_city_country_code,$geoip_city_country_code3,$geoip_city_country_name
city:$geoip_area_code,$geoip_city_continent_code,$geoip_dma_code
$geoip_latitude,$geoip_longitude,$geoip_region,$geoip_region_name,$geoip_city,$geoip_postal_code
';
    }
curl -H 'X-Forwarded-For: IP'
» 转载请注明来源:呢喃 » nginx 核心知识100讲笔记(一)

Leave a Reply

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

1 × two =