场景

  • 根据请求URI的某个参数,判断后分发到不同backend。具体来说会判断是否存在code=OVERSEA…,有则upstream到指定backend
  • 根据来源IP区分production环境和preview环境

问题

  • 上面提到的参数可能重复出现
  • if不支持复杂的逻辑运算
  • if is evil

方案

先写最终实现,然后解释

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
set code "";
if ( $args ~ "code=[^&]*.*code=[^&]*" ) {
    set $code "${code}Y";
}

if ( $args ~ "code=OVERSEA[^&]*" )  {
    set $code "${code}X";
}

if ( $code ~ "^X$" ) {
    proxy_pass http://oversea;
}

if ( $address_type ~ "lan" ) {
    proxy_pass http://lan;
}

proxy_pass http://wan;

nginx的set指令和if指令都工作在rewrite阶段。rewrite阶段的指令会按照config文件中的书写顺序依次执行

先从最简单实现开始,最开始想到的是如下写法

1
2
3
4
5
6
7
8
9
if ( $args ~ "code=OVERSEA[^&]*" )  {
    proxy_pass http://oversea;
}

if ( $address_type ~ "lan" ) {
    proxy_pass http://lan;
}

proxy_pass http://wan;

但会带来一个问题,如果请求参数有两个CODE,不管是代码写土了还是恶意,这种行为都是可能出现的。
如何解析args就成了问题。
据我实验$arg_xxx这种方式取到的值的应该是在参数列表中xxx第一次出现时的value。

为防止多个CODE参数出现时直接打流量到oversea,写出了上述最终实现。逻辑很简单,相信看一遍就明白了。

为什么写成这个鸟德行。

  1. nginx的if不支持逻辑运算
  2. 如果恶意的带上CODE=OVERSEA,可能会增加到oversea backend的非必要流量(该后端在国外,也会影响响应时间)。这里的逻辑是带2个及以上数量CODE参数的流量直接打到本国IDC
  3. $address_type的判断是很武断的,应该与$CODE变量一起参与判断
  4. 如何判断address_type,会另起一篇文章

关于if is evil,可看下如下两篇文章

-EOF-