Nginx之 Location 的生成
1. Location 的生成
location 的生成大致有三种:
- 由 location 指令直接生成
- 命令 location:仅用于 server 内部跳转,如 rewrite 就是命名 location,命令 location 不能包含在其他 location 里。
location @rewrite {
rewrite ~ /wiki/search(.*)$ /search.php?serach=$1 last;
}
- 未命名 location:由 limit_except 和 if 等指令会间接导致生成 location,即为未命名 location。
1.1 由 location 指令直接生成
static ngx_command_t ngx_http_core_commands[] = {
...
{ ngx_string("location"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
ngx_http_core_location,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },
...
};
标准的 location 指令配置语法为:
location [ = | ~ | ~* | ^~ ] uri { ... }
location = /uri
:= 表示精确匹配,只有完全匹配上才能生效location ^~ /uri
:^~ 开头对 URL 路径进行前缀匹配,并且在正则之前location ~ pattern
:开头表示区分大小写的正则匹配location ~* pattern
:开头表示不区分大小写的正则匹配location /uri
:不带任何修饰符,也表示前缀匹配,但是在正则匹配之后location /
:通用匹配,任何未匹配到其它 location 的请求都会匹配到,相当于 switch 中的 default
前缀匹配时,Nginx 不对 url 做编码,因此请求为/static/20%/aa
,可以被规则^~ /static/ /aa
匹配到(注意是空格)。
多个 location 配置的情况下匹配顺序为(参考资料而来):
- 首先精确匹配 =
- 其次前缀匹配 ^~
- 其次是按文件中顺序的正则匹配
- 然后匹配不带任何修饰符的前缀匹配
- 最后是交给 / 通用匹配
- 当有匹配成功时,停止匹配,按当前匹配规则处理请求
注意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。比如在前缀匹配:location /dir01
与location /dir01/dir02
,如有请求http://localhost/dir01/dir02/file
将最终匹配到location /dir01/dir02
。
有如下匹配规则:
location = / {
echo "规则A";
}
location = /login {
echo "规则B";
}
location ^~ /static/ {
echo "规则C";
}
location ^~ /static/files {
echo "规则X";
}
location ~ \.(gif|jpg|png|js|css)$ {
echo "规则D";
}
location ~* \.png$ {
echo "规则E";
}
location /img {
echo "规则Y";
}
location / {
echo "规则F";
}
则产生的效果如下:
- 访问根目录
/
,比如http://localhost/
将匹配规则 A - 访问
http://localhost/login
将匹配规则 B,http://localhost/register
则匹配规则 F - 访问
http://localhost/static/a.html
将匹配规则 C - 访问
http://localhost/static/files/a.exe
将匹配规则 X,虽然规则 C 也能匹配到,但因为最大匹配原则,最终选中了规则 X。 - 访问
http://localhost/a.gif
,http://localhost/b.jpg
将匹配规则 D 和规则 E,但是规则 D 顺序优先,规则 E 不起作用,而http://localhost/static/c.png
则优先匹配到规则 C。 - 访问
http://localhost/a.PNG
则匹配规则 E,而不会匹配规则 D,因为规则 E 不区分大小写。 - 访问
http://localhost/img/a.gif
会匹配上规则 D,虽然规则 Y 也可以匹配上,但是因为正则匹配优先,而忽略了规则 Y。 - 访问
http://localhost/img/a.tiff
会匹配上规则 Y。 - 访问
http://localhost/category/id/111
则最终会匹配到规则 F,因为以上规则都不匹配,这个时候应该是 Nginx 转发请求给后端应用服务器,比如 FastCGI(php),tomcat(jsp),Nginx 作为反向代理服务器存在。
1.1.1 ngx_http_core_location
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
u_char *mod;
size_t len;
ngx_str_t *value, *name;
ngx_uint_t i;
ngx_conf_t save;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx, *pctx;
ngx_http_core_loc_conf_t *clcf, *pclcf;
/* 为当前location{}分配一个存储所有配置项的配置上下文结构体 */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
/* pctx 指向当前location的父级 {} 的配置上下文结构体,如 server{} */
pctx = cf->ctx;
/* 当前location{}的main_conf、srv_conf都继承自父级 {} 的配置上下文结构体 */
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
/* 当前location{}仅需为所有 HTTP 模块分配 loc 级别的配置项存储空间 */
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
/* 调用所有 HTTP 模块的 create_loc_conf(若有实现的话),将各个 HTTP 模块
* 生成的 loc 级别的配置项结构体保存到当前location的 loc_conf 数组中的
* 各自 ctx_index 索引处 */
for (i = 0; cf->cycle->modules[i]; i++) {
if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = cf->cycle->modules[i]->ctx;
if (module->create_loc_conf) {
ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
module->create_loc_conf(cf);
if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
/* 获取当前location的loc_conf数组中 ngx_http_core_module 模块的loc级别配置项结构体 */
clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
/* ngx_http_core_module 模块的 ngx_http_core_loc_conf_t 结构体中的 loc_conf
* 成员指向当前 location 的 loc_conf 数组 */
clcf->loc_conf = ctx->loc_conf;
/* 获取配置文件中 location 的第一个参数,即为 "location" */
value = cf->args->elts;
/* 若当前location配置指令总共有三个参数, 如: location = /test */
if (cf->args->nelts == 3) {
len = value[1].len;
mod = value[1].data;
name = &value[2];
/* 若第二个参数为 '=',则表示该 location 为完全匹配,如: location = /test */
if (len == 1 && mod[0] == '=') {
/* 当前 location 的名称 */
clcf->name = *name;
/* 标志位,为 1 表示当前location为完全匹配类型 */
clcf->exact_match = 1;
/* 若第二个参数为 "^~",如: location ^~ /static/,则表示为对 URL 路径
* 进行前缀匹配,并且优先级大于正则匹配 */
} else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
clcf->name = *name;
/* 标志位,为 1 表示不是正则匹配 */
clcf->noregex = 1;
/* 若第二个参数为 "~",如:location ~ \.(gif|jpg|png|js|css)$,则表示为
* 区分大小写的正则匹配 */
} else if (len == 1 && mod[0] == '~') {
if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* 若第二个参数为 "~*",如:location ~* \.png$,则表示为不区分大小写的正则匹配 */
} else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* 除此之外的其他情况表示错误 */
} else {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid location modifier \"%V\"", &value[1]);
return NGX_CONF_ERROR;
}
/* 若为两个参数的情况,如:location /test */
} else {
/* 该location 的名称 */
name = &value[1];
/* 若第一个字符为'=',如:location =/test,则表示完全匹配 */
if (name->data[0] == '=') {
clcf->name.len = name->len - 1;
clcf->name.data = name->data + 1;
/* 标志位,为 1 表示完全匹配 */
clcf->exact_match = 1;
/* 若 1、2 字节为 "^~",如: location ^~/static/ */
} else if (name->data[0] == '^' && name->data[1] == '~') {
clcf->name.len = name->len - 2;
clcf->name.data = name->data + 2;
/* 标志位,为 1 表示不是正则匹配 */
clcf->noregex = 1;
/* 正则匹配 */
} else if (name->data[0] == '~') {
name->len--;
name->data++;
/* location ~*\.png$,不区分大小写的正则匹配 */
if (name->data[0] == '*') {
name->len--;
name->data++;
if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
return NGX_CONF_ERROR;
}
/* 否则为 location ~\.(gif|jpg|png|js|css)$,表示区分大小写的正则匹配 */
} else {
if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} else {
clcf->name = *name;
/* 类似: location @rewrite,属于命名location,仅用于内部 server 跳转,
* 如 rewrite 指令 */
if (name->data[0] == '@') {
/* 标志位,为 1 表示为命名location */
clcf->named = 1;
}
}
}
/* 获取父级(一般为 server{}或 location{}(嵌套location的情况))下loc_conf数组中
* ngx_http_core_module 模块的上下文配置结构体 */
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
/* 若当前的 location 位于另一个 location 内 */
if (cf->cmd_type == NGX_HTTP_LOC_CONF) {
/* nested location */
#if 0
clcf->prev_location = pclcf;
#endif
/* 若父级 location 为完全匹配的情况,则表示出错,因为
* 完全匹配的location内不允许嵌套另一个location */
if (pclcf->exact_match) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"location \"%V\" cannot be inside "
"the exact location \"%V\"",
&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}
/* 若父级 location 为命名 location 的情况,则同样表示错误,
* 因为命名 location 里也不能包含其他 location,并且命令 location
* 只能在 server 上下文里,仅用于 server 内部跳转 */
if (pclcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"location \"%V\" cannot be inside "
"the named location \"%V\"",
&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}
/* 若当前 location 为命名 location,则同样表示错误 */
if (clcf->named) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"named location \"%V\" can be "
"on the server level only",
&clcf->name);
return NGX_CONF_ERROR;
}
/* 父级 location 名称长度 */
len = pclcf->name.len;
#if (NGX_PCRE)
if (clcf->regex == NULL
&& ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#else
if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
#endif
{
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"location \"%V\" is outside location \"%V\"",
&clcf->name, &pclcf->name);
return NGX_CONF_ERROR;
}
}
/* 在对参数进行解析并区分出 location 类型以及做出有效性判断后,
* 将该 location 添加到父级的 locations 队列里,这里表明是当前
* server{} 下的所有 location 都添加到该 server{} 下的 locations
* 队列中进行统一管理 */
if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
save = *cf;
/* 设置配置结构体的 ctx 上下文指向当前 location{} 的上下文 */
cf->ctx = ctx;
/* 标记接下来解析的指令位于 location{} 内 */
cf->cmd_type = NGX_HTTP_LOC_CONF;
/* 开始解析该 location{} */
rv = ngx_conf_parse(cf, NULL);
*cf = save;
return rv;
}
1.1.2 ngx_http_core_regex_location
该函数用于解析当前 location 指令为正则匹配的情况。
/*
* @regex: 该 location 的正则表达式
* @caseless: 为 0 表示区分大小写; 为 1 表示不区分大小写
*/
static ngx_int_t
ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
ngx_str_t *regex, ngx_uint_t caseless)
{
/* 必须启用了 PCRE 功能 */
#if (NGX_PCRE)
ngx_regex_compile_t rc;
u_char errstr[NGX_MAX_CONF_ERRSTR];
ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
/* 该正则表达式 */
rc.pattern = *regex;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
#if (NGX_HAVE_CASELESS_FILESYSTEM)
rc.options = NGX_REGEX_CASELESS;
#else
/* 为 1 表示不区分大小写,为 0 表示区分大小写 */
rc.options = caseless ? NGX_REGEX_CASELESS : 0;
#endif
/* 暂时不继续向下分析 */
clcf->regex = ngx_http_regex_compile(cf, &rc);
if (clcf->regex == NULL) {
return NGX_ERROR;
}
/* 将该正则表达式置为当前 location 的名称 */
clcf->name = *regex;
return NGX_OK;
#else
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"using regex \"%V\" requires PCRE library",
regex);
return NGX_ERROR;
#endif
}
1.1.3 ngx_http_add_location
ngx_int_t
ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
ngx_http_core_loc_conf_t *clcf)
{
ngx_http_location_queue_t *lq;
/* 若该 server{} 下的 locations 队列还没有生成,则创建 */
if (*locations == NULL) {
*locations = ngx_palloc(cf->temp_pool,
sizeof(ngx_http_location_queue_t));
if (*locations == NULL) {
return NGX_ERROR;
}
/* 该队列用于管理所有的 location */
ngx_queue_init(*locations);
}
/* 下面构造初始化一个 ngx_http_location_queue_t 结构体,该结构体
* 表征一个 location,将会被挂载在 server{} 下的 loations 队列下 */
lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
if (lq == NULL) {
return NGX_ERROR;
}
/* 绝对匹配、正则匹配以及命名或未命名地址都挂载在 exact 字段下 */
if (clcf->exact_match
#if (NGX_PCRE)
|| clcf->regex
#endif
|| clcf->named || clcf->noname)
{
lq->exact = clcf;
lq->inclusive = NULL;
/* 其他情况,如前缀匹配则挂载在 inclusive 字段下 */
} else {
lq->exact = NULL;
lq->inclusive = clcf;
}
/* 该 location 的名称 */
lq->name = &clcf->name;
/* 配置文件绝对路径名 */
lq->file_name = cf->conf_file->file.name.data;
/* 解析当前的 location 时在 nginx.conf 中的行号 */
lq->line = cf->conf_file->line;
/* 初始化该 lq->list 队列 */
ngx_queue_init(&lq->list);
/* 将该 lq 插入到 locations 队列的尾部 */
ngx_queue_insert_tail(*locations, &lq->queue);
return NGX_OK;
}
一个 server{} 下若有 4 个 location,则会形成如下图所示的框架:
如果整个配置里存在多个 location 的层次嵌套,那么此时对应的组织结构图如下图所示。
此外,由 Nginx 的源码可知,ngx_http_add_location 函数也会被 ngx_http_core_limit_except() 和 ngx_http_rewrite_if() 调用。因此,除了 location 指令之外,还有指令 limit_except 和 if 也会间接的导致生成 location,也即未命名 location,它们对应的 noname 字段会被设置为 1(即 clcf->noname = 1;)。这些 location 同样会加入到上图的 location tree 中,因为虽然它们最终并没有用在一般的请求地址匹配查找过程里,但是在进行配置合并的时候,这样可以让它们可以继承来之上层 location 的相关设置值。
Nginx之 Location 的生成的更多相关文章
- Nginx 之 Location 的整理
1. Location 的整理 在将配置解析完后,所有的 location 此时都以 tree 的形式组织起来,具体可参考 Nginx之 Location 的生成. 此时需要对所有 server 下的 ...
- rewrite规则写法及nginx配置location总结
rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用. 例如http://seanlook.com/a/we/index.php ...
- 快速掌握Nginx(二) —— Nginx的Location和Rewrite
1 location详解 1.location匹配规则 Nginx中location的作用是根据Url来决定怎么处理用户请求(转发请求给其他服务器处理或者查找本地文件进行处理).location支持正 ...
- nginx负载均衡、nginx ssl原理及生成密钥对、nginx配制ssl
1.nginx负载均衡 新建一个文件:vim /usr/local/nginx/conf/vhost/load.conf写入: upstream abc_com{ip_hash;server 61.1 ...
- 如何安装nginx_lua_module模块,升级nginx,nginx-lua-fastdfs-GraphicsMagick动态生成缩略图,实现图片自动裁剪缩放
如何安装nginx_lua_module模块,升级nginx,nginx-lua-fastdfs-GraphicsMagick动态生成缩略图,实现图片自动裁剪缩放 参考网站:nginx-lua-fas ...
- Nginx配置location及rewrite规则
Nginx配置location及rewrite规则 示例: location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ configuration A ] } loca ...
- nginx配置location总结及rewrite规则写法【转】
转自 nginx配置location总结及rewrite规则写法 | Sean's Noteshttp://seanlook.com/2015/05/17/nginx-location-rewrite ...
- fastdfs+nginx+image_filter安装与生成缩略图
fastdfs简介 类似google FS的一个轻量级分布式文件系统,纯C实现,支持linux.FreeBSD等UNIX系统: 只能通过API访问,不支持POXIS: 文件不分块存储,上传的文件和OS ...
- Nginx应用-Location路由反向代理及重写策略 请求转发-URL匹配规则 NGINX Reverse Proxy
NGINX Docs | NGINX Reverse Proxy https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ ...
随机推荐
- 使用SSH命令行远程登录运行在CloudFoundry上的应用
当我试图用如下命令行采用SSH远程登录到运行在CloudFoundry环境下的应用时, cf ssh -N -T -L 9229:127.0.0.1:9229 jerry-demo-server 遇到 ...
- 根据CPU核心数确定线程池并发线程数(转)
一.抛出问题 关于如何计算并发线程数,一般分两派,来自两本书,且都是好书,到底哪个是对的?问题追踪后,整理如下: 第一派:<Java Concurrency in Practice>即&l ...
- C# 填充无效,无法被移除
1.本文采用微软的 RijndaelManaged 命名空间: System.Security.Cryptography Assemblies: mscorlib.dll, netstandard.d ...
- Dedecms限制栏目列表生成的最大页数
首先,我们要登陆DEDECMS后台 >> 系统 >> 站点设置 的同条栏目上,添加一个新的变量,变量名称:cfg_listmaxpage,变量说明:栏目生成列表最大页数,变量值 ...
- RobHess的SIFT代码解析之kd树
平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码:SIFT+KD ...
- django的几种缓存配置
前言 首先说,为什么要用缓存的,由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存 ...
- Linux rpm和yum软件管理
rpm是管理程序的一个小工具,rpm常来用作查询 什么源码包:大多数都是tar.gz,bz.bz2结尾的包 zip结尾的包 压缩格式为 zip –r 命名.zip ./* 解压格式为 unzip 命名 ...
- HAL库 TIM计数器及中断开启过程
1.初始化TIM基本计数器参数 void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {}; TIM_Master ...
- FFmpeg常用命令学习笔记(五)裁剪与合并命令
裁剪与合并命令 1.音视频裁剪 ffmpeg -i input.mp4 -ss 00:01:00 -t 10 out.mp4 -ss:起始时间(HH:MM:SS).-t:裁剪时长(秒) 2.视频合并 ...
- hdfs冷热数据分层存储
hdfs如何让某些数据查询快,某些数据查询慢? hdfs冷热数据分层存储 本质: 不同路径制定不同的存储策略. hdfs存储策略 hdfs的存储策略 依赖于底层的存储介质. hdfs支持的存储介质: ...