官方提供了5个类型的模块:核心模块、配置模块、事件模块、http模块、mail模块。
配置模块主要负责解析nginx.conf文件,是其他模块的基础,该类模块中只有一个ngx_conf_module模块;
核心模块主要负责定义除配置模块之外的其他模块,该类模块中有6个核心模块。
ngx_mail_module负责定义mail模块;
ngx_http_module负责定义http模块;
ngx_events_module负责定义事件模块;
ngx_core_module则是nginx启动加载的第一个模块,它主要用来保存全局配置项。
ngx_openssl_module只有当加载了之后,nginx才支持https请求
ngx_errlog_module
事件模块即负责事件的注册、分发处理、销毁等,该类模块中主要有这几个模块:
ngx_event_core_module负责加载其他事件模块,是其他事件模块的基础
ngx_epoll_module,该模块则是我们后面需要重点分析的模块,它负责事件的注册、集成、处理等
其他的模块比如ngx_kqueue_module这些我们后面基本不会涉及,就不解释了,毕竟是另外一种I/O多路复用机制,大致的思想是一样的
http模块和mail模块也无需太多解释,等到后面再介绍。
配置root和alias,测试访问的资源路径。
root:
(1) 前缀匹配
location /download1/ {
root /usr/local/mywork/test/html;
}
或者:
location /download1/ {
root /usr/local/mywork/test/html/;
}
或者:
location /download1 {
root /usr/local/mywork/test/html;
}
或者:
location /download1 {
root /usr/local/mywork/test/html/;
}
(2) 正则匹配
location ~ ... 重复上述四种情况,
以上八种情况:
curl -x 127.0.0.1:80 static.test.com/download1/1.html
==> /usr/local/mywork/test/html/download1/1.html
curl -x 127.0.0.1:80 static.test.com/download1/zhangbao/1.html
==> /usr/local/mywork/test/html/download1/zhangbao/1.html
总结: root不论哪种匹配方式,不论root最后加没加/,访问的都是root_path+uri
alias:
浏览器访问:http://test.com/download2/zhangbao/1.html
(1) 前缀匹配:
location /download2/ {
alias /usr/local/mywork/test/html;
}
location /download2/ {
alias /usr/local/mywork/test/html/;
}
location /download2 {
alias /usr/local/mywork/test/html;
}
location /download2 {
alias /usr/local/mywork/test/html/;
}
===> 以上四种情况:
/usr/local/mywork/test/htmlzhangbao/1.html
/usr/local/mywork/test/html/zhangbao/1.html
/usr/local/mywork/test/html/zhangbao/1.html
/usr/local/mywork/test/html//zhangbao/1.html
(2) 正则匹配:
location ~ /download2/ {
alias /usr/local/mywork/test/html;
}
location ~ /download2/ {
alias /usr/local/mywork/test/html/;
}
location ~ /download2 {
alias /usr/local/mywork/test/html;
}
location ~ /download2 {
alias /usr/local/mywork/test/html/;
}
===> 以上四种情况:
1和3:
403错误:日志显示 directory index of "/usr/local/mywork/test/html" is forbidden,request: "GET /download/zhangba/1.html/ HTTP/1.1"
2和4:
301无限重定向:浏览器地址栏目是:http://test.com/download2/web/1.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/index.html/
解释:
1和3: 当配置的server或者location没有指定首页时,找不到文件,会报403错误。
2和4static模块实现了root/alias功能时,发现访问目标是目录,但URL末尾未加/时,会返回301重定向。
absolute_redirect,port_in_redirect,server_name_in_redirect 可以控制
总结:
看来对于alias来说,用不带修饰符的前缀匹配方式比较合适,而且处理逻辑和proxy_pass的一样,将匹配中的部分去掉后再拼接
而alias不适合用正则匹配,可能有意想不到的错误。
client_body_buffer_size : 请求存在包体时,接收包体时所分配的内存
Syntax: client_body_buffer_size size;
Default: client_body_buffer_size 8k|16k;
Context: http, server, location
如果接收头部时就已经接收到了完整的包体,则这段内存不会被分配
如果剩余未接收的包体的长度小于client_body_buffer_size的大小,则仅分配所需的大小
如果剩余未接收的包体的长度大于client_body_buffer_size的大小,则分配client_body_buffer_size大的空间接收body
当关闭body缓存时,则该内存上的内容立即会被发往上游,一段一段的收,一段一段的发。
当开启body缓存时,该段内存用完时,写入临时文件,并释放这段内存。
client_body_in_single_buffer
Syntax: client_body_in_single_buffer on | off;
Default: client_body_in_single_buffer off;
Context: http, server, location
client_max_body_size: 包体最大长度限制
Syntax: client_max_body_size size;
Default: client_max_body_size 1m;
Context: http, server, location
仅对请求头部中含有Content-Length生效
超出最大长度限制,返回413错误。
client_body_temp_path :临时文件路径
Syntax: client_body_temp_path path [level1 [level2 [level3]]];
Default: client_body_temp_path client_body_temp;
Context: http, server, location
默认值是client_body_temp,即将超出client_body_buffer_size 的body存在这个目录下。
设置多级目录是为了防止一个目录下文件过多,文件的存取速度太慢。
client_body_in_file_only on :决定body是否写入文件,一般为了定位问题而配置的
Syntax: client_body_in_file_only on | clean | off;
Default: client_body_in_file_only off;
Context: http, server, location
on: 用户上传的body会一直保留在文件中,请求处理完成后也会一直保留,文件不会被删除。
clean:只要有body,用户上传的body必须写入文件,但是请求完成后文件就会被删除。
off:如果body较小,用client_body_buffer_size大小的内存就能存下,则此body不会被写入文件中
client_body_timeout time : 两次读取body之间的最大迟延,类似于tcp的keepalive_timeout的超时时间
Syntax: client_body_timeout time;
Default: client_body_timeout 60s;
Context: http, server, location
读取包体超时,则返回408错误。
展示目录结构
server {
server_name autoindex.test.com;
location / {
alias /usr/local/mywork/test/;
autoindex on;
autoindex_exact_size off;
autoindex_format html;
autoindex_localtime on;
}
}
location / {
proxy_pass http://github.com;
proxy_redirect 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;
}
1 proxy_redirect : 修改返回的location的头部
语法:proxy_redirect [ default|off|redirect replacement ]
默认值:proxy_redirect default
使用字段:http, server, location
如果需要修改从被代理服务器传来的应答头中的"Location"和"Refresh"字段,可以用这个指令设置。
假设被代理服务器返回Location字段为: http://localhost:8000/two/some/uri/
这个指令:
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
将Location字段重写为http://frontend/one/some/uri/。
参考连接:https://blog.csdn.net/u010391029/article/details/50395680
2 proxy_set_header :
上下文: http、server、location
默认: proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
注意:
(1)http段,server段,location段同时配置了proxy_set_header时,只有location中会生效,例如:
http{
proxy_set_header xxx1 A
proxy_set_header xxx2 B
location / {
proxy_set_header xxx3 C
}
}
A和B都不会生效,除非去除C!
(2) 若value的值为空(‘ ’),则整个header都不会向上游发送
3 proxy_method
Syntax: proxy_method method;
Default: —
Context: http, server, location
4 proxy_http_version
Syntax: proxy_http_version 1.0 | 1.1;
Default: proxy_http_version 1.0;
Context: http, server, location
5 proxy_pass_request_headers
Syntax: proxy_pass_request_headers on | off;
Default: proxy_pass_request_headers on;
Context: http, server, location
6 proxy_pass_request_body 和 proxy_set_body
生成发往上游的包体
Syntax: proxy_pass_request_body on | off;
Default: proxy_pass_request_body on;
Context: http, server, location
Syntax: proxy_set_body value;
Default: —
Context: http, server, location
7 proxy_request_buffering :控制接收完完整包体再转发或者是边收边发。
Syntax: proxy_request_buffering on | off;
Default: proxy_request_buffering on;
Context: http, server, location
默认情况下是接收完完整包体再转发。
8 proxy_connect_timeout :建立tcp连接的时间
Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http, server, location
如果规定时间内三次握手不成功,未能建立连接,则会返回给客户端502
9 proxy_next_upstream : 发生错误时尝试与下一个上游服务器转发请求。
Syntax: proxy_next_upstream http_502 | ..;
Default: proxy_next_upstream error timeout;
Context: http, server, location
10 proxy_socket_keepalive
Syntax: proxy_socket_keepalive on | off;
Default: proxy_socket_keepalive off;
Context: http, server, location
on时会使用操作系统默认的tcp keepalive相关的配置进行探测,及时关掉不再使用的的tcp连接。
11 proxy_buffering
Syntax: proxy_buffering on | off;
Default: proxy_buffering on;
Context: http, server, location
默认开启,先接收完完整的包体,然后再向客户端返回
12 proxy_buffer_size 接收上游的header响应头部
Syntax: proxy_buffer_size size;
Default: proxy_buffer_size 4k|8k;
Context: http, server, location
proxy_buffer_size 有一点特殊在于,无论 proxy_buffering 是否开启,proxy_buffer_size 都会起作用。指定后端 response 的 buffer 的大小。它是来自后端 response 的一部分,它包含 Headers,从 response 分离出来。它仅用于限定 headers 的 buffer 区,所以它的值比 proxy_buffers 更低。
如果上游的响应头部超过此限制,则会有错误日志:
error.logupstream sent too big header
13 proxy_buffers 定义接收上游的http包体的内存大小
Syntax: proxy_buffers number size;
Default: proxy_buffers 8 4k|8k;
Context: http, server, location
如果这段内存能够存放下包体,则不会再向磁盘中写入。
14 proxy_max_temp_file_size 和 proxy_temp_file_write_size 和proxy_temp_path
Syntax: proxy_max_temp_file_size size; 定义写入body的文件最大size
Default: proxy_max_temp_file_size 1024m;
Context: http, server, location
Syntax: proxy_temp_file_write_size size; 定义每次写入多少
Default: proxy_temp_file_write_size 8k|16k;
Context: http, server, location
Syntax: proxy_temp_path path [level1 [level2 [level3]]]; 定义临时文件的路径和级别
Default: proxy_temp_path proxy_temp;
Context: http, server, location
15 proxy_busy_buffers_size :及时的转发包体
Syntax: proxy_busy_buffers_size size;
Default: proxy_busy_buffers_size 8k|16k;
Context: http, server, location
16 proxy_read_timeout 定义两次读取上游响应的超时时间
Syntax: proxy_read_timeout time
Default: proxy_read_timeout 60s;
Context: http, server, location
17 proxy_limit_rate 限制读取上游的响应的速率
Syntax: proxy_limit_rate rate
Default: proxy_limit_rate 0;
Context: http, server, location
18 proxy_store_access 和proxy_store 上游包体的持久化
Syntax: proxy_store_access users:permissions ...;
Default: proxy_store_access user:rw;
Context: http, server, location
Syntax: proxy_store on | off | string;
Default: proxy_store off;
Context: http, server, location
19 proxy_ignore_headers 禁用上游响应头部的功能
Syntax: proxy_ignore_headers field ...;
Default: —
Context: http, server, location
功能:有些响应头可以改变nginx的行为,该指令能够禁止她们生效
可以禁用的头部:
X-Accel-Redirect : 由上游服务器指定nginx内部重定向,控制请求执行
X-Accel-Limit-rate :由上游服务器控制发往客户端的速率,等同于limit_rate指令
X-Accel-Buffering : 由上游控制是否缓存上游响应
X-Accel-Charset : 由上游控制Content-Type中的Charset
缓存相关:
X-Accel-Expires:设置响应在nginx中的缓存时间,单位为秒;@开头表示一天中的某一时刻
Expires: 控制nginx的缓存时间,优先级低于X-Accel-Expires
Cache-Control:控制nginx缓存时间,优先级低于X-Accel-Expires
Set-Cookies:响应中出现Set-Cookie则不缓存,可通过proxy_ignore_headers禁止生效
Vary:响应中出现Vary:*则不缓存,可通过proxy_ignore_headers禁止生效
20 proxy_hide_header 转发上游的响应
Syntax: proxy_hide_header field;
Default: —
Context: http, server, location
功能:对于上游响应中的某些头部,设置不向客户端转发
默认不转发的头部:
Date: 由ngx_http_header_filter_module过滤模块填写,值为nginx发送响应头部的时间
Server: 由ngx_http_header_filter_module过滤模块填写,值为nginx的版本号
X-Pad :通常是Apache为避免浏览器的bug生成的头部,默认忽略
X-Accel- :用户控制nginx的行为,不需要向客户端转发
22 proxy_pass_header 对于被proxy_hide_header的头部,设置向下游转发
Syntax: proxy_pass_header field;
Default: —
Context: http, server, location
23 proxy_next_upstream
Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 |
http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...;
Default: proxy_next_upstream error timeout;
Context: http, server, location
前提: 没有向客户端发送任何内容
限制proxy_next_upstream的时间与次数
Syntax: proxy_next_upstream_timeout time;
Default: proxy_next_upstream_timeout 0;
Context: http, server, location
Syntax: proxy_next_upstream_tries number;
Default: proxy_next_upstream_tries 0;
Context: http, server, location
24 proxy_intercept_errors 用error_page拦截上游失败的响应
Syntax: proxy_intercept_errors on | off;
Default: proxy_intercept_errors off;
Context: http, server, location
作用: 当上游的响应的状态码大于等于300时,将响应返回客户端还是按error_page指令处理
如果不开启,则当响应为403,404, 500等错误时,会原封不动的返回给客户端
如果开启,则可以通过定义error_page将错误页面进行修饰后返回给客户端: error_page 500 1.html
23 缓存指令proxy_cache
ngxin缓存功能只需要proxy_cache_path和proxy_cache两个指令就可开启。但是大型的网站中,nginx的缓存功能用的不多,而是用更加专业的缓存varnish和memache
开启与关闭:
Syntax: proxy_cache zone | off;
Default:
proxy_cache off;
Context: http, server, location
缓存路径:
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=mycache:10m max-size=10g inactive=60m;
则缓存文件可能形式为:/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
配置范例:
location /{
proxy_cache mycache;
proxy_cache_valid 200 1d; //设置缓存有效时间
proxy_cache_valid 302 1h;
proxy_cache_valid any 10m;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504; //在上述情况下使用它不新鲜的缓存,没有总比陈旧强
proxy_cache_method GET 只对GET方法缓存
proxy_cache_min_uses 10; 至少被访问10,才被缓存
proxy_cache_bypass string; 设置在何种情况下ngxin不从缓存中取数据,一般私有数据不能用公共缓存 $cookie_nocache $arg_nocache $http_authorization
proxy_pass http://10.99.2.15;
proxy_set_header X-Real-Ip $remote_addr
}
浏览器:http://test.proxy.com/test/science/getinfo?a=1&b=2
(1) location ~ /test/(.*) {
proxy_pass http://10.99.1.51;
upstream接收到的请求:/test/science/getinfo?a=1&b=2
(2) location ~ /test/(.*) {
proxy_pass http://10.99.1.51/;
}
upstream test_up{
server 10.99.1.51;
}
location ~ /test {
proxy_pass http://test_up/;
}
nginx启动报错:
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in /usr/local/mywork/gateway/orange/conf/vhosts/test.balancer.com.conf:24
(3) location ~ /test/(.*) {
set $new_url $1$is_args$args;
proxy_pass http://10.99.1.51/$new_url;
}
upstream接收到的请求:/science/getinfo?a=1&b=2
(4) location ~ /test/(.*) {
set $new_url $1$is_args$args;
proxy_pass http://10.99.1.51/$new_url/;
}
upstream接收到的请求:/science/getinfo?a=1&b=2/
(5) location ~ /test/(.*) {
set $new_url $1$is_args$args;
proxy_pass http://10.99.1.51/$request_uri;
}
upstream接收到的请求://test/science/getinfo?a=1&b=2
注意这里有两条/,$request_uri中本身包含了一个
(6) location ~ /test/(.*) {
set $new_url $1$is_args$args;
proxy_pass http://10.99.1.51/$request_uri;
}
upstream接收到的请求:/test/science/getinfo?a=1&b=2
现在改成不带任何修饰符的前缀匹配:
浏览器:http://test.proxy.com/test/science/getinfo?a=1&b=2
(1)
location /test/ {
proxy_pass http://10.99.1.51;
upstream接收到的请求:/test/science/getinfo?a=1&b=2
(2)
location /test/ {
proxy_pass http://10.99.1.51/;
upstream接收到的请求:/science/getinfo?a=1&b=2
(3)
upstream test_up{
server 10.99.1.51;
}
location /test/ {
proxy_pass http://test_up;
upstream接收到的请求:/test/science/getinfo?a=1&b=2
(4)
upstream test_up{
server 10.99.1.51;
}
location /test/ {
proxy_pass http://test_up/;
upstream接收到的请求:/science/getinfo?a=1&b=2
(5)
upstream test_up{
server 10.99.1.51;
}
location /test {
proxy_pass http://test_up/;
upstream接收到的请求://science/getinfo?a=1&b=2
(6)
upstream test_up{
server 10.99.1.51;
}
location /test {
proxy_pass http://test_up;
upstream接收到的请求:/test/science/getinfo?a=1&b=2
(7) 浏览器访问:http://test.proxy.com/zhangbao/test/science/getinfo?a=1&b=2
结果是这些location都匹配不中,因为前缀都匹配不上
(8) 浏览器访问:http://test.proxy.com/zhangbao/test/science/getinfo?a=1&b=2
upstream test_up{
server 10.99.1.51;
}
location ~ /test {
proxy_pass http://test_up;
upstream接收到的请求:/zhangbao/test/science/getinfo?a=1&b=2
这里就不是前缀匹配了,只要uri里有/test字段就可以匹配中
现在将后端upstream name 写成变量的形式:
浏览器:http://test.proxy.com/test/science/getinfo?a=1&b=2
配置如下:
location /test/ {
set $ups zhouzhongtao2;
proxy_pass http://$ups;
或者:
location ~ /test/ {
set $ups zhouzhongtao2;
proxy_pass http://$ups;
upstream接收到的请求: /test/science/getinfo?a=1&b=2
location /test/ {
set $ups zhouzhongtao2;
proxy_pass http://$ups/;
location ~ /test/ {
set $ups zhouzhongtao2;
proxy_pass http://$ups/;
upstream接收到的请求:/
总结:
对于 ~ 匹配:
只要语法不错误,而且匹配中了,直接往后端扔给全部的uri
对于不带修饰符的前缀匹配:
如果写成proxy_pass http://upstream_name
加根:转发给后端时掐掉匹配中的部分
不加根:转发给后端的时候带上匹配中的部分。
对于将upstream写成变量形式的,不管是~匹配黑市不带修饰符的前缀匹配
不加根:全量uri转发到后端
加根: 访问的是根,即uri = /
1 allow/deny
Syntax: allow address | CIDR | unix: | all;
Default: —
Context: http, server, location, limit_except
其中address:某个ip
CIDR:某个ip段
unix:基于socket的限制
all: 以上所有的
在location、server、http不同地方配置访问控制,生效的范围不一样
1 用户请求 ---> Nginx
2 Nginx返回401 Unauthorized,并在header头中加上WWW-Authenication:Basic
3 改错误浏览器不会显示,而是弹出输入框,输入用户名和密码。
4 浏览器将用户密码以明文的方式发送给Nginx,如果是https,则会加密。
auth_basic string|off :定义第二步中的header头
auth_basic_user_file file; 定义存储用户名和密码的文件
生成改file的方法:
1) 安装Apache的http-tools
2) htpasswd -c file -b user pass
调用服务器组的方法:proxy_pass fastcgi_pass uwsgi_pass
轮询(默认)
ip_hash
url_hash
hash:包括普通hash和c hash
least_conn
fair:智能选择,选择响应耗时短的自动分配
每个指令对应一个模块处理
Directives
upstream
server
zone
state
hash
ip_hash
keepalive
ntlm
least_conn :调度方法,指明最少连接
least_time
queue
sticky
sticky_cookie_insert //和sticky指令二选一使用
注意:后面要指定协议(http://不能掉)
配置范例:
upstream upservers {
server 10.99.2.15 weight=5;
server 10.99.2.122 weight=2;
server admin.xesv5.com weight=2;
server backend2.example.com:8080;
server unix:/tmp/backend3;
server backup1.example.com:8080 down;
server backup2.example.com:8080 backup;
}
server {
location / {
proxy_pass http://upservers; //加上http:
}
}
配置upstream遇到的坑:
场景:做分流测试,配置从10.99.2.15上转发流量到10.99.1.49
10.99.2.15配置如下:
upstream zxn {
server 10.99.1.49:80;
}
server{
listen 80;
server_name test.divide.com;
root /usr/local/src/klq;
location ~ /test/ {
proxy_set_header Host $host;
proxy_pass http://zxn;
}
location ~ \.php$ {
proxy_set_header Host $host;
proxy_pass http://zxn;
}
}
10.99.1.49配置如下:
server{
listen 80;
server_name test.divide.com;
access_log logs/test.divide.com.log main;
error_log logs/test.divide.com.error.log error;
location /test1.html {
root /usr/local/src/klq/;
}
location ~ \.php$ {
root /usr/local/src/zhangbao;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
浏览器访问:test.divide.com/test/testdivide.php
echo ($__SERVER);
array(35) {
["HTTP_X_FORWARDED_FOR"]=>string(13) "123.125.71.45"
["HTTP_HOST"]=>string(15) "test.divide.com"
["SERVER_NAME"]=>string(15) "test.divide.com"
["SERVER_ADDR"]=>string(10) "10.99.1.49"
["REMOTE_ADDR"]=>string(10) "10.99.2.15"
["DOCUMENT_ROOT"]=>string(23) "/usr/local/src/zhangbao"
["DOCUMENT_URI"]=>string(20) "/test/testdivide.php"
["REQUEST_URI"]=>string(20) "/test/testdivide.php"
["SCRIPT_NAME"]=>string(20) "/test/testdivide.php"
["SCRIPT_FILENAME"]=>string(43) "/usr/local/src/zhangbao/test/testdivide.php"
["PHP_SELF"]=>string(20) "/test/testdivide.php"
}
结论: 1 在代理的nginx服务器中指定proxy_pass时,需要proxy_set_header $Host host;否则就算请求打到了后端upstream server, 也匹配不中对应的server
2 nginx在转发的时候,自动加上了 HTTP_X_FORWARDED_FOR字段,用于表示客户端ip
3 通过REMOTE_ADDR拿到的只是前一级代理的ip,并不能拿到真实的ip地址
1 配置该指令后,网关在转发的时候如果发现后端A机器返回错误,则重新内部发起一次请求,将请求打到B机器,因此看到的日志是: 网关1条,后端2条,浏览器一条,对于用户来说是无感知的。
但是网关的1条中,upstream_status会显示: upstream_status:500,200
upstream iphashtest {
ip_hash;
#hash user_$arg_username [consistent];
server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
server 127.0.0.1:8012 weight=1;
}
其中配置了ip_hash或者是 hash,则后面的weight指令失效.
说明:
ip_hash:
ip_hash的弊端就是在SNAT模式的大量Client,这些client都是统一个ip地址,最终都会被分配到同一个server。
sticky:
解决了ip_hash的弊端,实现一种基于cookie、route或者learn的session绑定,比基于ip的session更加精细。(具体如何配置参看官方文档)
keepalive: 指定与后端服务器使用持久连接模式,并持久多长时间。
通常,当upstream server为非http服务器时,例如memcache缓存服务器,可以开启该功能。让指令工作一段时间还保持连接的状态。但当后面是httpd server时,并不建议开启,会影响并发性能。
health_check:指明对某个upstream进行健康检查,定期对上游服务器发送检测http请求。在使用healt_check时,建议关闭该location的log功能,避免不必要的日志。
interval = time 指明发送探测间隔
failes = number 指明失败数
pass = number 指明通过数
uri = uri 指明请求的资源,默认为主页面==>'/'
match = name 指明服务器返回的状态信息,默认为2XX或3XX,也可以自定义match
eg:
http {
server {
...
location / {
proxy_pass http://backend;
health_check match=welcome;
}
}
match welcome {
status 200;
header Content-Type = text/html;
body ~ "Welcome to nginx!";
}
}
建议:关闭访问日志。 ngxin会阶段性的向upstream server发送访问探测,这些访问日志没有必要。
基本语法:
add_header
add_trailer
expires
自定义返回给用户的response
Syntax: add_header name value [always];
Default: —
Context: http, server, location, if in location
配置范例:
eg1:
server {
listen 80;
server_name localhost;
add_header X-via $server_addr; 在response中显示处理该请求的服务器ip
add_header X-Cache $upstream_cache_status;//显示是否缓存命中了,MIIS或者HIT
location / {
proxy_pass http://upservers; //加上http:
}
}
eg2:
add_header Cache-Control private;
add_header X-First 1;
注意: 和proxy_set_header的区别,proxy_set_header是ngxin做反向代理转发请求的时候设置请求头的,而add_header是设置响应头的。
使用的是fast-cgi协议,用作处理/.php动态文件
proxy_pass:上游服务器是httpd,使用的协议是http协议
fascgi_pass:上游服务器不是httpd,使用的协议是fastcgi协议
指令集:
Directives
fastcgi_bind
fastcgi_buffer_size
fastcgi_buffering
fastcgi_buffers
fastcgi_busy_buffers_size
fastcgi_cache
fastcgi_cache_background_update
fastcgi_cache_bypass
fastcgi_cache_key
fastcgi_cache_lock
fastcgi_cache_lock_age
fastcgi_cache_lock_timeout
fastcgi_cache_max_range_offset
fastcgi_cache_methods
fastcgi_cache_min_uses
fastcgi_cache_path
fastcgi_cache_purge
fastcgi_cache_revalidate
fastcgi_cache_use_stale
fastcgi_cache_valid
fastcgi_catch_stderr
fastcgi_connect_timeout
fastcgi_force_ranges
fastcgi_hide_header
fastcgi_ignore_client_abort
fastcgi_ignore_headers
fastcgi_index :指定主页面
fastcgi_intercept_errors
fastcgi_keep_conn
fastcgi_limit_rate
fastcgi_max_temp_file_size
fastcgi_next_upstream
fastcgi_next_upstream_timeout
fastcgi_next_upstream_tries
fastcgi_no_cache
fastcgi_param
fastcgi_pass
fastcgi_pass_header
fastcgi_pass_request_body
fastcgi_pass_request_headers
fastcgi_read_timeout
fastcgi_request_buffering
fastcgi_send_lowat
fastcgi_send_timeout
fastcgi_split_path_info
fastcgi_store :缓存到磁盘上
fastcgi_store_access
fastcgi_temp_file_write_size
fastcgi_temp_path
使用范例
http
{
...
fastcgi_cache_path /data/nginx/cache keys_zone=zhangbao_cache_zone:10m;
map $request_method $purge_method {
PURGE 1;
default 0;
}
server {
...
location ~ \.php$ {
fastcgi_pass backend;
fastcgi_cache zhangbao_cache_zone;
fastcgi_cache mycache;
fastcgi_cache 200 1d;
fastcgi_cache 302 1h;
fastcgi_cache any 10m;
fastcgi_cache_use_stale error timeout http_500 http_502 http_503 http_504;
fastcgi_cache_key $uri;
fastcgi_cache_purge $purge_method;
}
}
}
指令
break
if
return
rewrite
rewrite_log
set
用途:域名规划,向前兼容,可以将老的url全部重定向到新的url。避免了用户流量的损失,同时降低了修改的成本。
语法:rewrite regex replacement flag
结束位:跳转完需要写上结束位
last -当前匹配结束之后,终止匹配,服务端会建立一个新的请求,新请求的url则是rewrite后的,那么将重新去找能匹配的location,注意虽然是建立了一个新的请求,但是新请求并不是浏览器发出的,而是nginx内部建立的,查看日志发现nginx只收到了一次请求。
break -当前匹配结束之后即终止匹配,不会建立新的请求,也不会再去重新匹配location,而是根据rewrite后的地址去寻找对应的资源返回给客户端。
redirect -返回临时重定向的 HTTP 状态 302,浏览器会发送两次请求给nginx,url分别是rewrite前和rewrite后的。
permanent -返回永久重定向的 HTTP 状态 301 ,浏览器会记住重定向之后的url,下次在访问老url时,自动跳转到新的地址去,不再发送请求给nginx,因此就算nginx挂了都可以访问(只要能正常解析)。
eg1:rewrite ^/images/(.*\.jpg)$ /imgs/$1
解释:^前缀匹配,&后缀匹配,&1匹配前面$括号中的内容,\转义符,将所有images/XXX.jpg的全部定向到/imgs/XXX.jpg下.
eg2: rewrite ^/shop/(.*\.html)$ /tuangou/$1 break;
eg3: rewrite ^/shop/(.*\.php)$ http://www.baidu.com/$1 redirect
典型示例:
1 location{
if ($http_user_agent ~ MSIE) {
rewrite ^.*$ /ie.html;
rewrire ^(.*)$ /msie/$1;
break; #(不break会循环重定向)
}
原始的url为:test.com/2..php匹配到了'/',重写之后,
相当于把url重写成了 test.com/ie.html,去访问了ie.html 与之前的2.php没有任何关系了
}
2 if (!-e $document_root$fastcgi_script_name) {
rewrite ^.*$ /404.html break;
}
3 糟糕的配置
rewrite ^/(.*)$ http://example.com/$1 permanent;
好一点的配置
rewrite ^ http://example.com/$request_uri
更好的配置
return 301 http://example.com/$request_uri
反复对比下这几个配置。 第一个 rewrite 捕获不包含第一个斜杠的完整 URI。 使用内置的变
量 $request_uri,我们可以有效的完全避免任何捕获和匹配。
4 location ~ ^/break {
rewrite ^/break /test/ break;
}
location ~ ^/last {
rewrite ^/last /test/ break;
}
location /test/ {
default_type application/json;
return 200 '{"status:success"}';
}
5 经典实例分析:
(1)
server {
server_name rewrite.test.com;
rewrite_log on;
root html/;
location /first {
rewrite /first(.*) /second$1 last;
return 200 "this is first!";
}
location /second {
rewrite /second(.*) /third$1 break;
return 200 "this is second!";
}
location /third {
return 200 "this is third!";
}
}
访问: curl rewrite.test.com/first/1.txt -x 127.0.0.1:80
==> 404
curl rewrite.test.com/second/1.txt -x 127.0.0.1:80
==> 404
日志:2019/01/23 18:59:28 [error] 19871#0: *30 open() "/usr/local/openresty/nginx/html/third/1.txt" failed (2: No such file or directory), client: 127.0.0.1, server: rewrite.test.com, request: "GET http://rewrite.test.com/first/1.txt HTTP/1.1", host: "rewrite.test.com"
分析: /first 首先会rewrite到 /second , /second rewrite到 /third,但是由于break的作用,rewrite模块停止工作,所有的该模块的指令都失效,则nginx会找/third对应的资源/third/1.txt,没有找到则报404错误
(2) 改为如下:
server {
server_name rewrite.test.com;
rewrite_log on;
root html/;
location /first {
rewrite /first(.*) /second$1 last;
return 200 "this is first!";
}
location /second {
rewrite /second(.*) /third$1 break;
echo "2222";
return 200 "this is second!";
}
location /third {
echo "3333";
return 200 "this is third!";
}
}
访问: curl rewrite.test.com/first/1.txt -x 127.0.0.1:80
==> 2222
curl rewrite.test.com/second/1.txt -x 127.0.0.1:80
==> 2222
分析:echo指令不是rewrite模块的,可以得到执行。
(3) 继续改:
upstream zhangbao{
server 10.99.2.15:80;
}
server {
server_name rewrite.test.com;
rewrite_log on;
root html/;
location /first {
rewrite /first(.*) /second$1 last;
return 200 "this is first!";
}
location /second {
rewrite /second(.*) /third$1 break;
proxy_pass http://zhangbao;
proxy_set_header Host rewrite.test2.com;
return 200 "this is second!";
}
location /third {
echo "3333";
return 200 "this is third!";
}
}
server {
server_name rewrite.test2.com;
location / {
return 200 "this is rewrite2";
}
}
访问: curl rewrite.test.com/first/1.txt -x 127.0.0.1:80
==> this is rewrite2
curl rewrite.test.com/second/1.txt -x 127.0.0.1:80
==> this is rewrite2
(4)继续改:
upstream zhangbao{
server 10.99.2.15:80;
}
server {
server_name rewrite.test.com;
rewrite_log on;
root html/;
location /first {
rewrite /first(.*) /second$1 last;
return 200 "this is first!";
}
location /second {
rewrite /second(.*) /third$1 break;
proxy_pass http://zhangbao;
proxy_set_header Host rewrite.test2.com;
echo "2222";
return 200 "this is second!";
}
location /third {
echo "3333";
return 200 "this is third!";
}
}
server {
server_name rewrite.test2.com;
location / {
return 200 "this is rewrite2";
}
}
此时的结果为:2222
可见echo模块在upstream模块之前,先echo执行了,upstream模块没有机会执行,自然也就不会proxy_pass了
参考连接:https://www.cnblogs.com/tongxiaoda/p/7451998.html
补充说明:
1 if 之后有空格
2 不要掉了break,不然会一直重定向。如果是IE浏览器访问,重定向到/ie.html, 则url变成xxx.com/ie.html,继续匹配location/,又进入第二个if判断,又被重定到/ie.html
3 当rewrite的正则表达时候中有“{}”时,需要将正则表达式用“”包起来,否则报错。
Nginx使用源于perl兼容正则表达式(PCRE)库,基本语法如下:
- ^:必须以 ^ 后的实体开头
- $:必须以$前的实体结尾
- .:匹配任意字符
- []:匹配指定字符集内的任意字符,[C],[A-Z]
- {n}:重复n次
- {n,}:重复n次或更多次
- [^]:匹配不包括在指定字符集内的任意字符
- |:匹配|之前或之后的实体
- ():分组,组成一组用于匹配的实体,通常与|,$等配合使用
1 配置参数:
gzip配置的常用参数
gzip on|off; #是否开启gzip
gzip_buffers 32 4K| 16 8K #缓冲(压缩在内存中缓冲几块? 每块多大?)
gzip_comp_level [1-9] #推荐6 压缩级别(级别越高,压的越小,越浪费CPU计算资源)
gzip_disable #正则匹配UA 什么样的Uri不进行gzip
gzip_min_length 200 # 开始压缩的最小长度(再小就不要压缩了,意义不在)
gzip_http_version 1.0|1.1 # 开始压缩的http协议版本(可以不设置,目前几乎全是1.1协议)
gzip_proxied # 设置请求者代理服务器,该如何缓存内容
gzip_types text/plain application/xml # 对哪些类型的文件用压缩 如txt,xml,html ,css
gzip_vary on|off # 是否传输gzip压缩标志
server {
location / {
set $memcached_key "$uri?$args";
memcached_pass host:11211;
error_page 404 502 504 = @fallback;
}
location @fallback {
proxy_pass http://backend;
}
}
eg:
server{
listen 80;
server_name memcached.com;
access_log logs/test.com.access.log main;
root html/test;
location / {
set $memcached_key $uri;
memcached_pass 127.0.0.1:11211;
error_page 404 callback.php;
}
...
}
这里写/callback.php 和写callback.php都行
location /basic_status {
stub_status on;
access_log off;
}
浏览器结果显示:
Active connections: 2
server accepts handled requests
4 4 9
Reading: 0 Writing: 1 Waiting: 1
location /basic_status {
stub_status on;
access_log off;
}
浏览器结果显示:
Active connections: 2
server accepts handled requests
4 4 9
Reading: 0 Writing: 1 Waiting: 1
生成安全的下载地址给用户,用户只有拿到正确的密钥和过期时间才能下载相应的资源,对服务器的资源起到了很好的保护作用。
用法实例:
可以参考官网实例
server{
listen 80;
server_name test.securelink.com;
root /usr/local/mywork/test;
location ~ \.(jpg|png|gif)$ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri secret";
if ($secure_link = "") {
return 403;
}
if ($secure_link = "0") {
return 410;
}
}
}
命令行得到密钥:
[root@zhangbao1 vhosts]# echo -n '2147483647/img/kb.jpg192.168.0.101 secret' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
MwseFtfFdnGK0R2KfBeiyA
拼接密钥和过期时间的链接地址:
http://test.securelink.com/img/kb.jpg?md5=MwseFtfFdnGK0R2KfBeiyA&expires=2147483647
则在浏览器上输入这个网址才能下载资源
也可以用脚本实现:
生产密钥和过期时间脚本:
#!/bin/sh
servername="test.securelink.com"
download_file="/img/kb.jpg"
time_num=$(date -d "2018-10-18 00:00:00" +%s)
secure_num="zhangbao"
res=$(echo -n '${time_num}${download_file} ${secure_num}' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =)
echo "http://${servername}${download_file}?md5=${res}&expires=${time_num}"
执行脚本之后输出:
http://test.securelink.com/img/kb.jpg?md5=oNz7HBPflrCWeEASTeZhIw&expires=1539792000
ngx_http_geo_module模块可以用来创建变量,其值依赖于客户端IP地址。
参考连接:http://www.ttlsa.com/nginx/using-nginx-geo-method/
模块用于定位ip地址
参考连接:https://www.centos.bz/2018/04/nginx-%E4%BD%BF%E7%94%A8-geoip-%E6%A8%A1%E5%9D%97%E5%8C%BA%E5%88%86%E7%94%A8%E6%88%B7%E5%9C%B0%E5%8C%BA/
使用方法:
1 linux上安装Geoip库:
附件地址:链接: https://pan.baidu.com/s/1dFl1zZN 密码: x37s
./configure
make
make install
2 重新编译nginx
我装的是openresty,直接在第一次安装时的编译参数后面加上geoip_module
./configure --prefix=/usr/local/openresty --add-module=/usr/local/src/openresty-1.13.6.1/bundle/ngx_cache_purge-2.3 --add-module=/usr/local/src/openresty-1.13.6.1/bundle/nginx_upstream_check_module-0.3.0 --add-module=/usr/local/src/openresty-1.13.6.1/bundle/ngx_http_consistent_hash-master --with-http_secure_link_module --with-http_geoip_module
3 配置
geoip_country /usr/local/share/GeoIP/GeoIP.dat;
geoip_city /usr/local/share/GeoIP/GeoLiteCity.dat;
4 使用该模块定义的变量
$geoip_country_code; – 两个字母的国家代码,如:”RU”, “US”。
$geoip_country_code3; – 三个字母的国家代码,如:”RUS”, “USA”。
$geoip_country_name; – 国家的完整名称,如:”Russian Federation”, “United States”。
$geoip_region – 地区的名称(类似于省,地区,州,行政区,联邦土地等),如:”30”。 30代码就是广州的意思
$geoip_city – 城市名称,如”Guangzhou”, “ShangHai”(如果可用)。
$geoip_postal_code – 邮政编码。
$geoip_city_continent_code。
$geoip_latitude – 所在维度。
$geoip_longitude – 所在经度。
使用方法:
一、生成CA证书
mkdir ssl_key
cd ssl_key/
openssl genrsa -idea -out zhangbao.key 1024 生成key
openssl req -new -key zhangbao.key -out zhangbao.csr 生成csr文件
openssl x509 -req -days 3650 -in zhangbao.csr -signkey zhangbao.key -out zhangbao.crt 生成crt证书文件
openssl x509 -noout -text -in /usr/local/mywork/ssl_key/zhangbao.crt 查看生成的证书文件信息
二 、配置nginx
...
指令:
map
map_hash_bucket_size
map_hash_max_size
用途:创建适用于AB测试的变量
http://www.ttlsa.com/nginx/nginx-ngx_http_split_clients_module/
用途:将返回给用户的用户的body中的内容替换掉
location / {
sub_filter 'Nginx.ORg' '$host/nginx'; #不区分大小写的替换
sub_fillter 'nginx.CoM' '$host/nginx';
sub_filter_once on; # 只替换一次
sub_filter_last_modified on; #是否返回last_modified的header头
sub_filter_last_modified off;
}
用途: 在返回当前请求之前或者之后,通过发送子请求的方式,追加额外的响应内容
http://www.ttlsa.com/linux/nginx-modules-ngx_http_addition_module/
location / {
add_before_body /before_action
add_after_body /after_action
addition_type *;
}
location /before_action {
return 'this is a before txt';
}
location /after_action {
return 'this is a after txt';
}
访问 test.com/a.txt
结果:
this is a before txt
a
this is a after txt
1 与upstream和health_check相关
微博的开源项目
参考:
https://blog.csdn.net/yueguanghaidao/article/details/52801043
https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=404151075&idx=1&sn=5f3b8c007981a2d048766f808f8c8c98&scene=2&srcid=0223XScbJrOv7noogVX6T60Q&from=timeline&isappinstalled=0#wechat_redirect
https://github.com/weibocom/nginx-upsync-module
另外每个 work 进程各自拉取、更新各自的路由表,采用这种方式的原因:一是基于 Nginx 的进程模型,彼此间数据独立、互不干扰;二是若采用共享内存,需要提前预分配,灵活性可能受限制,而且还需要读写锁,对性能可能存在潜在的影响;三是若采用共享内存,进程间协调去拉取配置,会增加它的复杂性,拉取的稳定性也会受到影响。基于这些原因,便采用了各自拉取的方式。
参考:https://blog.csdn.net/liutong123987/article/details/79239214
典型配置
http {
#---------------------
# test health check
#---------------------
lua_package_path "/usr/local/openresty/lualib/resty/?.lua;/usr/local/openresty/lualib/resty/upstream/?.lua;;";
upstream tomcat {
server 127.0.0.1:48080;
server 127.0.0.1:58080;
}
lua_shared_dict healthcheck 1m;
lua_socket_log_errors off;
init_worker_by_lua_block {
local hc = require "resty.upstream.healthcheck"
local ok, err = hc.spawn_checker {
shm = "healthcheck",
upstream = "tomcat",
type = "http",
http_req = "GET /health.txt HTTP/1.0\r\nHost: tomcat\r\n\r\n",
interval = 2000,
timeout = 5000,
fall = 3,
rise = 2,
valid_statuses = {200, 302},
concurrency = 1,
}
if not ok then
ngx.log(ngx.ERR, "=======> failed to spawn health checker: ", err)
return
end
}
server {
listen 38080;
server_name localhost;
access_log logs/access-38080.log main;
error_log logs/error-38080.log debug;
location / {
proxy_pass http://tomcat;
}
location /server/status {
access_log off;
default_type text/plain;
content_by_lua_block {
local hc = require "resty.upstream.healthcheck"
ngx.say("Nginx Worker PID: ", ngx.worker.pid())
ngx.print(hc.status_page())
}
}
}
}
总结:
nginx对后端real server做健康检查有三种方法:
方法1:ngx_http_proxy_module+ngx_http_upstream_module
nginx自带的模块
方法2:nginx_upstream_check_module模块
淘宝tengine自带的模块,nginx需要手动添加
方法3:lua-resty-upstream-healthcheck模块
方法4:ngx_http_healthcheck_module模块
早期的nginx提供的健康检查模块,目前已不用
2 ngx_set_misc
如果你想对 URI 参数值中的 %XX 这样的编码序列进行解码,可以使用第三方 ngx_set_misc 模块提供的 set_unescape_uri 配置指令:
配置示例:
location /test {
set_unescape_uri $name $arg_name;
set_unescape_uri $class $arg_class;
echo "name: $name";
echo "class: $class";
}
效果:
$ curl 'http://localhost:8080/test?name=hello%20world&class=9'
name: hello world
class: 9
3 realip模块
几点说明
1 CDN能够记录用户端的ip,它向后端转发的时候带上两个header头:X-Forwarded-For和X-Real-IP
2 nginx通过读取这两个头能够拿到真实ip,然后将真实ip用binary_remote_addr和remote_addr变量替换掉,其值为真实ip,这样做连接限制limit_conn才有意义,这也是为什么limit_req要出现在realip模块之后
编译: --with-http_realip_module