问题
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, 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`.
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.
- Make your own custom prefilter that has the order value between PreDecorationFilter's and SimpleHostRoutingFilter's.
- 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