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方法,去构造新的请求地址,这样整个请求过程就结束了。

 

posted @ 2019-10-07 22:21  myTang  阅读(1702)  评论(0)    收藏  举报