spring-cloud-zuul原理解析(一)

Zuul是Netflix開源的微服務網關,它能夠和Eureka,consul,Ribbon,Hystrix等組件配合使用,網上也有不少如何使用zuul的文章,咱們也在生產環境使用了,因此讀了下zuul的源碼,下面把它分享出來,與你們探討下zuul核心原理。java

1、spring-cloud-zuul是如何映射路由的?web

zuul的路由映射是使用springMVC功能,咱們知道springMVC有兩大核心組件:正則表達式

  • HandlerMapping:映射器
  • HandlerAdapter:適配器

    具體的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

  • 判斷urlPath是否被忽略,若是忽略則返回null
  • 判斷路由規則有沒有加載過或者更新過,沒有加載或者有更新則從新加載
  • 註冊處理器的時候,使用的是ZuulController,是Controller的子類,對應的適配器是SimpleControllerHandlerAdapter,也就說每個路由規則公共處理器都是ZuulController,這個處理器最終會調用ZuulServlet通過zuul定義的和自定義的攔截器,這個zuul的核心,後面咱們做詳細講解。
  • 根據url找處處理器,返回

2、路由定位器segmentfault

在上面咱們註冊了路由規則,而路由規則是由路由定位器獲取,那麼zuul給咱們提供哪些路由定位器,類圖以下:app

  • SimpleRouteLocator:主要加載配置文件的路由規則
  • DiscoveryClientRouteLocator:服務發現的路由定位器,去註冊中心如Eureka,consul等拿到服務名稱,以這樣的方式/服務名稱/**映射成路由規則
  • CompositeRouteLocator:複合路由定位器,主要集成全部的路由定位器(如配置文件路由定位器,服務發現定位器,自定義路由定位器等)來路由定位。
  • RefreshableRouteLocator:路由刷新,只有實現了此接口的路由定位器才能被刷新

擴展負載均衡

一、這裏咱們能夠實現本身的路由定位器,擴展本身想要的功能,如從數據庫加載路由規則,能夠參考文章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把過濾器分爲四個階段,分別是

  • pre:主要是在請求路由以前調用,不少驗證能夠在這裏作
  • route:在路由請求時候被調用,主要用來轉發請求
  • post:主要用來處理響應請求
  • error:當錯誤發生時,會經由這個類型的過濾器處理

zuul爲咱們提供了各個階段的過濾器一共10個

這裏咱們來着重看下路由階段的兩個過濾器

  • SimpleHostRoutingFilter:主要提供當路由設置url方式時,由這個路由器來轉發請求,使用的是apache的CloseableHttpClient來發送http請求
  • RibbonRoutingFilter:當路由設置serviceId時,由此過濾器來轉發請求,這裏集成了ribbon,Hystrix,實現負載均衡,熔斷的功能;默認狀況下也是使用apache的HttpClient來轉發請求

未完待續......

相關文章
相關標籤/搜索