core:Java功能增強 ——過濾器
微服務(wù)中引入API網(wǎng)關(guān)服務(wù)的必要性:
1、首先微服務(wù)架構(gòu)中服務(wù)單獨細化并存在多個實例,那就有必要維護服務(wù)實例列表和路由規(guī)則等等,系統(tǒng)越大越復(fù)雜越難維護(當然服務(wù)實例列表可以從Eureka中獲取)。那如果用網(wǎng)關(guān)來實現(xiàn)請求路由與負載均衡呢?
2、服務(wù)中經(jīng)常會有一些權(quán)限校驗機制等,而且每個服務(wù)都要有。最原始的就是在每個服務(wù)中都寫校驗代碼,改進一點就是把校驗機制做成一個單獨的服務(wù),但即使是這樣,每個服務(wù)都需要自己去調(diào)用一下校驗服務(wù)。如果使用在網(wǎng)關(guān)中寫校驗機制,由它統(tǒng)一過濾呢?
過濾機制
Zuul最重要的應(yīng)該屬于它的過濾機制,zuul使用的不是web容器的Filter,是自己實現(xiàn)的Filter,具體如下:
先定義一個servlet:
service方法如下:
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute(); ******************
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.route(); ******************
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.postRoute(); ******************
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
void postRoute() throws ZuulException {
this.zuulRunner.postRoute();
}
void route() throws ZuulException {
this.zuulRunner.route();
}
void preRoute() throws ZuulException {
this.zuulRunner.preRoute();
}
再使用定義的Runner來執(zhí)行自己的Filter:
public void preRoute() throws ZuulException {
try {
this.runFilters("pre");
} catch (ZuulException var2) {
throw var2;
} catch (Throwable var3) {
throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
}
}
而所有的Filter是放在一個List里的,過濾出對應(yīng)的Filter執(zhí)行:
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 = (ZuulFilter)list.get(i);
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
在processZuulFilter函數(shù)中會執(zhí)行filter.runFilter()。
thinking:Javaweb應(yīng)用中請求都是由servlet進行處理的,Spring中servlet會將請求轉(zhuǎn)發(fā)到對應(yīng)的controller。而Filter是在請求被Servlet處理之前攔截請求進行過濾的。在Zuul中這個Filter實現(xiàn)是定義了一個Servlet,在Servlet中調(diào)用自己的過濾器,通過過濾的請求會接著走后面的流程。
小問題:如果是SpringBoot項目,默認有一個DispatcherServlet,如果再定義一個自己的Servlet,那這兩個Servlet是怎么執(zhí)行的,是先執(zhí)行我們自己定義的?然后再走DispatcherServlet?
答:試了一下,當存在多個Servlet時,只會執(zhí)行url最精確的Servlet,比如MyServlet的url是“/hello”,那DispatcherServlet就不會執(zhí)行了(默認是“/”)。
(在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通過 @WebServlet、@WebFilter、@WebListener 注解自動注冊)
那問題來了。如果只會執(zhí)行一個Servlet,那么框架自定義的Servlet處理后,請求要怎么轉(zhuǎn)給DispatcherServlet再轉(zhuǎn)給Controlller呢?
答:這里是網(wǎng)關(guān),沒有Controller啊,當然DispatcherServlet也會執(zhí)行,很奇怪,但是好像它執(zhí)不執(zhí)行沒有影響?應(yīng)該是有個操作向server轉(zhuǎn)發(fā)請求(過濾通過后),那這個操作在哪呢?
zuul有三大類過濾器,分別是pre、route、post。對應(yīng)著請求前,請求中和請求后。所以向服務(wù)實例轉(zhuǎn)發(fā)請求應(yīng)該是在route過濾器中