Настройка маршрута программно в 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, originalRibbonRoutingFilterwill not work. Instead,SimpleRoutingHostFilterwill process your request. It means that your not-modified requests will be handled byRibbonRoutingFilterand modified requests will be handled bySimpleHostRoutingFilter`.


Обновление: для маршрутизации различных хостов на основе заголовка 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. Таким образом, вы можете изменить эту информацию в вашем предварительном фильтре.

  1. Создайте свой собственный префильтр со значением порядка между PreDecorationFilter и SimpleHostRoutingFilter.
  2. Внутри вашего фильтра проверьте хост маршрута. Если это какой-либо известный хост, который вы хотите изменить на основе заголовка HTTP, измените маршрутный хост с помощью RequestContext.getCurrentContext().setRouteHost на основе HTTP-заголовка.

В случае второго подхода я не пытался сделать это сам. Я думаю, это просто теоретическое решение. Проблема второго подхода заключается в том, что он использует SimpleHostRoutingFilter. SimpleHostRoutingFilter не создает HystrixCommand для запроса, поэтому вы не можете использовать функции автоматического выключателя, предоставляемые Hystrix в Zuul. Если вы используете релиз Edgware, первый подход лучше, чем я думаю.

Другие вопросы по тегам