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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM