Настройка маршрута программно в Spring Cloud Netflix Zuul
Я создал два окружения AWS Beanstalk, каждый со своей версией приложений. URL-адреса этих envs: https://beta.myserver.com/v1073 и https://beta.myserver.com/v1084. Эти URL-адреса указывают на балансировщик нагрузки.
Теперь у меня также есть реализация Zuul, которая имеет следующие конфигурации.
zuul:
routes:
beta:
path: /api/**
serviceId: beta-root
strip-prefix: false
sensitive-headers: Cookie,Set-Cookie
ribbon:
eureka:
enabled: false
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
beta-root:
ribbon:
listOfServers: https://beta.myserver.com
Запрос на мое приложение должен иметь заголовок версии. У меня есть "предварительный" фильтр Zuul, который проверяет это значение заголовка. Цель состоит в том, чтобы направить запрос на основе значения заголовка.
Я смог перехватить запрос, но не смог перенаправить его из фильтра. Фрагмент кода ниже. После выполнения кода запрос все еще пытается перейти на https://beta.myserver.com/api/...
@Override
public Object run() {
/* Logic to get version header etc */
/* Set the new route */
Map<String, ZuulRoute> routes = zuulProps.getRoutes();
ZuulRoute currentRoute = routes.get("beta-root");
currentRoute.setLocation("https://beta.myserver.com/v1073");
/* Refresh the route */
routeLocator.getRoutes();
logger.warn("Current Route:" + currentRoute.getLocation());
return null;
}
Любое предложение, как решить эту проблему?
1 ответ
Что вам действительно нужно сделать, это это изменение requestURI
, а не местоположение сервера в вашем случае. Вы можете легко сделать это, как показано ниже.
Во-первых, порядок вашего фильтра должен быть больше, чем PreDecorationFilter
Порядок, который в настоящее время 5 в выпуске Dalston. (Вам нужно проверить значение из выпуска, который вы используете).PreDecorationFilter
обрабатывает заголовок запроса и заполняет необходимую информацию RequestContext
, И это RequestContext
будет использоваться для определения фактического URL для вашего запроса. requestURI
это ключ, который вам нужно изменить. В вашем случае вы можете добавить или изменить requestURI на основе заголовков. Ниже приведен фрагмент вашего предварительного фильтра.
@Override
public int filterOrder() {
return 6;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// override request URI
ctx.set("requestURI", "/v1073" + ctx.get("requestURI"));
return null;
}
Кажется, у вашей оригинальной реализации есть некоторые проблемы. Сначала вы пытаетесь изменить местоположение в маршруте ZuulProperties. Этот объект является общим для всех запросов. Если вы измените его только для определенного запроса, другой запрос, который может иметь другие заголовки, может быть перенаправлен в неправильное местоположение.
Во-вторых, вы устанавливаете свойство местоположения в ZuulRoute
объект как ниже.
currentRoute.setLocation("https://beta.myserver.com/v1073");
Первоначально значением свойства location является ваш идентификатор службы -beta-root
- с вашей конфигурацией. Если вы измените его с определенным URL, который имеет "http" или "https"prefix, original
RibbonRoutingFilterwill not work. Instead,
SimpleRoutingHostFilterwill process your request. It means that your not-modified requests will be handled by
RibbonRoutingFilterand modified requests will be handled by
SimpleHostRoutingFilter`.
Обновление: для маршрутизации различных хостов на основе заголовка HTTP
Если вы хотите маршрутизировать к разным хостам на основе заголовков http, есть несколько способов сделать это.
Случай 1: в случае, если вы используете Ribbon (и RibbonRoutingFilter)
Следующая функция работает только в Edgware.SR1 и более поздних версиях. В Eddware.SR1 вы можете указать заданное значение для loadbalancer с именем FilterConstants.LOAD_BALANCER_KEY
в RequestContext
, Это значение будет передано в балансировщик нагрузки ленты. Вы можете поместить любое значение (любой объект) в свой префильтр. Если вы хотите изменить маршрут на основе специального http-заголовка, вы можете сделать это в своем предварительном фильтре. А затем определите свой собственный IRule
реализация для ленты.LOAD_BALANCER_KEY
будет дан вашему IRule
реализация. Поэтому вы можете выбрать конкретный сервер из списка серверов, который имеет лента на основе значения LOAD_BALANCER_KEY
что вы установили.
Вы можете найти краткую документацию здесь.
Вы можете найти образец кода из теста в PR. (RibbonRoutingFilterLoadBalancerKeyIntegrationTests.java)
Случай 2: в случае, если вы используете SimpleHostRoutingFilter (без ленты)
Если вы укажете url
вместо serviceId
в свойствах маршрута zuul запрос будет перенаправлен SimpleHostRoutingFilter без ленты.
SimpleHostRoutingFilter просто используйте адрес хоста по приведенному ниже коду
RequestContext.getCurrentContext().getRouteHost();
Это значение устанавливается PreDecorationFilter. Таким образом, вы можете изменить эту информацию в вашем предварительном фильтре.
- Создайте свой собственный префильтр со значением порядка между PreDecorationFilter и SimpleHostRoutingFilter.
- Внутри вашего фильтра проверьте хост маршрута. Если это какой-либо известный хост, который вы хотите изменить на основе заголовка HTTP, измените маршрутный хост с помощью
RequestContext.getCurrentContext().setRouteHost
на основе HTTP-заголовка.
В случае второго подхода я не пытался сделать это сам. Я думаю, это просто теоретическое решение. Проблема второго подхода заключается в том, что он использует SimpleHostRoutingFilter. SimpleHostRoutingFilter не создает HystrixCommand для запроса, поэтому вы не можете использовать функции автоматического выключателя, предоставляемые Hystrix в Zuul. Если вы используете релиз Edgware, первый подход лучше, чем я думаю.