Zuul是Netflix開源的微服務網關,它能夠和Eureka,consul,Ribbon,Hystrix等組件配合使用,網上也有不少如何使用zuul的文章,咱們也在生產環境使用了,因此讀了下zuul的源碼,下面把它分享出來,與你們探討下zuul核心原理。java
1、spring-cloud-zuul是如何映射路由的?web
zuul的路由映射是使用springMVC功能,咱們知道springMVC有兩大核心組件:正則表達式
具體的springMVC原理這裏不作講解,咱們來看下zuul是如何自定義HandlerMapping來註冊路由映射的?下圖是springMVC的類繼承關係spring
很清晰看到Zuul提供的ZuulHandlerMapping是AbstractUrlHandlerMapping的子類,這個類是根據url來查找處理器,核心處理方法在lookupHandler裏面:數據庫
@Override protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) { return null; } //過濾忽略的路由規則 String[] ignored = this.routeLocator.getIgnoredPaths().toArray(new String[0]); if (PatternMatchUtils.simpleMatch(ignored, urlPath)) { return null; } RequestContext ctx = RequestContext.getCurrentContext(); if (ctx.containsKey("forward.to")) { return null; } if (this.dirty) { synchronized (this) { if (this.dirty) {//若是沒有加載過路由或者路由有刷新,則加載路由 registerHandlers(); this.dirty = false; } } } //根據url調用父類獲取處理器 return super.lookupHandler(urlPath, request); } private void registerHandlers() { //使用路由定位器獲取路由規則 Collection<Route> routes = this.routeLocator.getRoutes(); if (routes.isEmpty()) { this.logger.warn("No routes found from RouteLocator"); } else { for (Route route : routes) { //調用父類,註冊處理器 registerHandler(route.getFullPath(), this.zuul); } } }
梳理一下,以上方法的核心幾步:apache
ZuulController
,是Controller
的子類,對應的適配器是SimpleControllerHandlerAdapter
,也就說每個路由規則公共處理器都是ZuulController
,這個處理器最終會調用ZuulServlet
通過zuul定義的和自定義的攔截器,這個zuul的核心,後面咱們做詳細講解。2、路由定位器segmentfault
在上面咱們註冊了路由規則,而路由規則是由路由定位器獲取,那麼zuul給咱們提供哪些路由定位器,類圖以下:app
/服務名稱/**
映射成路由規則擴展:負載均衡
一、這裏咱們能夠實現本身的路由定位器,擴展本身想要的功能,如從數據庫加載路由規則,能夠參考文章ide
二、利用服務發現的路由定位器去加載理由規則的時候,咱們只是簡單的是把serviceId映射成路由規則,有的時間咱們仍是想在serviceId和路由之間提供約定 ,因而咱們可使用PatternServiceRouteMapper來實現
@Bean public PatternServiceRouteMapper serviceRouteMapper() { return new PatternServiceRouteMapper( "(?<name>^.+)-(?<version>v.+$)", "${version}/${name}"); }
這樣serviceId:myusers-v1
將被映射到路由/ v1 / myusers / **
,這裏任何正則表達式均可以接受,根據本身須要本身設定。
3、過濾器
前面提到,因此路由請求都會被控制器ZuulControoler
攔截到,最終交由ZuulServlet
來處理,核心處理代碼以下:
@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(); } }
這段代碼體現了zuul過濾器的生命週期,官方提供了一張圖很形象的展現:
zuul把過濾器分爲四個階段,分別是
zuul爲咱們提供了各個階段的過濾器一共10個
這裏咱們來着重看下路由階段的兩個過濾器
CloseableHttpClient
來發送http請求HttpClient
來轉發請求未完待續......