springcloud網關源碼分析及總結

一:網關路由配置方式分爲兩種:靜態路由配置、動態路由配置

  1. 靜態:路由信息可以從配置文件、數據庫DB、redis等其他地方獲取,這種方式沒有使用eureka的註冊發現,熔斷等功能,如需要則可以自己實現
  2. 動態:路由配置信息從Eureka中通過Discover發現,默認使用sc的熔斷等功能

二:網關注冊路由地址、路由轉發等流程

1、註冊路由地址:

      首先看RouteLocator這個家族的勢力,下面再截殺是幹嘛用的

               

  1. 項目啓動時從配置文件和Eureka中把其他pool的信息地址等加載出來並設置到RouteLocator中   
  2. zuul默認加載路由信息的地方有兩個,一個是從配置文件中,第二個是從Eureka中,
  3. 源碼如圖所示:
  4. 由源碼可知,zuul在locateRouters方法中加載路由信息,首先會調用super.locateRoutes()方法,該方法是SimpleRouteLocator類的,是從配置文件中去加載路由配置,再往下看List<String> services = this.discovery.getServices();方法是DiscoveryClientRouteLocator類從Eureka中獲取服務列表,最後把兩個地方獲取到的路由信息彙總之後存放到LinkedHashMap中。
  5. 由上面可知,不管你配置你文件中配不配zuul.routes的路由信息,Routers中都會有服務的路由地址的。
    1. 只不過從配置文件和eureka上加載的路由配置訪問時會有一點區別
    2. Eureka默認會把每個服務的映射存儲爲http:gateway_ip/serviceName/method這樣,所以訪問:http://網關地址/訪問的服務名/方法名
    3. 配置文件訪問則是根據配置中的path路徑去訪問,然後網關會映射到配置中的url中,所以訪問:http://網關地址/path/方法名
  6. 配置文件獲取的路由信息與eureka中獲取的配置信息的路由規則區別
  7. Eureka中獲取的路由信息訪問路徑默認的是,/服務名/** 如:/user-service/**
  8. 配置文件文件中獲取的信息訪問如圖:
  9. 除了上述還有一個路由自動刷新的功能,RefreshableRouteLocator繼承自RouteLocator,額外提供了對於路由刷新方面的定義,refresh方法會結合spring的ApplicationEvent,實現基於事件的路由刷新機制,具體可以參看ZuulDiscoveryRefreshListener源碼。
  10. DiscoveryClientRouteLocator繼承自SimpleRouteLocator,並且實現了RefreshableRouteLocator,從類定義上可以看出他具備了基本的路由功能及路由刷新功能【原理是使用spring的事件發佈與監聽機制】
  11. 源碼可知DiscoveryClientRouteLocator重寫了refresh方法該方法中執行了上述步驟locateRoutes加載路由信息的接口
  12. 讓我們來到RouteLocator家族中的最後一個成員CompositeRouteLocator,他雖然僅僅實現了RefreshableRouteLocator,但是卻是能力最強的一個,因爲他能夠整合衆多RouteLocator的功能於一身。注意其構造方法,它會將傳入的routeLocators排序,還記得SimpleRouteLocator是實現了Ordered接口吧?
  13. 實際上,CompositeRouteLocator也被標記爲@Primary,作爲主要的RouteLocator被使用,CompositeRouteLocator構造過程中被傳入的routeLocators其實僅包括一個DiscoveryClientRouteLocator,所以這就是默認只使用了DiscoveryClientRouteLocator的原因
  14. 到此RouteLocator的作用差不多了
  15. 注意:從這份代碼中也就可以解答我在一開始開發時遇到的一個問題,就是我僅在配置文件中配置了xxx的路由配置,但是實際訪問時爲什麼yyy下的接口也可以被路由?原因就在於zuul默認會從兩個地方把路由信息加載出來,並且會以服務發現eureka中的路由信息爲主,雖然自己在配置文件中沒有配置,但是eureka中卻可能已經存在了好多個服務了

2、路由轉發

  1. 看完RouteLocator家族,讓我們再來拜訪下另一家族ZuulFilter實現了IZuulFilter
  2. 我們先來看一下IZuulFilter,很簡單明瞭,shouldFilter表示該filter是否應該被執行,run方法只有在shouldFilter方法返回true的時候纔會被執行
  3. 這裏面我們如果用默認的轉發那麼只需要關注三個類PreDecorationFilterRibbonRoutingFilter(服務註冊發現路由) SimpleHostRoutingFilter (簡單方式)
  4. 那麼什麼時候用SimpleHostRoutingFilter什麼時候用RibbonRoutingFilter呢?【而哪種路由配置方式是“URL映射”,哪種配置方式又是“serviceId映射”呢?】答案就是PreDecorationFilter過濾器幫我們做了判斷
  5. Zuul有一個前置過濾器PreDecorationFilter用於通過RouteLocator路由定位器決定在何時以何種方式路由轉發
  6. 首先請求到達網關但還沒有路由前, 會先執行PreDecorationFilter中的shouldFilter方法再執行run()方法,在run方法【Route route = this.routeLocator.getMatchingRoute(requestURI);】中會先根據請求的path來加載路由規則route,如果route不爲空,則說明有路由信息繼續往下執行,如果爲空則說明沒有任何路由信息則不執行路由
  7. 其次在判斷路由規則中是否有以http或者https開頭的請求路徑,如果有則把ctx.setRouteHost(this.getUrl(location));請求路徑放到RouteHost中,如果沒有則ctx.set("serviceId", location);ctx.setRouteHost((URL)null);設置RouteHost爲空,並設置serviceId,這一步比較關鍵,是爲了下面到是根據配置文件中配置的url路徑使用SimpleHostRoutingFilter中的方法去路由,還是根據eureka中的serviceId使用RibbonRoutingFilter去路由做先決條件
  8. PreDecorationFilter的作用就是在路由之前執行,判斷用那種方式進行路由,源碼如圖所示:
  9. 接下來先看SimpleHostRoutingFilter類中的方法
  10. 如果是配置文件中配置的路由信息,則在上面PreDecorationFilter中已經把RouteHost設置爲配置中的url地址,並且RouteHost肯定不爲null
  11. 然後再看RibbonRoutingFilter類中的方法
  12. 如果是Eureka中獲取的路由信息,則在上面PreDecorationFilter中已經把RouteHost設置爲null,並設置了serviceId
  13. 總結PreDecorationFilter攔截器是路由前先攔截請求,然後由裏面的run方法來判斷確定應該使用哪種路由方式是簡單路由【SimpleHostRoutingFilter】還是註冊發現路由【RibbonRoutingFilter
    1. 兩種方式區別:
    2. SimpleHostRoutingFilter是用HttpClient的方式直接向目標url發起請求,不再經過ribbonhystrix
    3. RibbonRoutingFilter是用通過eureka註冊發現的面向服務根據serviceId然後交由ribbonhystrix向下遊服務進行請求進行的路由請求
  14. 到此zuulfilter的路由轉發已經分析完畢,當然IZuulFilter下不止這幾個springcloud已經幫我們的攔截器,還有好多,其他的可以自行研究

3、自定義動態路由規則

  1. 由上面分析可知
  2. 首先springcloud給我們默認實現的動態路由方式有兩種,一種是url另一種是eureka,所以自定義動態路由當然指的就是根據我們自己配置的url進行路由啦。
  3. 然而根據url進行路由springcloud只是默認加載application.yml中我們配置的
  4. zuul:
      routes:
        user-service:
          path: /user-service/**
          url: http://localhost:8081/

    這中方式進行url路由,那麼我們如果不想把這些路由信息寫在配置文件中怎麼辦呢?

  5. 答案就是我們自定義一個路由信息的加載類並實現RouteLocator類,重寫裏面的locateRoutes()方法,然後可以自定義把加載路由信息的邏輯換成從數據庫加載,當然路由信息的格式要參照DiscoveryClientRouteLocator中從配置文件加載路由信息並封裝到ZuulRoute中的格式,數據庫也按這種格式設計就好,所以自定義RouteLocator時可以參照源碼中的DiscoveryClientRouteLocator做法
  6. 注意:因爲用eureka的方式去路由只需要知道serviceId即可,然後discover會根據serviceId自動去找到服務ip地址,然後結合fegin把ip地址和需要轉發的方法拼接成http://pool_ip:port/method這種方式去轉發,其實底層用的也是HttpClient的方式去訪問的,而配置文件或者是自定義的路由方式直接把路由地址寫全了寫的http://pool_ip:port/method少了一步fegin的自動拼接轉化,其原理都是相似的
  7. 所以想要自定義路由方式就可以從這幾個地方入手,並且可以借鑑源碼

      完!建議學習時結合源碼!

參考地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章