zuul的核心邏輯都是由一系列filter過濾器鏈實現的,但是filter的類型不同,執行的時機也不同,效果自然也不一樣,主要特點如下: zuul內部有一套完整的機制,可以動態讀取編譯運行filter機制,filter與filter之間不直接通信,在請求線程中會通過RequestContext來共用 ...
zuul的核心邏輯都是由一系列filter過濾器鏈實現的,但是filter的類型不同,執行的時機也不同,效果自然也不一樣,主要特點如下:
- filter的類型:filter的類型,決定了它在整個filter鏈中的執行順序,可能在端點路由前執行,也可能在端點路由時執行,還有可能在端點路由後執行,甚至是端點路由發生異常時執行。
- filter的執行順序:同一種類型的filter,可以通過filterOrder()方法設置執行順序,一般都是根據業務場景自定義filter執行順序。
- filter執行條件:filter運行所需的標準,或條件。
- filter執行效果:符合某個filter執行條件,產生執行效果。
zuul內部有一套完整的機制,可以動態讀取編譯運行filter機制,filter與filter之間不直接通信,在請求線程中會通過RequestContext來共用狀態,它內部是用ThreadLocal實現的,例如HttpServletRequest、HttpServletResponse、異常信息等。部分源碼如下:
public class RequestContext extends ConcurrentHashMap<String, Object> { private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class); protected static Class<? extends RequestContext> contextClass = RequestContext.class; private static RequestContext testContext = null; protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() { @Override protected RequestContext initialValue() { try { return contextClass.newInstance(); } catch (Throwable e) { throw new RuntimeException(e); } } };
//.......
}
zuul中不同類型的filter執行邏輯的核心在ZuulServlet類中,主要代碼如下:
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(); //如果preRoute方法在執行的時候出現異常,直接就拋出500異常,不會走catch中的error方法,見下圖FilterProcessor類中的preRoute方法。 } catch (ZuulException e) { error(e); //如果preRoute在執行過程中,拋出Zuul異常,這裡被捕捉到以後,會執行error方法,列印堆棧信息,見下圖FilterProcessor類中的error方法。 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(); } }
//....... }
zuul一共有4種不同的生命周期:
- pre:在zuul網關按照規則路由到下級服務之前執行,如果需要對請求進行預處理,可以使用這種類型的過濾器。如:認證鑒權,限流等。
- route:這種過濾器是zuul路由動作的執行者,是Apache HttpClient或Ribbon構建和發送原始HTTP請求的地方,現在也支持OKHTTP。
- post:這種過濾器是在端點請求完畢,返回結果或者發生異常後執行的filter。如果需要對返回的結果進行再次處理,可以在這種過濾中處理邏輯。
- error: 這種過濾器是在整個生命周期內,如果發生異常,就執行該filter,可以做全局異常處理。