SpringCloud之从Zuul网关调用下游微服务的过程
Zuul网关两个核心过滤器:PreDecorationFilter,RibbonRoutingFilter
一、先看PreDecorationFilter过滤器:
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
//通过请求地址匹配到路由
Route route = this.routeLocator.getMatchingRoute(requestURI);
//配置由哪个route过滤器处理的逻辑
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(
this.properties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(
route.getSensitiveHeaders().toArray(new String[0]));
}
if (route.getRetryable() != null) {
ctx.put(RETRYABLE_KEY, route.getRetryable());
}
if (location.startsWith(HTTP_SCHEME + ":")
|| location.startsWith(HTTPS_SCHEME + ":")) {
ctx.setRouteHost(getUrl(location));
ctx.addOriginResponseHeader(SERVICE_HEADER, location);
}
else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
ctx.set(FORWARD_TO_KEY,
StringUtils.cleanPath(
location.substring(FORWARD_LOCATION_PREFIX.length())
+ route.getPath()));
ctx.setRouteHost(null);
return null;
}
else {
// set serviceId for use in filters.route.RibbonRequest
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
}
if (this.properties.isAddProxyHeaders()) {
addProxyHeaders(ctx, route);
String xforwardedfor = ctx.getRequest()
.getHeader(X_FORWARDED_FOR_HEADER);
String remoteAddr = ctx.getRequest().getRemoteAddr();
if (xforwardedfor == null) {
xforwardedfor = remoteAddr;
}
else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates
xforwardedfor += ", " + remoteAddr;
}
ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
}
if (this.properties.isAddHostHeader()) {
ctx.addZuulRequestHeader(HttpHeaders.HOST,
toHostHeader(ctx.getRequest()));
}
}
}
else {
}
return null;
}
这个过滤器主要做了两件事:
1、通过请求地址匹配到路由
2、设置由哪个Route过滤器处理的信息到Zuul上下文
二、我们这里只看复杂的RibbonRoutingFilter过滤器
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
&& ctx.sendZuulResponse());
}
这里面我们可以看到getRouteHost()==null并且由ServiceId的则有该过滤器处理,然后我们看过滤器的Run方法
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
这里面主要看到最核心的forward方法:
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
ClientHttpResponse response = command.execute();
this.helper.appendDebug(info, response.getRawStatusCode(),
response.getHeaders());
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
}
}
这里面主要是this.ribbonCommandFactory.create和command.execute是最关键代码
1、我们先看this.ribbonCommandFactory.create,可以看到RibbonCommandFactory的实现主要是如下三种:

对应的Create方法分别如下:
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
FallbackProvider zuulFallbackProvider = getFallbackProvider(
context.getServiceId());
final String serviceId = context.getServiceId();
final RibbonLoadBalancingHttpClient client = this.clientFactory
.getClient(serviceId, RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties,
zuulFallbackProvider, clientFactory.getClientConfig(serviceId));
}
@Override
public OkHttpRibbonCommand create(final RibbonCommandContext context) {
final String serviceId = context.getServiceId();
FallbackProvider fallbackProvider = getFallbackProvider(serviceId);
final OkHttpLoadBalancingClient client = this.clientFactory.getClient(serviceId,
OkHttpLoadBalancingClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties,
fallbackProvider, clientFactory.getClientConfig(serviceId));
}
@Override
@SuppressWarnings("deprecation")
public RestClientRibbonCommand create(RibbonCommandContext context) {
String serviceId = context.getServiceId();
FallbackProvider fallbackProvider = getFallbackProvider(serviceId);
RestClient restClient = this.clientFactory.getClient(serviceId, RestClient.class);
return new RestClientRibbonCommand(context.getServiceId(), restClient, context,
this.zuulProperties, fallbackProvider,
clientFactory.getClientConfig(serviceId));
}
三个实现的唯一区别是:
HttpClientRibbonCommand对应的是RibbonLoadBalancingHttpClient,RibbonLoadBalancingHttpClient是在spring-cloud-netflix-ribbon包下的apache目录下;
OkHttpRibbonCommand 对应的是OkHttpLoadBalancingClient,OkHttpLoadBalancingClient是在spring-cloud-netflix-ribbon包下的okhttp目录下;
RestClientRibbonCommand对应的RestClient,RestClient是在ribbon-httpclient包下的niws.client.http目录下;
2、command.execute()方法最后会执行到如下AbstractRibbonCommand的run方法,如下:
@Override
protected ClientHttpResponse run() throws Exception {
final RequestContext context = RequestContext.getCurrentContext();
RQ request = createRequest();
RS response;
boolean retryableClient = this.client instanceof AbstractLoadBalancingClient
&& ((AbstractLoadBalancingClient) this.client)
.isClientRetryable((ContextAwareRequest) request);
if (retryableClient) {
response = this.client.execute(request, config);
}
else {
response = this.client.executeWithLoadBalancer(request, config);
}
context.set("ribbonResponse", response);
// Explicitly close the HttpResponse if the Hystrix command timed out to
// release the underlying HTTP connection held by the response.
//
if (this.isResponseTimedOut()) {
if (response != null) {
response.close();
}
}
return new RibbonHttpResponse(response);
}
我们看到里面的核心方法是createRequest、this.client.execute、this.client.executeWithLoadBalancer,其中this.client就是前面RibbonLoadBalancingHttpClient、OkHttpLoadBalancingClient、RestClient这三个,这里我们主要看核心的executeWithLoadBalancer方法:
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
//这里从负载均衡器中获取到新的请求地址
URI finalUri = reconstructURIWithServer(server, request.getUri());
//替换成新的地址
S requestForServer = (S) request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
}
}
我们可以从LoadBalancerCommand的submit方法看到server是从负载均衡器获得到一台服务信息,然后调用了reconstructURIWithServer方法,去构造新的请求地址,这样整个请求过程就结束了。

浙公网安备 33010602011771号