Nginx 根据 HTTP method 分流 upstream

2022年9月24日 3041点热度 0人点赞 0条评论

本文是一篇简要的技术笔记。Nginx 作为反向代理,根据不同的 HTTP 方法,选择不同的 upstream。

1 if is evil

根据需求,我们很自然的想到在 location 块中添加 if 判断语句。 如

if ($request_method = POST ) {
  return 405;
}

location 块编排逻辑是可以完成分流的。但请务必参考 nginx 官网的警告文章:

文章中警告,在 location 使用多个 if 编排逻辑时,容易出现反直觉的异常现象。并提示到这不是 bug,是因为 nginx 对 if 的实现原理。这种错误甚至可以严重到 SIGSEGV

笔者也直接在这里粘贴官方给的 evil 实例,看看是否符合读者的预期呢?

# Here is collection of unexpectedly buggy configurations to show that
# if inside location is evil.

# only second header will be present in response
# not really bug, just how it works

location /only-one-if {
    set $true 1;

    if ($true) {
        add_header X-First 1;
    }

    if ($true) {
        add_header X-Second 2;
    }

    return 204;
}

# request will be sent to backend without uri changed
# to '/' due to if

location /proxy-pass-uri {
    proxy_pass http://127.0.0.1:8080/;

    set $true 1;

    if ($true) {
        # nothing
    }
}

# try_files wont work due to if

location /if-try-files {
     try_files  /file  @fallback;

     set $true 1;

     if ($true) {
         # nothing
     }
}

# nginx will SIGSEGV

location /crash {

    set $true 1;

    if ($true) {
        # fastcgi_pass here
        fastcgi_pass  127.0.0.1:9000;
    }

    if ($true) {
        # no handler here
    }
}

# alias with captures isn't correcly inherited into implicit nested
# location created by if

location ~* ^/if-and-alias/(?<file>.*) {
    alias /tmp/$file;

    set $true 1;

    if ($true) {
        # nothing
    }
}

另外文中指出,对于复杂的 if 逻辑,可以使用 lua-nginx-module

2 使用 map 分流 upstream

如果不使用额外的 lua 脚本,笔者在生产环境中使用 map 数据结构,参考了这篇 segmentfault 答案

        upstream webdav_default {
                server ...;
        }
        upstream webdav_upload {
                server ...;
        }
        upstream webdav_download {
                server ...;
        }
        map $request_method $upstream_location {
            GET     webdav_download;
            HEAD    webdav_download;
            PUT     webdav_upload;
            LOCK    webdav_upload;
            default webdav_default;
        }
        server {
            location / {
                proxy_pass https://$upstream_location;
            }
        }

注:

  • map 可以和 upstream 同级共同管理,储存在 server 块之外。
  • 实际生产环境比较复杂,有各类 headerrewrite 等操作。避免在 locaiton 中使用 if

3 多级 map 分流

可以使用多个连续 map, 如:

location / {
    proxy_pass http://$upstream_server_by_host;
    ...

upstream.conf 中:

map $host $upstream_server_by_host {
    example.com  $upstream_server_1_by_method;
    default      $upstream_server_2_by_method;
}
map $request_method $upstream_server_1_by_method {
    GET     upstream_server1;
    HEAD    upstream_server2;
    default upstream_server3;
}

参考

SPtuan

团子最大的愿望是度过平静的时光。 当前从事分布式存储研发工作。

0 0 votes
文章评分
Subscribe
提醒
guest

0 评论
最新
最旧 得票最多
Inline Feedbacks
View all comments