Setting the route programmatically in Spring Cloud Netflix Zuul

最后都变了- 提交于 2019-12-09 18:38:01

问题


I have created two AWS Beanstalk envs, each with their own version of the applications. The urls for these envs are https://beta.myserver.com/v1073 and https://beta.myserver.com/v1084. These urls point to the load balancer.

Now I also have a Zuul implementation that have the following configurations.

    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

Request for my application must have a version header. I have a Zuul "pre" filter that inspect this header value. The goal is to route the request based on the header value.

I have been able to intercept the request, but have not been able to route it from the filter. The code snippet is below. After running the code the request still tries to go 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;
}

Any suggestion how to resolve this issue?


回答1:


What you really need to do is that changing requestURI, not server location in your case. You can easily do that like below.

First, your filter's order should be bigger than PreDecorationFilter's order that is currently 5 in Dalston release. (You need to check the value from the release you're using). PreDecorationFilter processes request header and fill necessary info into RequestContext. And this RequestContext will be used to define actual URL for your request. requestURI is the key that you need to change. In you case, you can append or modify requestURI based on headers. The below is snippet for your pre-filter.

@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;
}

Your original implementation seems to have some problems. First, you're trying to change location in ZuulProperties route. This object is shared for all requests. If you change it just for a specific request, other request that may have different headers could be routed to wrong location.

Second, you're setting location property in ZuulRoute object like below.

currentRoute.setLocation("https://beta.myserver.com/v1073");

Originally location property value is your service-id -beta-root - with your configuration. If you change it with a specific url that has 'http' or 'httpsprefix, 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`.


Update : To route different hosts based on Http Header

If you want to route to different hosts based on http headers, there are several ways to do that.

Case 1: In case that you are using Ribbon (and RibbonRoutingFilter)

The following feature only works on Edgware.SR1 and later version. From Eddware.SR1, you can put specify value for loadbalancer called FilterConstants.LOAD_BALANCER_KEY in RequestContext. This value will be passed into Ribbon load balancer. You can put any value(any object) that you want in your prefilter. If you want to change route based on a special http header, you can do that in your custom prefilter. And then define your own IRule implementation for Ribbon.LOAD_BALANCER_KEY will be given to your IRule implementation. Therefore you can choose the specific server from the list of server that Ribbon has based on the value of LOAD_BALANCER_KEY that you set.

You can find brief documentation here.

You can find sample code from the test case in PR. (RibbonRoutingFilterLoadBalancerKeyIntegrationTests.java)

Case 2: In case that you are using SimpleHostRoutingFilter (without Ribbon)

If you specify url instead of serviceId in zuul's route properties, the request will be routed by SimpleHostRoutingFilter without Ribbon.

SimpleHostRoutingFilter just use the host address by the below code

RequestContext.getCurrentContext().getRouteHost();

This value is set by PreDecorationFilter. So you can change this info in your prefilter.

  1. Make your own custom prefilter that has the order value between PreDecorationFilter's and SimpleHostRoutingFilter's.
  2. Inside your filter, check route host. If it is any known host that you want to change it based on HTTP header, change the route host via RequestContext.getCurrentContext().setRouteHost based on Http Header.

In case of the second approach, I didn't try to do that by myself. It's just theoretical solution that I think. The problem of the second approach is that it is using SimpleHostRoutingFilter. SimpleHostRoutingFilter doesn't make any HystrixCommand for the request, so you can't use any circuit breaker features that is provided by Hystrix inside Zuul. If you are using Edgware release, the first approach is better as I think.



来源:https://stackoverflow.com/questions/43740659/setting-the-route-programmatically-in-spring-cloud-netflix-zuul

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!