nginx location regex matches directory range

I am trying to set up nginx and I don't get the point how to do the following setup:

I have an URL http://somehost.com/foo/bar/123/xxx_xxx that needs to be passed to different backends depending on a range the '123' matches (e.g. 0-150 -> backend1, 151-400 -> backend2, ...).

Since I have almost no experience with regexp, I don't know how to do this (in location?).

Thanks in advance for your help, Sascha


Solution 1:

You can use:

location ~ ^/foo/bar/(\d|\d\d|1[0-4]\d|150)/ {
    proxy_pass backend1;
}

location ~ ^/foo/bar/(15[1-9]|1[6-9]\d|[23]\d\d|400)/ {
    proxy_pass backend2;
}
...

But why you do load balancing in such a heterogeneous way? Why not just shard by image id/hash of name? Your solution will result in different load and even load patterns on different servers. It would be tricky to administer them.

Solution 2:

If you have a static set of the picture sizes, you can make use of map. Declare backends and a map in http section of Nginx configuration file:

http {
  upstream backend1 {
    server backend1:80;
  }

  upstream backend2 {
    server backend2:80;
  }

  map $pic_size $backend {
    default backend_default;
    80 backend1;
    150 backend2;
  }
}

Declare the location in server:

location ~ ^/foo/bar/(?<pic_size>\d+) {
  proxy_pass http://$backend;
}

If you'd like to support conditional logic, I would recommend using Perl handler. Again, in http:

http {
  perl_set $backend 'sub {
my $r = shift;
my $pic_size = $r->variable("pic_size");

$pic_size <= 150 and return "backend1";
$pic_size <= 400 and return "backend2";
return "backend_default";
}';
}

In location:

location ~ ^/foo/bar/(?<pic_size>\d+) {
  proxy_pass http://$backend:80;
}