文档中心 CDN Pro 用“if”来选择多个源站

用“if”来选择多个源站

更新时间:2022-05-31 14:06:05

用 `if` 来选择多个源站

在这一节里,我们将介绍一个比前面更复杂一点的用例。

需求描述:一个加速域名需要从两个不同的源站来加载内容。请求回哪一个由请求里的 x-origin 头部来决定;需要在回源请求里携带一个 x-msg2origin 头部,其值根据不同的源站有所不同;对于源站2,当请求对象是图片、HTML或者CSS文件时,需要在URI之前加上"/images"用来回源。

根据这个需求,我们可以写出如下的边缘逻辑:

location / {
  origin_pass origin1; # 默认回源站1
  origin_set_header x-msg2origin "message for origin 1";
  if ($http_x_origin = "origin2") { # 检查请求头x-origin
    origin_pass origin2; # 回源站2
    origin_set_header x-msg2origin "message for origin 2";
    rewrite /.*\.(html?|css|png|js|jpe?g) /image$uri break;
  }
}

这个例子里我们用到了rewrite模块里的if指令来检查请求头 x-origin 的值,定义不同的行为。但是,由于rewrite模块和其他模块在执行时序上的差异,我们在使用 if 指令的时候需要格外的小心。如果您需要在边缘逻辑里使用该指令,请务必遵循以下原则:

  • 仔细阅读开源版本的 rewrite 模块文档。比如您需要理解在上面的例子里,为什么 rewrite 指令最后需要加上 break 参数。
  • 请记住:只有rewrite模块里的指令(ifsetrewritereturnbreakeval_func)会按照他们在location配置块里出现的顺序被执行,这些指令也被称为 顺序型 (imperative)指令,它们的执行时间发生在请求处理的早期阶段,早于几乎所有其他的 声明型 (declarative)指令。在控制台的边缘逻辑编辑器里,为了让用户能方便地区分两类指令,顺序型指令呈蓝色而声明型指令为红色。不同的声明型指令在请求处理的不同阶段按需要被执行。如果有声明型指令被包含在多个 if (imperative)配置块里,只有最后一个满足条件的配置块里的指令会生效。一个例子如下:
location / {
  if ($http_header_a != '') {
    add_header has-header-a 1;
  }
  if ($http_header_b != '') {
    add_header has-header-b 1;
  }
  ...
}

如果 header-a header-b 同时出现在请求头部里,只有 has-header-b 会被添加到响应头里。对于NGINX来说这是符合预期的行为,不是一个bug。只不过,这个行为与大多数编程语言不一样而已。

不是所有的指令都可以用在 if 配置块里。在支持的指令文档中,我们在每条指令下的“可用位置”字段里,都标记指令支持的可支持配置块。对于那些不能在 if 配置块里使用的指令,如果他们支持变量参数,我们仍然可以通过在 if 配置块用 set 指令设置变量来控制他们的行为。比如这些指令: proxy_cache_validproxy_redirect 以及 proxy_cookie_domain
基于以上提到的原因,我们建议用户应尽可能避免在 if 配置块里使用声明型指令。这将大大提高配置的可读性,特别是对于NGINX的新用户来说。前面的例子如果用如下方式来重写,其结果可能更符合用户的期望:

location / {
  # 根据客户端请求来定义变量的取值
  if ($http_header_a != '') {
    set $has_header_a 1;
  }
  if ($http_header_b != '') {
    set $has_header_b 1;
  }
  # 将变量应用到声明型指令
  add_header has-header-a $has_header_a;
  add_header has-header-b $has_header_b;
  ...
}

如果需要根据条件来修改请求或响应的头部,也可以通过我们特别引入的 if() 参数来实现。下面这三个修改头部的指令都支持这个参数:add_headerorigin_set_header 以及 origin_header_modify 。如果条件里需要包含源站的响应内容,这实际上是唯一的实现方法。下面是如何使用这个参数来实现前面的例子:

location / {
  add_header has-header-a 1 if($http_header_a != '');
  add_header has-header-b 1 if($http_header_b != '');
  ...
}
本篇文档内容对您是否有帮助?
有帮助
没帮助
提交成功!非常感谢您的反馈,我们会继续努力做到更好!