从多站点静态页来看Nginx的HttpRewrite模块
背景:
在工作中我们通常会使用Nginx来部署静态页,不仅因为Nginx强大的web服务器功能,供重要的是因为Nginx可以充当静态页缓存服务,可以大大加速用户访问速度,提高用户体验。
但是通常服务器上仅用Nginx部署单站点静态页会造成资源的极大浪费(主机,网络资源等),因此企业内部经常会使用单独的区域,单独的主机来构建一个Nginx集群来专门用作企业内部的公共静态服务。这个时候我们就会将不同站点的静态内容都部署到该集群,而此时可能遇到的问题就会是可能多个站点会有相同的URI,但是希望定位到的是不同站点的资源,此时就需要借助HttpRewrite模块中的一些指令来完成。
例如: www.bgops.com 站点希望访问的资源A,而test-1.bgops.com和test-2.bgops.com站点希望访问的是资源B,对应而后的URI也是相应的(比如每个站点都会有domain/URI 资源)
如上场景,我们第一时间想到的会是通过headerhost
来判断站点,从而定位到响应资源,比如:
location / {
if ($host = 'www.bgops.com') {
root /var/www/html/A;
}
if ($host ~* 'test-(\d+).bgops.com') {
root /var/www/html/B;
}
}
注意:if指令是不可以进行或与非判断的,因此需要借助set指令
如上场景其实可以满足我们的需求的,但是如果站点过多,而且匹配的URI较多,那么我们需要再每个location
指令中进行很多的if
指令的判断,这样可能会导致配置冗长,很难进行管理,要知道,作为运维大多数的事故都是源自于变更,而通常变更出事故的原因也大多数是因为配置冗长,不便于管理二导致配置误变更的。因此,我们需要熟悉nginx的执行阶段,以及相关的指令优化。
接下来从Nginx的主要执行阶段
以及HttpRewrite模块
来优化下我们上述的场景。
nginx的主要执行阶段以及执行顺序
注意: Nginx处理请求共可以划分11个阶段,主要为rewrite 、access 、content
三个阶段.
-
rewrite:
-
ngx_rewrite模块
。set、rewrite、return、brek、if指定
-
-
ngx_lua模块
。set_by_lua指令
-
-
access:
-
ngx_access模块
。deny、alllow指令(按照书写先后匹配)
-
-
ngx_lua模块
。access_by_lua指令(access阶段末尾执行)
-
- content:
-
ngx_echo模块
。echo指令
-
-
ngx_lua模块
。content_by_lua指令
-
-
ngx_proxy模块
。proxy_pass指令
-
三个阶段的执行顺序遵循以下基本规则:
- 执行顺序rewrite > access > content
- 同一阶段中的相同模块中的指令执行顺序是按照书写顺序匹配(比如access阶段的ngx_access模块中的deny和allow指令)
- 同一阶段中不同模块的指令执行顺序是不确定的(有的是顺序执行,有的依赖模块)
if和set指令的相关使用
首先,我们来看下if和set指令是在哪个模块以及作用域下.
从Nginx官方文档我们知道if和set
指令均属于HttpRewrite
模块,该模块主要用来允许使用正则表达式改变URI,并且根据变量来转向以及选择配置。
需要注意的是:在location内部的重写规则如果重复匹配的话会循环10次,之后Nginx会返回500错误
HttpRewrite模块相关指令
break指令
# 语法:break
# 默认值:None
# 作用域: server,location,if
# 作用: 完成当前的规则匹配列
# 示例
if ($slow) {
limit_rate 10k;
break;
}
if指令
# 语法:if(condition) {}
# 默认值: None
# 作用域: server,location
# 作用: 检测条件是否为真,并执行相关内容块定义的规则.
# 可以是如下条件:
## 变量名(使用默认变量或set指令自定义变量),假值会是一个空字符串""或者一个以"0"开始的任意字符串
## 变量和等号的比较(=或者!=)
## 正则匹配(~*和~,前者大小写敏感)
## 文件目录检测(-f and !-f 以及-d and !-d)
## 文件/目录/软链检测(-e and !-e)
## 可执行文件检测(-x)
# 示例
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
}
if ($http_cookie ~* "id=([^;] +)(?:;|$)" ) {
set $id $1;
}
if ($request_method = POST ) {
return 405
}
if (!-f $request_filename) {
break;
proxy_pass http://127.0.0.1;
}
## invalid_referers变量是valid_referers指令内置的变量
if ($invalid_referer) {
return 403;
}
return指令
# 语法: return code
# 默认值: none
# 作用域: server,location,if
# 作用: 根据规则返回状态码
rewrite指令
# 语法: rewrite regex replacement flag
# 默认: none
# 作用域: server,location,if
# 作用: 根据表达式更改URI或修改字符串,指令根据配置文件顺序执行。
## 注意:重写仅对相对路径有效,如果想匹配主机名,需要使用if语句
# 示例
if ($host ~* www\.(.*)) {
#$1 会匹配www.之后的域名
set $host_without_www $1;
#$1 为匹配到的相对URI 比如/bgops/abc
rewrite ^(.*)$ http://$host_without_www$1 permanent;
}
## 注意:如果rewrite的内容是http://开始,客户端重定向之后其他的rewrite指令将不会执行(终止了)
## 不过可以使用参数来改变这一行为:
### last:完成rewrite指令的处理,然后搜索相应的URI和location
### break:完成rewrite指令的处理后结束
### redirect:返回临时重定向302,如果是http开头的比较有用
### permanent:返回永久重定向301
# 特殊字符的重定向
# 示例:
/photos/123456重定向/path/to/photos/12/1234/123456.png
rewrite "/photos/([0-9] {2})([0-9] {2})([0-9] {2})" /path/to/photos/$1/$1$2/$1$2$3.png;
set指令
# 语法: set variable value
# 默认值: none
# 作用域: server,location,if
# 作用: 通常用来自定义变量,在if 中进行条件判断。其中value可以是一个变量,文本,以及变量和文本的集合
再来看我们上述的场景
我们可以借助set
和if
指令来解决上述场景:
# nginx核心配置
server {
listen 80;
server_name www.bgops.com test1.bgops.com test2.bgops.com test3.bgops.com;
root html;
if ($host ~* "test(\d+).bgops.com") {
set $servername 'testfamily';
}
location / {
root /export/Data/bgops;
if ($servername ~* "testfamily") {
root /export/servers/nginx/html/i;
}
client_max_body_size 300m;
client_body_buffer_size 128k;
}
location /statics {
root /export/Data/bgops;
if ($servername ~* "testfamily") {
root /export/servers/nginx/html/i;
}
}
}
bash-4.1# cat /export/servers/nginx/html/i/index.html
iiiiiiiiiiii
bash-4.1# cat /export/servers/nginx/html/index.html
blog.bgops.com
bash-4.1# cat /export/Data/bgops/index.html
www.bgops.com
bash-4.1# curl localhost
blog.bgops.com
bash-4.1# curl -H 'host:www.bgops.com' localhost
www.bgops.com
bash-4.1# curl -H 'host:test1.bgops.com' localhost
iiiiiiiiiiii
bash-4.1# curl -H 'host:test2.bgops.com' localhost
iiiiiiiiiiii
bash-4.1# curl -H 'host:test3.bgops.com' localhost
iiiiiiiiiiii
网友评论