ZuulFilter的兩種初始化過程我們在前面已經(jīng)分析過柱徙,這一節(jié)我們也直奔主題缓屠,講講ZuulFilter初始化之后的調(diào)用過程,看看整個(gè)調(diào)用過程中Zuul是如何處理的护侮,都經(jīng)過了哪些步驟敌完?下面我們就以routing filters執(zhí)行過程進(jìn)行分析!
ZuulServletFilter
public class ZuulServletFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
......
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
......
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
//routing filters執(zhí)行階段羊初,請求下游服務(wù)在此階段執(zhí)行
void routing() throws ZuulException {
zuulRunner.route();
}
}
ZuulRunner
public class ZuulRunner {
......
//初始化滨溉,默認(rèn)直接使用HttpServletRequest輸入流,輸出則使用HttpServletResponseWrapper包裝流
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));
}
......
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
......
}
ZuulRunner這個(gè)對象類中我們看見了有HttpServletRequest长赞、HttpServletResponse晦攒、HttpServletRequestWrapper、HttpServletResponseWrapper四種類型
HttpServletRequest中參數(shù)是無法修改的得哆,HttpServletResponse中輸出流是無法讀取的脯颜,但是往往我們有很多需求是需要進(jìn)行請求參數(shù)的修改及響應(yīng)輸出流讀取的,這個(gè)時(shí)候就需要我們使用HttpServletRequestWrapper贩据、HttpServletResponseWrapper這兩個(gè)包裝類栋操,
HttpServletRequestWrapper作為HttpServletRequest的包裝類,主要職責(zé)就是可以替換HttpServletRequest中的參數(shù)
HttpServletResponseWrapper作為HttpServletResponse的包裝類饱亮,主要職責(zé)就是可以讀取HttpServletResponse中的輸出流矾芙!
FilterProcessor#route()
public class FilterProcessor {
......
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());
}
}
......
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
//根據(jù)filter類型獲取所有相關(guān)類型的ZuulFilter
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
//執(zhí)行ZuulFilter#runFilter()調(diào)用IZuulFilter#run()方法
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
......
}
從片段中我們也比較清晰的得知調(diào)用過程為:
1.根據(jù)filter類型獲取所有相關(guān)類型的ZuulFilter
2.執(zhí)行ZuulFilter的run()方法
那么我們看看如何獲取List<ZuulFilter>這個(gè)集合對象的。
FilterLoader#getFiltersByType()
public class FilterLoader {
......
public List<ZuulFilter> getFiltersByType(String filterType) {
//如果hashFiltersByType緩存對象中已經(jīng)有當(dāng)前filterType類型的ZuulFilter集合則使用緩存
List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;
list = new ArrayList<ZuulFilter>();
//獲取全局對象filterRegistry中保存的所有ZuulFilter
Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
//添加到list對象中
for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
ZuulFilter filter = iterator.next();
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
//根據(jù)filterOrder優(yōu)先級排序近上,默認(rèn)從小到大
Collections.sort(list);
//添加到hashFiltersByType對象中
hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
......
}
通過filterType類型獲取對應(yīng)的List<ZuulFilter>集合對象剔宪,其中筆者剛開始以為List<ZuulFilter> list = hashFiltersByType.get(filterType);這個(gè)地方有問題,因?yàn)閱为?dú)看這個(gè)地方會(huì)有一個(gè)問題,就是這個(gè)地方獲取出來如果有數(shù)據(jù)歼跟,那么就直接返回了和媳,那比如我動(dòng)態(tài)增加了一個(gè)Groovy文件并且類型相同的ZuulFilter,那這個(gè)地方看起來好想是有問題的哈街。
所以帶著疑問留瞳,我去仔細(xì)看了看初始化Groovy文件并初始化為ZuulFilter對象后添加到全局filterRegistry的過程,在FilterLoader#putFilter()這個(gè)方法中骚秦,有兩行非常重要的代碼她倘,List<ZuulFilter> list = hashFiltersByType.get(filter.filterType());
hashFiltersByType.remove(filter.filterType());這里如果有相同類型的,那么就直接就刪除了filterType對應(yīng)的List<ZuulFilter>集合數(shù)據(jù)作箍,在后面重新賦值硬梁,所以疑問迎刃而解!
FilterProcessor#processZuulFilter()
public class FilterProcessor {
......
//執(zhí)行ZuulFilter的run()邏輯
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
......
try {
......
//重點(diǎn):執(zhí)行ZuulFilter#runFilter()內(nèi)部會(huì)調(diào)用run()方法
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) {
......
}
}
......
}
在processZuulFilter這個(gè)方法體中荧止,會(huì)記錄每個(gè)ZuulFilter的耗時(shí)時(shí)間,以及執(zhí)行過程中的成功阶剑、失敗跃巡、異常的狀態(tài)都會(huì)通知給Netflix Servo 實(shí)現(xiàn)監(jiān)控信息采集(JMX -Java Management Extensions標(biāo)準(zhǔn)),監(jiān)控信息不過多分析(PS:開啟JMX牧愁,然后使用JConsole或Visual VM進(jìn)行預(yù)覽)
ZuulFilter#runFilter()
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
......
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
//默認(rèn)false素邪,及所有ZuulFilter都有效
if (!isFilterDisabled()) {
//是否滿足過濾條件
if (shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
//重點(diǎn):實(shí)現(xiàn)了ZuulFilter都會(huì)重寫這個(gè)方法,執(zhí)行具體的業(yè)務(wù)邏輯方法
Object res = run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable e) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(e);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
......
}
分析到這里猪半,執(zhí)行run()方法兔朦,因?yàn)槲覀兪欠治鰎oute 類型的filterType,并且是調(diào)用下游服務(wù)的ZuulFilter磨确,所以我們最終會(huì)執(zhí)行到RibbonRoutingFilter這個(gè)實(shí)現(xiàn)類沽甥,那這個(gè)實(shí)現(xiàn)類都做了什么事情呢?我們接著往下俐填!
RibbonRoutingFilter
public class RibbonRoutingFilter extends ZuulFilter {
......
//配置的routes路由為使用serviceId方式安接,非url配置方式
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return (ctx.getRouteHost() == null && ctx.get(SERVICE_ID_KEY) != null
&& ctx.sendZuulResponse());
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
//添加忽略的頭信息,zuul的ignored-headers配置,會(huì)過濾這些頭信息參數(shù)
this.helper.addIgnoredHeaders();
try {
//構(gòu)建Ribbon命令執(zhí)行的上下文,保存相關(guān)參數(shù)的對象
RibbonCommandContext commandContext = buildCommandContext(context);
//調(diào)用下游服務(wù)英融,返回結(jié)果
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
......
}
這里我們簡單解釋下在run()方法中經(jīng)過的步驟RibbonRoutingFilter(routing filters)->Hystrix->Ribbon-> Response盏檐。如果想了解routing filters->Hystrix->Ribbon-> Response整個(gè)詳細(xì)過程請參閱:Spring Cloud Hystrix 分析(四)之Zuul集成
- 構(gòu)建Ribbon命令執(zhí)行的上下文,保存相關(guān)參數(shù)的對象驶悟,封裝RibbonCommandContext對象(頭信息胡野、請求參數(shù)信息)
- 封裝HttpClientRibbonCommand(RibbonCommand)對象,設(shè)置負(fù)載均衡客戶端痕鳍,而且內(nèi)部繼承HystrixExecutable硫豆,可以使用Hystrix熔斷功能
- 執(zhí)行命令龙巨,最終調(diào)用到HystrixCommand#execute()
- AbstractRibbonCommand#run()方法中this.client.executeWithLoadBalancer(request, config);通過負(fù)載均衡客戶端進(jìn)行下游服務(wù)的調(diào)用并返回結(jié)果
文章到這里也接近尾聲,本節(jié)主要講解了route類型ZuulFilter的整個(gè)調(diào)用過程熊响,其他類型的也基本差不多的旨别,都是相同類型的ZuulFilter按照從小到大的優(yōu)先級進(jìn)行執(zhí)行,如果文章對你有所幫助汗茄,就點(diǎn)贊關(guān)注吧秸弛!