zuul网关源码解析

zuul网关源码解析

zuul请求的生命周期

image_1ca2nppk7gn218991jjh3aing79.png-48.2kB

ZuulServlet

ZuulServlet定义了对zuul整个过程的处理,如下:

public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    try {
        init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

        // Marks this request as having passed through the "Zuul engine", as opposed to servlets
        // explicitly bound in web.xml, for which requests will not have the same data attached
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();

        try {
            preRoute();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            route();
        } catch (ZuulException e) {
            error(e);
            postRoute();
            return;
        }
        try {
            postRoute();
        } catch (ZuulException e) {
            error(e);
            return;
        }

    } catch (Throwable e) {
        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
    } finally {
        RequestContext.getCurrentContext().unset();
    }
}

PRE阶段

PreDecorationFilter过滤器寻找路由,如下图:

image_1ca2qif8j1psa131flf41b7n1su24j.png-106.3kB

当得到匹配的路由后,装饰RequestContext往请求内容中添加路径等路由信息。

ROUTE阶段

RibbonRoutingFilter真正的对服务发起请求,并得到响应结果

image_1ca2qk10ujk613usl4g1nbj1toq50.png-82.7kB

run()方法

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()方法通过RibbonCommand实现对服务的调用

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.getStatusCode().value(),
				response.getHeaders());
		return response;
	}
	catch (HystrixRuntimeException ex) {
		return handleException(info, ex);
	}

}

setResponse()方法将响应内容写入RequestContext

protected void setResponse(ClientHttpResponse resp)
		throws ClientException, IOException {
	RequestContext.getCurrentContext().set("zuulResponse", resp);
	this.helper.setResponse(resp.getStatusCode().value(),
			resp.getBody() == null ? null : resp.getBody(), resp.getHeaders());
}

ROUTE还有两个过滤器SendForwardFilter(forward请求转发)SimpleHostRoutingFilter(url请求转发),根据不同的路由类型匹配相应的过滤器。

POST阶段

SendResponseFilter对内容进行响应

image_1ca2oi1kv6g61q3a9p2n4cnaq2j.png-69.3kB

run()方法

public Object run() {
	try {
		addResponseHeaders();
		//将response输出
		writeResponse();
	}
	catch (Exception ex) {
		ReflectionUtils.rethrowRuntimeException(ex);
	}
	return null;
}

writeResponse()方法,从RequestContext中获取response并输出

private void writeResponse() throws Exception {
	RequestContext context = RequestContext.getCurrentContext();
	// there is no body to send
	if (context.getResponseBody() == null
			&& context.getResponseDataStream() == null) {
		return;
	}
	HttpServletResponse servletResponse = context.getResponse();
	if (servletResponse.getCharacterEncoding() == null) { // only set if not set
		servletResponse.setCharacterEncoding("UTF-8");
	}
	OutputStream outStream = servletResponse.getOutputStream();
	InputStream is = null;
	try {
		if (RequestContext.getCurrentContext().getResponseBody() != null) {
			String body = RequestContext.getCurrentContext().getResponseBody();
			writeResponse(
					new ByteArrayInputStream(
							body.getBytes(servletResponse.getCharacterEncoding())),
					outStream);
			return;
		}
		boolean isGzipRequested = false;
		final String requestEncoding = context.getRequest()
				.getHeader(ZuulHeaders.ACCEPT_ENCODING);

		if (requestEncoding != null
				&& HTTPRequestUtils.getInstance().isGzipped(requestEncoding)) {
			isGzipRequested = true;
		}
		is = context.getResponseDataStream();
		InputStream inputStream = is;
		if (is != null) {
			if (context.sendZuulResponse()) {
				if (context.getResponseGZipped() && !isGzipRequested) {
					// If origin tell it's GZipped but the content is ZERO bytes,
					// don't try to uncompress
					final Long len = context.getOriginContentLength();
					if (len == null || len > 0) {
						try {
							inputStream = new GZIPInputStream(is);
						}
						catch (java.util.zip.ZipException ex) {
							log.debug(
									"gzip expected but not "
											+ "received assuming unencoded response "
											+ RequestContext.getCurrentContext()
											.getRequest().getRequestURL()
											.toString());
							inputStream = is;
						}
					}
					else {
						// Already done : inputStream = is;
					}
				}
				else if (context.getResponseGZipped() && isGzipRequested) {
					servletResponse.setHeader(ZuulHeaders.CONTENT_ENCODING, "gzip");
				}
				writeResponse(inputStream, outStream);
			}
		}
	}
	finally {
		
		if (is != null) {
			try {
				is.close();
			}
			catch (Exception ex) {
				log.warn("Error while closing upstream input stream", ex);
			}
		}

		try {
			Object zuulResponse = RequestContext.getCurrentContext()
					.get("zuulResponse");
			if (zuulResponse instanceof Closeable) {
				((Closeable) zuulResponse).close();
			}
			outStream.flush();
			// The container will close the stream for us
		}
		catch (IOException ex) {
			log.warn("Error while sending response to client: " + ex.getMessage());
		}
	}
}

ERROR阶段

当PRE、ROUTE、POST阶段的过滤器发生错误时,会调用ERROR过滤器。默认的error过滤器有 SendErrorFilter

原文地址:https://www.cnblogs.com/liangzs/p/8695397.html