5

Let's say I have items A, B, and C. I have two backend servers: server01 and server02.

Item A can be handled by server01, item B and C can be handled by server02. New items and servers get added and removed, and we programatically update a hash table between item id and backend server in a file, Redis, Memcache, or similar (whatever the proxy would support).

Can I make a frontend such as /items/${id}, and have HAProxy route to the correct backend server based on ${id}? So by looking up what id is associated with what server?

If not, would Nginx be able to do this?

kvz
  • 402
  • 4
  • 14
  • 2
    Related: http://serverfault.com/questions/323733/haproxy-url-introspection – HBruijn Jan 09 '15 at 10:50
  • I guess Nginx could do it via Lua? http://sosedoff.com/2012/06/11/dynamic-nginx-upstreams-with-lua-and-redis.html – kvz Jan 09 '15 at 10:50
  • It is not really in keeping with the performance aims of a load balancer to try and include DB lookups on every connection. A better way to go about the problem would be to structure your requests to indicate the divide between fairly static groups of backends. – JamesRyan Jan 09 '15 at 11:23

2 Answers2

6

You can use maps to make it work.

This is perfectly outlined in a post on the HAProxy.com blog. They're mapping based on the Host header, but it would be trivial to change the config to work for the URL.

One extra benefit here is that you can dynamically add and remove mapping entries using the http-[request|response] [set-map|del-map] keywords, or via the admin socket.
Changes made with http-[request|response]or via the socket aren't persisted between reboots, so you'd want to have some out of band process to update the map file at the same time, but that's another question all together.

Assuming you have a backend for each server, and given a map file called /etc/haproxy/items.map with these contents:

#itemPath    backendname
/item/a      bk_server01
/item/b      bk_server02
/item/c      bk_server02

You would do something like this in your frontend:

frontend ft_items
  [...]
  use_backend %[path,lower,map(/etc/haproxy/items.map,bk_default)]

If you have both servers in the same backend, you'd write an ACL in the front-end that catches all the items (acl items path_beg /items), send them to the backend (use_backend servers if items) and replace the use_backend line from the frontend with appropriately if-claused use_server lines in the backend.

GregL
  • 9,030
  • 2
  • 24
  • 35
  • Isn't stick-tables meant for this purpose? – Karthik Murugan Jun 04 '19 at 09:13
  • 1
    No, stick tables are mostly meant to store data for server persistence, aka sticky sessions. – GregL Jun 04 '19 at 10:29
  • would this work if i had 100000 items and both them and the servers change often? A realtime lookup would be best, but i'll settle for something that is fast to pick up on changes. I think the proposed solution is probably not intended for such a noisy use case? – kvz Mar 18 '21 at 13:38
  • Yeah, that seems like a bad idea. I’d find a way to break it down more so that you have less items in your map. That said, I’ve got to assume that 100000 routes of going to be hard for any reverse proxy to handle properly. – GregL Mar 18 '21 at 13:44
5

Yes, you can easily do this with HAProxy. Simply create an ACL with a list of IDs served by server and apply it to a use-server directive. IE:

acl server1-ids path_beg -i /items/id1 /istems/id2
use-server server1 if server1-ids
kasperd
  • 29,894
  • 16
  • 72
  • 122
Baptiste
  • 306
  • 1
  • 2