目錄
概述
zuul 是netflix開(kāi)源的一個(gè)API Gateway 服務(wù)器, 本質(zhì)上是一個(gè)web servlet應(yīng)用,下面通過(guò)源碼分析zuul的請(qǐng)求處理流程
Zuul請(qǐng)求的生命周期
如圖,該圖詳細(xì)描述了各種類型的過(guò)濾器的執(zhí)行順序

通過(guò)上圖可以看到zuul通過(guò)攔截servlet應(yīng)用http請(qǐng)求后進(jìn)入zuul定義的過(guò)濾器鏈表進(jìn)行處理路由;
一般我們開(kāi)發(fā)web應(yīng)用需要在請(qǐng)求進(jìn)來(lái)前進(jìn)行統(tǒng)一處理,通常有兩種方式
- 定義一個(gè)servlet Filter來(lái)在請(qǐng)求進(jìn)來(lái)前攔截處理,例如會(huì)話校驗(yàn),請(qǐng)求header校驗(yàn)等
- 定義一個(gè)統(tǒng)一servlet,所有請(qǐng)求進(jìn)來(lái)都走此servlet,一般mvc框架通過(guò)此方式實(shí)現(xiàn),例如springMvc的DispatcherServlet
zuul也不例外,zuul分別實(shí)現(xiàn)了這兩種攔截方式: ZuulServletFilter與ZuulServlet。
ZuulServletFilter
所在包:com.netflix.zuul.filters
public class ZuulServletFilter implements Filter {
//zuul核心運(yùn)行器
private ZuulRunner zuulRunner;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String bufferReqsStr = filterConfig.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
//創(chuàng)建
zuulRunner = new ZuulRunner(bufferReqs);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
//初始化當(dāng)前請(qǐng)求zuul的上下問(wèn)環(huán)境信息
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
//執(zhí)行前置過(guò)濾器列表
try {
preRouting();
} catch (ZuulException e) {
//執(zhí)行錯(cuò)誤過(guò)濾器列表
//執(zhí)行后置路由過(guò)濾器列表
error(e);
postRouting();
return;
}
//判斷響應(yīng)是否已經(jīng)發(fā)送,發(fā)送完成時(shí)不需要執(zhí)行下面路由列表流程
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
//執(zhí)行路由中過(guò)濾器列表
try {
routing();
} catch (ZuulException e) {
//執(zhí)行錯(cuò)誤過(guò)濾器列表
//執(zhí)行后置路由過(guò)濾器列表
error(e);
postRouting();
return;
}
//執(zhí)行后置路由過(guò)濾器列表
try {
postRouting();
} catch (ZuulException e) {
//執(zhí)行錯(cuò)誤過(guò)濾器列表
error(e);
return;
}
} catch (Throwable e) {
//執(zhí)行錯(cuò)誤過(guò)濾器列表
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
void postRouting() throws ZuulException {
//zuul核心運(yùn)行器執(zhí)行后置過(guò)濾器列表
zuulRunner.postRoute();
}
void routing() throws ZuulException {
zuulRunner.route();
}
void preRouting() throws ZuulException {
zuulRunner.preRoute();
}
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}
void error(ZuulException e) {
//設(shè)置異常的上下文環(huán)境中
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
}
ZuulServlet
所在包:com.netflix.zuul.http
public class ZuulServlet extends HttpServlet {
private static final long serialVersionUID = -3374242278843351500L;
private ZuulRunner zuulRunner;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
zuulRunner = new ZuulRunner(bufferReqs);
}
@Override
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();
}
}
/**
* executes "post" ZuulFilters
*
* @throws ZuulException
*/
void postRoute() throws ZuulException {
zuulRunner.postRoute();
}
/**
* executes "route" filters
*
* @throws ZuulException
*/
void route() throws ZuulException {
zuulRunner.route();
}
/**
* executes "pre" filters
*
* @throws ZuulException
*/
void preRoute() throws ZuulException {
zuulRunner.preRoute();
}
/**
* initializes request
*
* @param servletRequest
* @param servletResponse
*/
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}
/**
* sets error context info and executes "error" filters
*
* @param e
*/
void error(ZuulException e) {
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
}
ZuulServlet與ZuulServletFilter完成了相同的功能
- 初始化時(shí)創(chuàng)建ZuulRunner
- 請(qǐng)求進(jìn)入調(diào)用ZuulRunner.init初始化當(dāng)前請(qǐng)求的上下文環(huán)境
- 按規(guī)則執(zhí)行zuul的過(guò)濾器列表:
- 正常情況 :preRoute()->route()->postRoute()
- preRoute異常:preRoute()->error()->postRoute()
- route異常:route()->error()->postRoute()
- postRoute異常:postRoute()->error()
ZuulRunner
ZuulRunner將servlet請(qǐng)求和響應(yīng)初始化到請(qǐng)求上下文中,并封裝過(guò)濾器處理器調(diào)用preRoute()、Router()、postRoute()和Error()方法
public class ZuulRunner {
private boolean bufferRequests;
/**
* Creates a new <code>ZuulRunner</code> instance.
*/
public ZuulRunner() {
this.bufferRequests = true;
}
/**
*
* @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body.
*/
public ZuulRunner(boolean bufferRequests) {
this.bufferRequests = bufferRequests;
}
/**
* sets HttpServlet request and HttpResponse
*
* @param servletRequest
* @param servletResponse
*/
public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
RequestContext ctx = RequestContext.getCurrentContext();
if (bufferRequests) {
ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
} else {
ctx.setRequest(servletRequest);
}
ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
/**
* executes "post" filterType ZuulFilters
*
* @throws ZuulException
*/
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
/**
* executes "route" filterType ZuulFilters
*
* @throws ZuulException
*/
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
/**
* executes "pre" filterType ZuulFilters
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
/**
* executes "error" filterType ZuulFilters
*/
public void error() {
FilterProcessor.getInstance().error();
}
}
ZuulRunner 是提供給請(qǐng)求的核心處理類,主要完成下面事件
- 初始化request,response到上下問(wèn)環(huán)境
- 將 FilterProcessor的過(guò)濾器調(diào)用暴露出去
FilterProcessor
FilterProcessor是執(zhí)行過(guò)濾器的核心類
public class FilterProcessor {
static FilterProcessor INSTANCE = new FilterProcessor();
protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);
private FilterUsageNotifier usageNotifier;
public FilterProcessor() {
usageNotifier = new BasicFilterUsageNotifier();
}
/**
* @return the singleton FilterProcessor
*/
public static FilterProcessor getInstance() {
return INSTANCE;
}
/**
* sets a singleton processor in case of a need to override default behavior
*
* @param processor
*/
public static void setProcessor(FilterProcessor processor) {
INSTANCE = processor;
}
/**
* Override the default filter usage notification impl.
*
* @param notifier
*/
public void setFilterUsageNotifier(FilterUsageNotifier notifier) {
this.usageNotifier = notifier;
}
/**
* 按順序執(zhí)行類型為post的過(guò)濾器列表(后置過(guò)濾器)
* runs "post" filters which are called after "route" filters. ZuulExceptions from ZuulFilters are thrown.
* Any other Throwables are caught and a ZuulException is thrown out with a 500 status code
*
* @throws ZuulException
*/
public void postRoute() throws ZuulException {
try {
runFilters("post");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
}
}
/**
* 按順序執(zhí)行類型為error的過(guò)濾器列表(錯(cuò)誤過(guò)濾器)
* runs all "error" filters. These are called only if an exception occurs. Exceptions from this are swallowed and logged so as not to bubble up.
*/
public void error() {
try {
runFilters("error");
} catch (Throwable e) {
logger.error(e.getMessage(), e);
}
}
/**
* 按順序執(zhí)行類型為route的過(guò)濾器列表(路由過(guò)濾器)
* Runs all "route" filters. These filters route calls to an origin.
*
* @throws ZuulException if an exception occurs.
*/
public void route() throws ZuulException {
try {
runFilters("route");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
}
}
/**
* 按順序執(zhí)行類型為pre的過(guò)濾器列表(前置過(guò)濾器)
* runs all "pre" filters. These filters are run before routing to the orgin.
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}
/**
* 按順序執(zhí)行給定類型的過(guò)濾器列表
* 通過(guò)FilterLoader加載所有給定類型type的過(guò)濾器
* 循環(huán)遍歷過(guò)濾器列表依次執(zhí)行
* runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
*
* @param sType the filterType.
* @return
* @throws Throwable throws up an arbitrary exception
*/
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
/**
* Processes an individual ZuulFilter. This method adds Debug information. Any uncaught Thowables are caught by this method and converted to a ZuulException with a 500 status code.
*
* @param filter
* @return the return value for that filter
* @throws ZuulException
*/
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
final String metricPrefix = "zuul.filter-";
long execTime = 0;
String filterName = "";
try {
long ltime = System.currentTimeMillis();
filterName = filter.getClass().getSimpleName();
RequestContext copy = null;
Object o = null;
Throwable t = null;
if (bDebug) {
Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
copy = ctx.copy();
}
ZuulFilterResult result = filter.runFilter();
ExecutionStatus s = result.getStatus();
execTime = System.currentTimeMillis() - ltime;
switch (s) {
case FAILED:
t = result.getException();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
break;
case SUCCESS:
o = result.getResult();
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
if (bDebug) {
Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
Debug.compareContextState(filterName, copy);
}
break;
default:
break;
}
if (t != null) throw t;
usageNotifier.notify(filter, s);
return o;
} catch (Throwable e) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
}
usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (e instanceof ZuulException) {
throw (ZuulException) e;
} else {
ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
/**
* Publishes a counter metric for each filter on each use.
*/
public static class BasicFilterUsageNotifier implements FilterUsageNotifier {
private static final String METRIC_PREFIX = "zuul.filter-";
@Override
public void notify(ZuulFilter filter, ExecutionStatus status) {
DynamicCounter.increment(METRIC_PREFIX + filter.getClass().getSimpleName(), "status", status.name(), "filtertype", filter.filterType());
}
}
}
FilterProcessor 通過(guò)FilterLoader加載給定類型的過(guò)濾器列表,然后按順序依次執(zhí)行過(guò)濾器,根據(jù)過(guò)濾器的類型包含如下方法:
- preRoute:按順序執(zhí)行類型為pre的過(guò)濾器列表(前置過(guò)濾器)
- route:按順序執(zhí)行類型為route的過(guò)濾器列表(路由過(guò)濾器)
- postRoute:按順序執(zhí)行類型為post的過(guò)濾器列表(后置過(guò)濾器)
- error:按順序執(zhí)行類型為error的過(guò)濾器列表(錯(cuò)誤過(guò)濾器)
到此,zuul攔截servlet應(yīng)用http請(qǐng)求后通過(guò)過(guò)濾器列表進(jìn)行處理路由的整個(gè)流程以及代碼都查看完畢,對(duì)后期我們根據(jù)業(yè)務(wù)改造還是自定義過(guò)濾器有很大的指導(dǎo)意義。
備注 過(guò)濾器分類:
- pre:前置過(guò)濾器
- route:路由過(guò)濾器
- post:后置過(guò)濾器
- error:錯(cuò)誤過(guò)濾器