一、Nginx变量的基本概念与类型

在Nginx中,变量就像是我们日常生活中的"便签",可以临时存储各种信息。它们主要分为两种:内置变量和自定义变量。内置变量是Nginx自带的,比如$host代表请求的主机名,$uri表示当前请求的URI;而自定义变量则是我们通过set指令创建的,灵活性极高。

举个例子,我们可以在配置中这样使用内置变量:

server {
    listen 80;
    server_name example.com;
    
    location / {
        # 使用内置变量$host记录访问的域名
        add_header X-Host-Name $host;
        return 200 "Welcome to $host!";
    }
}

而自定义变量的创建也很简单:

server {
    listen 80;
    server_name example.com;
    
    # 定义一个变量$backend_server
    set $backend_server "192.168.1.100";
    
    location /api {
        proxy_pass http://$backend_server;
    }
}

注意:变量名区分大小写,且自定义变量仅在当前作用域有效。

二、动态配置的实现技巧

Nginx变量的真正威力在于它能实现动态配置。比如,我们可以根据请求参数动态改变代理目标:

server {
    listen 80;
    server_name example.com;
    
    # 从查询参数中获取backend参数值
    if ($arg_backend) {
        set $backend_server $arg_backend;
    }
    
    location /dynamic {
        # 动态代理到$backend_server
        proxy_pass http://$backend_server;
    }
}

这样,请求/dynamic?backend=192.168.1.101就会被代理到192.168.1.101

另一个常见场景是根据用户的地理位置动态返回内容。我们可以结合geoip模块:

http {
    geoip_country /usr/share/GeoIP/GeoIP.dat;
    
    server {
        listen 80;
        server_name example.com;
        
        # 根据国家代码设置变量
        set $country_code $geoip_country_code;
        
        location / {
            # 根据国家返回不同内容
            if ($country_code = "CN") {
                return 200 "你好,中国用户!";
            }
            return 200 "Hello, World!";
        }
    }
}

三、灵活路由控制的实现

利用变量,我们可以实现复杂的路由逻辑。比如,基于用户代理的路由:

map $http_user_agent $route {
    default           "default";
    "~*iPhone"       "mobile";
    "~*Android"      "mobile";
    "~*iPad"         "tablet";
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        # 根据设备类型路由
        if ($route = "mobile") {
            proxy_pass http://mobile_backend;
        }
        if ($route = "tablet") {
            proxy_pass http://tablet_backend;
        }
        proxy_pass http://default_backend;
    }
}

更复杂的场景是A/B测试。我们可以通过cookie实现:

server {
    listen 80;
    server_name example.com;
    
    # 检查ab_test cookie
    set $ab_group "A";
    if ($cookie_ab_test) {
        set $ab_group $cookie_ab_test;
    }
    
    location / {
        # 根据ab_group路由
        if ($ab_group = "A") {
            proxy_pass http://backend_a;
        }
        if ($ab_group = "B") {
            proxy_pass http://backend_b;
        }
        proxy_pass http://backend_a;
    }
}

四、高级技巧与性能优化

变量虽然强大,但滥用会影响性能。比如,避免在频繁执行的location中使用复杂变量计算:

server {
    listen 80;
    server_name example.com;
    
    # 不推荐:每次请求都计算
    location /bad {
        set $sum 0;
        set $i 0;
        while ($i < 100) {
            set $sum $sum+$i;
            set $i $i+1;
        }
        return 200 "Sum is $sum";
    }
    
    # 推荐:预计算
    location /good {
        # 假设sum已预先计算好
        set $pre_sum 4950;
        return 200 "Sum is $pre_sum";
    }
}

另一个技巧是使用map指令创建变量映射表,比多个if更高效:

map $uri $custom_header {
    default         "default";
    "/special"      "special_case";
    "/vip"          "vip_user";
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        add_header X-Custom $custom_header;
        proxy_pass http://backend;
    }
}

五、应用场景与注意事项

应用场景

  1. 多租户系统:根据域名动态选择后端
  2. 灰度发布:通过cookie或header路由流量
  3. 地域化内容:基于地理位置返回不同内容
  4. 设备适配:针对不同设备优化体验

技术优缺点

  • 优点:灵活、无需重启生效、可与其他模块配合
  • 缺点:复杂逻辑影响性能、调试困难

注意事项

  1. 变量作用域要清晰
  2. 避免在变量中使用Nginx指令
  3. 谨慎使用if,它有自己的作用域规则
  4. 性能敏感场景要测试变量计算开销

六、总结

Nginx变量就像一把瑞士军刀,用好了能让配置变得极其灵活。从简单的动态代理到复杂的路由策略,变量都能优雅地解决问题。关键是要理解其工作原理,在灵活性和性能之间找到平衡。