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. 所以想要自定义路由方式就可以从这几个地方入手,并且可以借鉴源码

      完!建议学习时结合源码!

参考地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值