1. Location 的生成

location 的生成大致有三种:

  • 由 location 指令直接生成
  • 命令 location:仅用于 server 内部跳转,如 rewrite 就是命名 location,命令 location 不能包含在其他 location 里。
  1. location @rewrite {
  2. rewrite ~ /wiki/search(.*)$ /search.php?serach=$1 last;
  3. }
  • 未命名 location:由 limit_except 和 if 等指令会间接导致生成 location,即为未命名 location。

1.1 由 location 指令直接生成

  1. static ngx_command_t ngx_http_core_commands[] = {
  2. ...
  3. { ngx_string("location"),
  4. NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
  5. ngx_http_core_location,
  6. NGX_HTTP_SRV_CONF_OFFSET,
  7. 0,
  8. NULL },
  9. ...
  10. };

标准的 location 指令配置语法为:

  1. 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 /dir01location /dir01/dir02,如有请求http://localhost/dir01/dir02/file将最终匹配到location /dir01/dir02

有如下匹配规则:

  1. location = / {
  2. echo "规则A";
  3. }
  4. location = /login {
  5. echo "规则B";
  6. }
  7. location ^~ /static/ {
  8. echo "规则C";
  9. }
  10. location ^~ /static/files {
  11. echo "规则X";
  12. }
  13. location ~ \.(gif|jpg|png|js|css)$ {
  14. echo "规则D";
  15. }
  16. location ~* \.png$ {
  17. echo "规则E";
  18. }
  19. location /img {
  20. echo "规则Y";
  21. }
  22. location / {
  23. echo "规则F";
  24. }

则产生的效果如下:

  • 访问根目录/,比如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.gifhttp://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

  1. static char *
  2. ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
  3. {
  4. char *rv;
  5. u_char *mod;
  6. size_t len;
  7. ngx_str_t *value, *name;
  8. ngx_uint_t i;
  9. ngx_conf_t save;
  10. ngx_http_module_t *module;
  11. ngx_http_conf_ctx_t *ctx, *pctx;
  12. ngx_http_core_loc_conf_t *clcf, *pclcf;
  13. /* 为当前location{}分配一个存储所有配置项的配置上下文结构体 */
  14. ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
  15. if (ctx == NULL) {
  16. return NGX_CONF_ERROR;
  17. }
  18. /* pctx 指向当前location的父级 {} 的配置上下文结构体,如 server{} */
  19. pctx = cf->ctx;
  20. /* 当前location{}的main_conf、srv_conf都继承自父级 {} 的配置上下文结构体 */
  21. ctx->main_conf = pctx->main_conf;
  22. ctx->srv_conf = pctx->srv_conf;
  23. /* 当前location{}仅需为所有 HTTP 模块分配 loc 级别的配置项存储空间 */
  24. ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
  25. if (ctx->loc_conf == NULL) {
  26. return NGX_CONF_ERROR;
  27. }
  28. /* 调用所有 HTTP 模块的 create_loc_conf(若有实现的话),将各个 HTTP 模块
  29. * 生成的 loc 级别的配置项结构体保存到当前location的 loc_conf 数组中的
  30. * 各自 ctx_index 索引处 */
  31. for (i = 0; cf->cycle->modules[i]; i++) {
  32. if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
  33. continue;
  34. }
  35. module = cf->cycle->modules[i]->ctx;
  36. if (module->create_loc_conf) {
  37. ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
  38. module->create_loc_conf(cf);
  39. if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
  40. return NGX_CONF_ERROR;
  41. }
  42. }
  43. }
  44. /* 获取当前location的loc_conf数组中 ngx_http_core_module 模块的loc级别配置项结构体 */
  45. clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
  46. /* ngx_http_core_module 模块的 ngx_http_core_loc_conf_t 结构体中的 loc_conf
  47. * 成员指向当前 location 的 loc_conf 数组 */
  48. clcf->loc_conf = ctx->loc_conf;
  49. /* 获取配置文件中 location 的第一个参数,即为 "location" */
  50. value = cf->args->elts;
  51. /* 若当前location配置指令总共有三个参数, 如: location = /test */
  52. if (cf->args->nelts == 3) {
  53. len = value[1].len;
  54. mod = value[1].data;
  55. name = &value[2];
  56. /* 若第二个参数为 '=',则表示该 location 为完全匹配,如: location = /test */
  57. if (len == 1 && mod[0] == '=') {
  58. /* 当前 location 的名称 */
  59. clcf->name = *name;
  60. /* 标志位,为 1 表示当前location为完全匹配类型 */
  61. clcf->exact_match = 1;
  62. /* 若第二个参数为 "^~",如: location ^~ /static/,则表示为对 URL 路径
  63. * 进行前缀匹配,并且优先级大于正则匹配 */
  64. } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
  65. clcf->name = *name;
  66. /* 标志位,为 1 表示不是正则匹配 */
  67. clcf->noregex = 1;
  68. /* 若第二个参数为 "~",如:location ~ \.(gif|jpg|png|js|css)$,则表示为
  69. * 区分大小写的正则匹配 */
  70. } else if (len == 1 && mod[0] == '~') {
  71. if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
  72. return NGX_CONF_ERROR;
  73. }
  74. /* 若第二个参数为 "~*",如:location ~* \.png$,则表示为不区分大小写的正则匹配 */
  75. } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
  76. if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
  77. return NGX_CONF_ERROR;
  78. }
  79. /* 除此之外的其他情况表示错误 */
  80. } else {
  81. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  82. "invalid location modifier \"%V\"", &value[1]);
  83. return NGX_CONF_ERROR;
  84. }
  85. /* 若为两个参数的情况,如:location /test */
  86. } else {
  87. /* 该location 的名称 */
  88. name = &value[1];
  89. /* 若第一个字符为'=',如:location =/test,则表示完全匹配 */
  90. if (name->data[0] == '=') {
  91. clcf->name.len = name->len - 1;
  92. clcf->name.data = name->data + 1;
  93. /* 标志位,为 1 表示完全匹配 */
  94. clcf->exact_match = 1;
  95. /* 若 1、2 字节为 "^~",如: location ^~/static/ */
  96. } else if (name->data[0] == '^' && name->data[1] == '~') {
  97. clcf->name.len = name->len - 2;
  98. clcf->name.data = name->data + 2;
  99. /* 标志位,为 1 表示不是正则匹配 */
  100. clcf->noregex = 1;
  101. /* 正则匹配 */
  102. } else if (name->data[0] == '~') {
  103. name->len--;
  104. name->data++;
  105. /* location ~*\.png$,不区分大小写的正则匹配 */
  106. if (name->data[0] == '*') {
  107. name->len--;
  108. name->data++;
  109. if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
  110. return NGX_CONF_ERROR;
  111. }
  112. /* 否则为 location ~\.(gif|jpg|png|js|css)$,表示区分大小写的正则匹配 */
  113. } else {
  114. if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
  115. return NGX_CONF_ERROR;
  116. }
  117. }
  118. } else {
  119. clcf->name = *name;
  120. /* 类似: location @rewrite,属于命名location,仅用于内部 server 跳转,
  121. * 如 rewrite 指令 */
  122. if (name->data[0] == '@') {
  123. /* 标志位,为 1 表示为命名location */
  124. clcf->named = 1;
  125. }
  126. }
  127. }
  128. /* 获取父级(一般为 server{}或 location{}(嵌套location的情况))下loc_conf数组中
  129. * ngx_http_core_module 模块的上下文配置结构体 */
  130. pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
  131. /* 若当前的 location 位于另一个 location 内 */
  132. if (cf->cmd_type == NGX_HTTP_LOC_CONF) {
  133. /* nested location */
  134. #if 0
  135. clcf->prev_location = pclcf;
  136. #endif
  137. /* 若父级 location 为完全匹配的情况,则表示出错,因为
  138. * 完全匹配的location内不允许嵌套另一个location */
  139. if (pclcf->exact_match) {
  140. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  141. "location \"%V\" cannot be inside "
  142. "the exact location \"%V\"",
  143. &clcf->name, &pclcf->name);
  144. return NGX_CONF_ERROR;
  145. }
  146. /* 若父级 location 为命名 location 的情况,则同样表示错误,
  147. * 因为命名 location 里也不能包含其他 location,并且命令 location
  148. * 只能在 server 上下文里,仅用于 server 内部跳转 */
  149. if (pclcf->named) {
  150. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  151. "location \"%V\" cannot be inside "
  152. "the named location \"%V\"",
  153. &clcf->name, &pclcf->name);
  154. return NGX_CONF_ERROR;
  155. }
  156. /* 若当前 location 为命名 location,则同样表示错误 */
  157. if (clcf->named) {
  158. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  159. "named location \"%V\" can be "
  160. "on the server level only",
  161. &clcf->name);
  162. return NGX_CONF_ERROR;
  163. }
  164. /* 父级 location 名称长度 */
  165. len = pclcf->name.len;
  166. #if (NGX_PCRE)
  167. if (clcf->regex == NULL
  168. && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
  169. #else
  170. if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
  171. #endif
  172. {
  173. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  174. "location \"%V\" is outside location \"%V\"",
  175. &clcf->name, &pclcf->name);
  176. return NGX_CONF_ERROR;
  177. }
  178. }
  179. /* 在对参数进行解析并区分出 location 类型以及做出有效性判断后,
  180. * 将该 location 添加到父级的 locations 队列里,这里表明是当前
  181. * server{} 下的所有 location 都添加到该 server{} 下的 locations
  182. * 队列中进行统一管理 */
  183. if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
  184. return NGX_CONF_ERROR;
  185. }
  186. save = *cf;
  187. /* 设置配置结构体的 ctx 上下文指向当前 location{} 的上下文 */
  188. cf->ctx = ctx;
  189. /* 标记接下来解析的指令位于 location{} 内 */
  190. cf->cmd_type = NGX_HTTP_LOC_CONF;
  191. /* 开始解析该 location{} */
  192. rv = ngx_conf_parse(cf, NULL);
  193. *cf = save;
  194. return rv;
  195. }

1.1.2 ngx_http_core_regex_location

该函数用于解析当前 location 指令为正则匹配的情况。

  1. /*
  2. * @regex: 该 location 的正则表达式
  3. * @caseless: 为 0 表示区分大小写; 为 1 表示不区分大小写
  4. */
  5. static ngx_int_t
  6. ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
  7. ngx_str_t *regex, ngx_uint_t caseless)
  8. {
  9. /* 必须启用了 PCRE 功能 */
  10. #if (NGX_PCRE)
  11. ngx_regex_compile_t rc;
  12. u_char errstr[NGX_MAX_CONF_ERRSTR];
  13. ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
  14. /* 该正则表达式 */
  15. rc.pattern = *regex;
  16. rc.err.len = NGX_MAX_CONF_ERRSTR;
  17. rc.err.data = errstr;
  18. #if (NGX_HAVE_CASELESS_FILESYSTEM)
  19. rc.options = NGX_REGEX_CASELESS;
  20. #else
  21. /* 为 1 表示不区分大小写,为 0 表示区分大小写 */
  22. rc.options = caseless ? NGX_REGEX_CASELESS : 0;
  23. #endif
  24. /* 暂时不继续向下分析 */
  25. clcf->regex = ngx_http_regex_compile(cf, &rc);
  26. if (clcf->regex == NULL) {
  27. return NGX_ERROR;
  28. }
  29. /* 将该正则表达式置为当前 location 的名称 */
  30. clcf->name = *regex;
  31. return NGX_OK;
  32. #else
  33. ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  34. "using regex \"%V\" requires PCRE library",
  35. regex);
  36. return NGX_ERROR;
  37. #endif
  38. }

1.1.3 ngx_http_add_location

  1. ngx_int_t
  2. ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
  3. ngx_http_core_loc_conf_t *clcf)
  4. {
  5. ngx_http_location_queue_t *lq;
  6. /* 若该 server{} 下的 locations 队列还没有生成,则创建 */
  7. if (*locations == NULL) {
  8. *locations = ngx_palloc(cf->temp_pool,
  9. sizeof(ngx_http_location_queue_t));
  10. if (*locations == NULL) {
  11. return NGX_ERROR;
  12. }
  13. /* 该队列用于管理所有的 location */
  14. ngx_queue_init(*locations);
  15. }
  16. /* 下面构造初始化一个 ngx_http_location_queue_t 结构体,该结构体
  17. * 表征一个 location,将会被挂载在 server{} 下的 loations 队列下 */
  18. lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
  19. if (lq == NULL) {
  20. return NGX_ERROR;
  21. }
  22. /* 绝对匹配、正则匹配以及命名或未命名地址都挂载在 exact 字段下 */
  23. if (clcf->exact_match
  24. #if (NGX_PCRE)
  25. || clcf->regex
  26. #endif
  27. || clcf->named || clcf->noname)
  28. {
  29. lq->exact = clcf;
  30. lq->inclusive = NULL;
  31. /* 其他情况,如前缀匹配则挂载在 inclusive 字段下 */
  32. } else {
  33. lq->exact = NULL;
  34. lq->inclusive = clcf;
  35. }
  36. /* 该 location 的名称 */
  37. lq->name = &clcf->name;
  38. /* 配置文件绝对路径名 */
  39. lq->file_name = cf->conf_file->file.name.data;
  40. /* 解析当前的 location 时在 nginx.conf 中的行号 */
  41. lq->line = cf->conf_file->line;
  42. /* 初始化该 lq->list 队列 */
  43. ngx_queue_init(&lq->list);
  44. /* 将该 lq 插入到 locations 队列的尾部 */
  45. ngx_queue_insert_tail(*locations, &lq->queue);
  46. return NGX_OK;
  47. }

一个 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 的生成的更多相关文章

  1. Nginx 之 Location 的整理

    1. Location 的整理 在将配置解析完后,所有的 location 此时都以 tree 的形式组织起来,具体可参考 Nginx之 Location 的生成. 此时需要对所有 server 下的 ...

  2. rewrite规则写法及nginx配置location总结

    rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用. 例如http://seanlook.com/a/we/index.php ...

  3. 快速掌握Nginx(二) —— Nginx的Location和Rewrite

    1 location详解 1.location匹配规则 Nginx中location的作用是根据Url来决定怎么处理用户请求(转发请求给其他服务器处理或者查找本地文件进行处理).location支持正 ...

  4. nginx负载均衡、nginx ssl原理及生成密钥对、nginx配制ssl

    1.nginx负载均衡 新建一个文件:vim /usr/local/nginx/conf/vhost/load.conf写入: upstream abc_com{ip_hash;server 61.1 ...

  5. 如何安装nginx_lua_module模块,升级nginx,nginx-lua-fastdfs-GraphicsMagick动态生成缩略图,实现图片自动裁剪缩放

    如何安装nginx_lua_module模块,升级nginx,nginx-lua-fastdfs-GraphicsMagick动态生成缩略图,实现图片自动裁剪缩放 参考网站:nginx-lua-fas ...

  6. Nginx配置location及rewrite规则

    Nginx配置location及rewrite规则 示例: location  = / {   # 精确匹配 / ,主机名后面不能带任何字符串   [ configuration A ] } loca ...

  7. nginx配置location总结及rewrite规则写法【转】

    转自 nginx配置location总结及rewrite规则写法 | Sean's Noteshttp://seanlook.com/2015/05/17/nginx-location-rewrite ...

  8. fastdfs+nginx+image_filter安装与生成缩略图

    fastdfs简介 类似google FS的一个轻量级分布式文件系统,纯C实现,支持linux.FreeBSD等UNIX系统: 只能通过API访问,不支持POXIS: 文件不分块存储,上传的文件和OS ...

  9. Nginx应用-Location路由反向代理及重写策略 请求转发-URL匹配规则 NGINX Reverse Proxy

    NGINX Docs | NGINX Reverse Proxy https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/ ...

随机推荐

  1. Ubuntu18.04安装MySQL与默认编码设置

    安装 打开终端直接开始,编码配置方法在后面 #通过apt更新包索引 sudo apt update #按照默认软件包安装 sudo apt install mysql-server #运行安全脚本 s ...

  2. 用python 打印出爱心

    其实,如果程序员真的很浪漫,普通人不懂,科技兴旺,也许你是惊呆了!!!!! 今天,泰泰又给你带来了一个“程序员技术(浪漫)表现”教程.飞鲸水龙头有希望它能在这个七月前夜帮到你.如果使用成功,记得给泰泰 ...

  3. vue组件常用传值

    一.使用Props传递数据   在父组件中使用儿子组件 <template> <div> 父组件:{{mny}} <Son1 :mny="mny"&g ...

  4. Java后端开发常用工具

    Java后端开发常用工具推荐: 俗话说,工欲善其事,必先利其器.不过初学时候不大建议过度依赖IDE等过多工具,这会让自己的编程基础功变得很差,比如各种语法的不熟悉,各种关键字比如synchronize ...

  5. python小知识-sys.argv

    sys.argv 就是一个从程序外部获取参数的桥梁 1.t1.py import sys a = sys.argv b = len(sys.argv) print(a) print(b) 在pytho ...

  6. mysql详解常用命令操作,利用SQL语句创建数据表—增删改查

    关系型数据库的核心内容是 关系 即 二维表 MYSQL的启动和连接show variables; [所有的变量] 1服务端启动 查看服务状态 sudo /etc/init.d/mysql status ...

  7. Scala高阶函数与泛型

    1. Scala中的函数 在Scala中,函数是“头等公民”,就和数字一样.可以在变量中存放函数,即:将函数作为变量的值(值函数). 2. scala中的匿名函数,即没有函数名称的函数,匿名函数常作为 ...

  8. java lambda 所有列求和

    今天做东西的时候遇到一个需求,求list集合所有列的求和.折腾半天也没有搞出来,网上大部分都是单列求和就像下面这样的,其他都差多,什么 min,max avg count 只得到了number这个属性 ...

  9. 美团面经-java开发

     美团(1)1 1 2 3 5 8...,求第n项写了个递归,面试官问了两个,n=-1,和极限最大值情况下怎么办.我回答,会导致栈的内存空间溢出.又问了,在栈里会是个怎样的过程.(2)打开摩拜单车页面 ...

  10. 浅析servlet

    目录 Servlet简介 Servlet定义 Servlet的特点 Servlet底层原理 Servlet GenericServlet HttpServlet Servlet的生命周期 Servle ...