nginx处理http请求实例详解

本文在这基础上分析nginx服务器收到http请求行、请求头部后,http框架是如何调度各个http模块共同完成这个http请求。例如: http框架调度静态模块,获取服务器目录下的某个html页面返回给客户端; 或者http框架调度access权限访问模块,判断这个客户端是否有权限访问服务器。

一、event事件与http框架的交互

在接收完http请求行、http请求头部后,会调用ngx_http_process_request这个函数开始处理http请求。因为一个http请求由11个处理阶段组成,而每一个处理阶段都允许多个http模块介入,因此在这个函数中,将调度各个阶段的http模块共同完成这个请求。

//接收到http请求行与请求头后,http的处理流程,是第一个http处理请求的读事件回调
//这个函数执行后,将把读写事件的回调设置为ngx_http_request_handler。这样下次再有事件时
//将调用ngx_http_request_handler函数来处理,而不会再调用ngx_http_process_request了
static void ngx_http_process_request(ngx_http_request_t *r)
{
  ngx_connection_t *c;
  c = r->connection;
  //因为已经接收完http请求行、请求头部了,准备调用各个http模块处理请求了。
  //因此需要接收任何来自客户端的读事件,也就不存在接收http请求头部超时问题
  if (c->read->timer_set)
  {
    ngx_del_timer(c->read);
  }
  //重新设置当前连接的读写事件回调
  c->read->handler = ngx_http_request_handler;
  c->write->handler = ngx_http_request_handler;
  //设置http请求对象的读事件回调,这个回调不做任何的事情。
  //那http请求对象的读事件回调,与上面的连接对应的读事件回调有什么关系呢?
  //当读事件发生后,连接对应的读事件回调ngx_http_request_handler会被调用,
  //在这个回调内会调用http请求对象的读事件回调ngx_http_block_reading,而这个回调是
  //不会做任何事件的,因此相当于忽略了读事件。因为已经接收完了请求行请求头,现在要做的是调用各个http模块,
  //对接收到的请求行请求头进行处理
  r->read_event_handler = ngx_http_block_reading; 

  //调用各个http模块协同处理这个请求
  ngx_http_handler(r);
  //处理子请求
  ngx_http_run_posted_requests(c);
}

ngx_http_process_request函数只会被调用一次。如果一次调度并不能处理完11个http阶段,那会将连接对象对应的读事件、写事件回调设置为ngx_http_request_handler。而请求对象的读事件设置为ngx_http_block_reading, 请求对象的写事件回调设置为ngx_http_core_run_phases, 这个回调在ngx_http_handler内设置。这样在事件再次到来时不会调用

ngx_http_process_request函数处理了。那event事件模块的读写事件回调与http请求对象的读写事件回调有什么关系呢?

//http请求处理读与写事件的回调,在ngx_http_process_request函数中设置。
//这个函数中将会调用http请求对象的读写事件回调。将event事件模块与http框架关联起来
static void ngx_http_request_handler(ngx_event_t *ev)
{
  //如果同时发生读写事件,则只有写事件才会触发。写事件优先级更高
  if (ev->write)
  {
    r->write_event_handler(r);  //在函数ngx_http_handler设置为:ngx_http_core_run_phases 

  }
  else
  {
    r->read_event_handler(r);  //在函数ngx_http_process_request设置为:ngx_http_block_reading
  } 

  //处理子请求
  ngx_http_run_posted_requests(c);
} 

可以看到,连接对象的读事件回调中,会调用http请求对象的读事件回调。连接对象的写事件回调会调用http请求对象的写事件回调。

图中可看出,在event的读事件发生时,epoll返回后会调用读事件的回调ngx_http_request_handler。在这个读事件回调中,又会调用http框架,也就是http请求对象的读事件回调ngx_http_block_reading,这个http请求对象的读事件回调是不做任何事情的,相当于忽略读事件。因此http框架将会返回到事件模块。那为什么要忽略读事件呢?因为http请求行、请求头部都已经全部接收完成了, 现在要做的是调度各个http模块共同协作,完成对接收到的请求行,请求头部的处理。因此不需要接收来自客户端任何数据了。

对于写事件的处理就复杂多了,  在event的写事件发生时,epoll返回后会调用写事件的回调ngx_http_request_handler,在这个写事件回调中,又会调用http框架,也就是http请求对象的写事件回调ngx_http_core_run_phases。这个http框架的回调会调度介入11个请求阶段的各个http模块的hander方法,共同完成http请求。

二、调度http模块处理请求

在上面代码中,会调度ngx_http_core_run_phases这个函数,使得各个http模块能介入到http请求中来。而这个函数是在ngx_http_handler设置的。

//调用各个http模块协同处理这个请求
void ngx_http_handler(ngx_http_request_t *r)
{
  //不需要进行内部跳转。什么是内部跳转? 例如有个location结构,里面的
  //
  if (!r->internal)
  {
    //将数组序号设为0,表示从数组第一个元素开始处理http请求
    //这个下标很重要,决定了当前要处理的是11个阶段中的哪一个阶段,
    //以及由这个阶段的哪个http模块处理请求
    r->phase_handler = 0;
  }
  else
  {
    //需要做内部跳转
    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    //将序号设置为server_rewrite_index
    r->phase_handler = cmcf->phase_engine.server_rewrite_index;
  }
  //设置请求对象的写事件回调,这个回调将会调度介入11个http阶段的各个http模块
  //共同完成对请求的处理
  r->write_event_handler = ngx_http_core_run_phases;
  //开始调度介入11个http阶段的各个http模块
  ngx_http_core_run_phases(r);
} 

而ngx_http_core_run_phases函数就很简单了,调度介入11个http处理阶段的所有http模块的checker方法。

//调用各个http模块协同处理这个请求, checker函数内部会修改phase_handler
void ngx_http_core_run_phases(ngx_http_request_t *r)
{
  cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
  ph = cmcf->phase_engine.handlers;
  //调用各个http模块的checker方法,使得各个http模块可以介入http请求
  while (ph[r->phase_handler].checker)
  {
    rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
    //从http模块返回NGX_OK,http框架则会把控制全交还给事件模块
    if (rc == NGX_OK)
    {
      return;
    }
  } 

假设阶段2有三个http模块介入了http请求, 阶段3有一个模块介入了http请求、阶段4也有一个模块介入了请求。当开始处理阶段2时,将调用阶段2中的所有http模块进行处理,此时phase_handler指向阶段2的开始位置。之后每处理完阶段2中的一个模块时,phase_handler指向阶段2的下一个模块,直到阶段2处理完成。

当阶段2中的所有http模块都处理完成时,phase_handler将指向阶段3

因阶段3只有一个http模块,因此当阶段3中的所有http模块都处理完成时,phase_handler将指向阶段4

那这个handlers数组是什么时候创建的呢? 每一个http模块的checker回调又是做什么呢? 接下来将分析这两个问题

三、11个http请求阶段数组创建

在解析nginx.conf配置文件时,解析到http块时,会调用ngx_http_block这个函数开始解析http块。在这个函数中,也会把所有需要介入到11个http请求阶段的http模块,注册到数组中。

//开始解析http块
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
  //http配置解析完成后的后续处理,使得各个http模块可以介入到11个http阶段
  for (m = 0; ngx_modules[m]; m++)
  {
    if (ngx_modules[m]->type != NGX_HTTP_MODULE)
    {
      continue;
    } 

    module = ngx_modules[m]->ctx;
    if (module->postconfiguration)
    {
      //每一个http模块的在这个postconfiguration函数中,都可以把自己注册到11个http阶段
      if (module->postconfiguration(cf) != NGX_OK)
      {
        return NGX_CONF_ERROR;
      }
    }
  }
} 

例如ngx_http_static_module静态模块,会将自己介入11个http阶段的NGX_HTTP_CONTENT_PHASE阶段回调设置为ngx_http_static_handler

//静态模块将自己注册到11个http请求阶段中的NGX_HTTP_CONTENT_PHASE阶段
static ngx_int_t ngx_http_static_init(ngx_conf_t *cf)
{
  cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
  h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); 

  //静态模块在NGX_HTTP_CONTENT_PHASE阶段的处理方法
  *h = ngx_http_static_handler; 

  return NGX_OK;
} 

例如: ngx_http_access_module访问权限模块,会将自己介入11个http阶段的NGX_HTTP_ACCESS_PHASE阶段回调设置为ngx_http_access_handler

//访问权限模块将自己注册到11个http请求阶段中的NGX_HTTP_ACCESS_PHASE阶段
static ngx_int_t ngx_http_access_init(ngx_conf_t *cf)
{
  cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
  h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers); 

  //访问权限模块在NGX_HTTP_ACCESS_PHASE阶段的处理方法
  *h = ngx_http_access_handler; 

  return NGX_OK;
} 

上面的这些操作,只是把需要介入到11个http阶段的http模块保存到了ngx_http_core_main_conf_t中的phases成员中,并没有保存到phase_engine中。那什么时候将phases的内容保存到phase_engine中呢?  还是在ngx_http_block函数中完成

//开始解析http块
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    //初始化请求的各个阶段
  if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK)
  {
    return NGX_CONF_ERROR;
  }
} 

假设阶段1有一个http模块介入请求,阶段2有三个http模块介入请求、阶段3也有一个http模块介入请求。则ngx_http_init_phase_handlers这个函数调用后,从ngx_http_phase_t   phases[11]数组转换到ngx_http_phase_handler_t    handlers数组的过程如下图所示:

//初始化请求的各个阶段
static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{
  //11个http请求阶段,每一个阶段都可以有多个http模块介入。
  //这里统计11个节点一共有多个少http模块。以便下面开辟空间
  for (i = 0; i < NGX_HTTP_LOG_PHASE; i++)
  {
    n += cmcf->phases[i].handlers.nelts;
  }
  //开辟空间,存放介入11个处理阶段的所有http模块的回调
  ph = ngx_pcalloc(cf->pool,n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
  cmcf->phase_engine.handlers = ph;
  n = 0; 

  //对于每一个http处理阶段,给该阶段中所有介入的http模块赋值
  for (i = 0; i < NGX_HTTP_LOG_PHASE; i++)
  {
    h = cmcf->phases[i].handlers.elts; 

    switch (i)
    {
      case NGX_HTTP_SERVER_REWRITE_PHASE://根据请求的uri查找location之前,修改请求的uri阶段
        if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1)
        {
          cmcf->phase_engine.server_rewrite_index = n; //重定向模块在数组中的位置
        }
        checker = ngx_http_core_rewrite_phase;   //每一个阶段的checker回调
        break;
      case NGX_HTTP_FIND_CONFIG_PHASE://根据请求的uri查找location阶段(只能由http框架实现)
        find_config_index = n;
        ph->checker = ngx_http_core_find_config_phase;
        n++;
        ph++;
        continue;
      case NGX_HTTP_REWRITE_PHASE:  //根据请求的rui查找location之后,修改请求的uri阶段
        if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1)
        {
          cmcf->phase_engine.location_rewrite_index = n;
        }
        checker = ngx_http_core_rewrite_phase;
        break;
      case NGX_HTTP_POST_REWRITE_PHASE: //NGX_HTTP_REWRITE_PHASE阶段修改rul后,防止递归修改uri导致死循环阶段
        if (use_rewrite)
        {
          ph->checker = ngx_http_core_post_rewrite_phase;
          ph->next = find_config_index;//目的是为了地址重写后,跳转到NGX_HTTP_FIND_CONFIG_PHASE阶段,根据
                        //url重写查找location
          n++;
          ph++;
        }
        continue;
      case NGX_HTTP_ACCESS_PHASE:     //是否允许访问服务器阶段
        checker = ngx_http_core_access_phase;
        n++;
        break;
      case NGX_HTTP_POST_ACCESS_PHASE:  //根据NGX_HTTP_ACCESS_PHASE阶段的错误码,给客户端构造响应阶段
        if (use_access)
        {
          ph->checker = ngx_http_core_post_access_phase;
          ph->next = n;
          ph++;
        }
        continue;
      case NGX_HTTP_TRY_FILES_PHASE:   //try_file阶段
        if (cmcf->try_files)
        {
          ph->checker = ngx_http_core_try_files_phase;
          n++;
          ph++;
        }
        continue;
      case NGX_HTTP_CONTENT_PHASE:    //处理http请求内容阶段,大部分http模块最愿意介入的阶段
        checker = ngx_http_core_content_phase;
        break;
      default:
        //NGX_HTTP_POST_READ_PHASE,
        //NGX_HTTP_PREACCESS_PHASE,
        //NGX_HTTP_LOG_PHASE三个阶段的checker方法
        checker = ngx_http_core_generic_phase;
    }
    n += cmcf->phases[i].handlers.nelts;
    //每一个阶段中所介入的所有http模块,同一个阶段中的所有http模块有唯一的checker回调,
    //但handler回调每一个模块自己实现
    for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--)
    {
      ph->checker = checker;
      ph->handler = h[j];
      ph->next = n;
      ph++;
    }
  }
  return NGX_OK;
} 

四、http阶段的checker回调

在11个http处理阶段中,每一个阶段都有一个checker函数,当然有些阶段的checker函数是相同的。对每一个处理阶段,介入这个阶段的所有http模块都共用同一个checker函数。这些checker函数的作用是调度介入这个阶段的所有http模块的handler方法,或者切换到一下个http请求阶段。下面分析下NGX_HTTP_POST_READ_PHASE,NGX_HTTP_PREACCESS_PHASE,NGX_HTTP_LOG_PHASE三个阶段的checker方法。

//NGX_HTTP_POST_READ_PHASE,
//NGX_HTTP_PREACCESS_PHASE,
//NGX_HTTP_LOG_PHASE三个阶段的checker方法
//返回值: NGX_OK,http框架会将控制权交还给epoll模块
ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph)
{
  ngx_int_t rc;
  //调用http模块的处理方法,这样这个http模块就介入到了这个请求阶段
  rc = ph->handler(r);
  //跳转到下一个http阶段执行
  if (rc == NGX_OK)
  {
    r->phase_handler = ph->next;
    return NGX_AGAIN;
  } 

  //执行本阶段的下一个http模块
  if (rc == NGX_DECLINED)
  {
    r->phase_handler++;
    return NGX_AGAIN;
  } 

  //表示刚执行的handler无法在这一次调度中处理完这一个阶段,
  //需要多次调度才能完成
  if (rc == NGX_AGAIN || rc == NGX_DONE)  

  {
    return NGX_OK;
  }
  //返回出错
  /* rc == NGX_ERROR || rc == NGX_HTTP_... */
  ngx_http_finalize_request(r, rc); 

  return NGX_OK;
} 

到此http框架调度各个http模块共同完成http请求已经分析完了,

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • 微信小程序Server端环境配置详解(SSL, Nginx HTTPS,TLS 1.2 升级)

    微信小程序Server环境配置详解 主要内容: 1. SSL免费证书申请步骤 2. Nginx HTTPS 配置 3. TLS 1.2 升级过程 微信小程序要求使用 https 发送请求,那么Web服务器就要配置成支持 https,需要先申请SSL证书 小程序也要求 TLS(传输层安全协议)的版本至少为 1.2,在配置好 https之后,如果 TLS 的版本较低,就涉及到升级问题 所以 Server端环境配置的主要步骤: 申请 SSL 证书 配置web服务器支持https(我使用的是nginx)

  • 详解nginx同一端口监听多个域名和同时监听http与https

    详解nginx同一端口监听多个域名和同时监听http与https 1.同一端口监听多个域名 如今公网ip资源越来越珍贵,多域名监听应用非常广泛,就是用一个端口,比如80或者443,监听多个入口域名.如下: server { listen 443 ssl; server_name xxx.xxx.cn; ssl_certificate ssl/server.pem; ssl_certificate_key ssl/server.key; ...... } 在启用新的域名的时候,只需要另启一个ser

  • Nginx丢弃http包体处理实例详解

    Nginx丢弃http包体处理实例详解 http框架丢弃http请求包体和上一篇文章http框架接收包体, 都是由http框架提供的两个方法,供http各个模块调用,从而决定对包体做什么处理.是选择丢弃还是接收,都是由模块决定的.例如静态资源模块,如果接收到来自浏览器的get请求,请求某个文件时,则直接返回这个文件内容给浏览器就可以了.没有必要再接收包体数据,get请求实际上也不会有包体.因此静态资源模块将调用http框架提供的丢弃包体函数进行丢包处理. 相比接收包体过程, 丢弃包体操作就简单很

  • 详解nginx如何配置HTTPS

    使用ssl模块配置同时支持http和https并存 一,生成证书 # 1.首先,进入你想创建证书和私钥的目录,例如: cd /etc/nginx/ # 2.创建服务器私钥,命令会让你输入一个口令: openssl genrsa -des3 -out server.key 1024 # 3.创建签名请求的证书(CSR): openssl req -new -key server.key -out server.csr # 4.在加载SSL支持的Nginx并使用上述私钥时除去必须的口令: cp se

  • 详解NGINX访问https跳转到http的解决方法

    问题:浏览器打开https://www.jb51.net/aaa.html,然后跳转到http://www.jb51.net/aaa.html 网站架构:用户--https--->nginx代理---http---->tomcat/nginx+php nginx待遇发给后端的请求是http协议,后端程序跳转获取到的协议是http,返回一个redirect(http header中带Location:http://www.jb51.net/aaa.html),浏览器收到location,跳转到了

  • 详解Nginx服务器和iOS的HTTPS安全通信

    详解Nginx服务器和iOS的HTTPS安全通信 简介 在网络通信中,使用抓包软件可以对网络请求进行分析,并进行重放攻击,重放攻击的解决方案一般是使用一个变化的参数,例如RSA加密的时间戳,但考虑到网络传输时延,时间戳需要有一定的误差容限,这样仍然不能从根本上防止重放攻击.想要较好的解决重放攻击问题,应考虑使用HTTPS通信,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输.身份认证的网络协议,比HTTP协议安全. 实现 对于用浏览器访问的网站,需要向CA申请证书才能保证HTTPS的网

  • 详解阿里云LINUX服务器配置HTTPS(NGINX)

    背景说明 服务器为阿里云 ECS,操作系统为 CentOS 6.5. 部署配置说明 第一步,安装nginx 之所以要先安装 nginx,是因为下面配置域名解析的时候可以直接在浏览器看到效果,当然了,先配置域名,然后 ping 一下也是可以的 下载Nginx源码包,解压源码包,进入解压后的目录,编译配置,命令如下: ./configure --prefix=/usr/local/nginx --with-http_ssl_module 以上命令将nginx安装到usr/local/nginx目录下

  • 详解nginx服务器http重定向到https的正确写法

    http重定向到https使用了nginx的重定向命令.那么应该如何写重定向?之前老版本的nginx可能使用了以下类似的格式. rewrite ^/(.*)$ http://domain.com/$1 permanent; 或者 rewrite ^ http://domain.com$request_uri? permanent; 现在nginx新版本已经换了种写法,上面这些已经不再推荐. 下面是nginx http页面重定向到https页面最新支持的写法: server { listen 80

  • nginx处理http请求实例详解

    本文在这基础上分析nginx服务器收到http请求行.请求头部后,http框架是如何调度各个http模块共同完成这个http请求.例如: http框架调度静态模块,获取服务器目录下的某个html页面返回给客户端: 或者http框架调度access权限访问模块,判断这个客户端是否有权限访问服务器. 一.event事件与http框架的交互 在接收完http请求行.http请求头部后,会调用ngx_http_process_request这个函数开始处理http请求.因为一个http请求由11个处理阶

  • Linux Nginx 配置SSL访问实例详解

    Linux Nginx 配置SSL访问实例详解 生成证书 可以通过以下步骤生成一个简单的证书: 首先,进入你想创建证书和私钥的目录,例如: $ cd /usr/local/nginx/conf 创建服务器私钥,命令会让你输入一个口令: $ openssl genrsa -des3 -out server.key 1024 创建签名请求的证书(CSR): $ openssl req -new -key server.key -out server.csr 在加载SSL支持的Nginx并使用上述私钥

  • Nginx的反向代理实例详解

    一.反向代理实例 1 1.实现效果 (1)打开浏览器,在浏览器中输入www.123.com,跳转到linux系统tomcat主页面. 2.具体配置 (1)在windows系统的host文件进行域名和ip对应关系 (2)在Nginx进行请求 注意: 端口乤对外开放.  二.反向代理实例 2 1.实现效果 (1)使用Nginx反向代理,根据访问的路径跳转到不同端口的服务中,Nginx监听端口为8001. 访问 http://127.0.0.1:9001/deu/ 直接跳转到127.0.0.1:801

  • JSONP跨域请求实例详解

    JSOP简介 JSONP(JSON with Padding)是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题.由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外.利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 资料,而这种使用模式就是所谓的 JSONP.用 JSON

  • JS JSOP跨域请求实例详解

    在项目开发中遇到跨域的问题,一般都是通过JSONP来解决的.但是JSONP到底是个什么东西呢,实现的原理又是什么呢.在项目的空闲时间可以好好的来研究一下了. 1.什么是JSONP? 要了解JSONP,不得不提一下JSON,那么什么是JSON? JSON is a subset of the object literal notation of JavaScript. Since JSON is a subset of JavaScript, it can be used in the langu

  • Vue Ajax跨域请求实例详解

    一.设置config/index.js || proxyTable添加 proxyTable: { '/api': { target: 'http://192.168.48.239:8080/ydzl', changeOrigin: true, pathRewrite: { '^/api': '' } } 二.mian.js 添加vue全局属性 Vue.prototype.HOST = '/api' 三.如果是post的话 1.修改数据格式 transformRequest: [function

  • Nginx 多站点配置实例详解

    Nginx 多站点配置实例详解 在一台 VPS 上,我们有时候需要同时跑几个 virtualenv.比如 virtualenv app1 跑的是 Django 的一个应用,而 virtualenv app2 跑的是 Tornado.那么如何配置 Nginx,让它同时支持这两个 virtualenv 的运行呢? 首先是 Nginx 的主配置,位于 etc/nginx/ngnix.conf,让它保持默认就行: user nginx; worker_processes 1; error_log /va

  • nodejs处理http请求实例详解之get和post

    目录 前言:http 请求概述 Q: 从浏览器输入 url 到页面显示的整个过程? nodejs 处理 get 请求 简单的 demo 测试一下 nodejs 处理 post 请求 简单的demo 测试一下 补充:postman postman 的下载 总结 前言:http 请求概述 从一个经典的面试题入手,简单了解下 http 请求: Q: 从浏览器输入 url 到页面显示的整个过程? 第一步:DNS 解析, 建立 TCP 连接,发送 http 请求,简单来讲就是—— 先进行 DNS 解析,即

  • nginx虚拟主机配置实例详解

    nginx虚拟主机配置 server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.

  • nginx location 配置 正则表达式实例详解

    1.location 介绍 •location 是在 server 块中配置,用来通过匹配接收的uri来实现分类处理不同的请求,如反向代理,取静态文件等 •location 在 server 块中可以有多个,且是有顺序的,会被第一个匹配的 location 处理 •localtion 匹配功能只做匹配分发用,并不会改变uri的内容或其他作用,我一开始理解的时候就混淆了一些概念,建议多做测试看实际效果 2.localtion 匹配规则 •location [ = | ~ | ~* | ^~ ] u

随机推荐