spring gateway 动态路由学习笔记和基础加载流程

    前提:因为工作需要,想要搞一个动态代理,本来想用nginx定时跑脚本的方式,不过lua脚本的学习成本略高,同事提醒要不用gateway,正好学习成本能低些,也可以成体系,好控制。

    略过趟坑,官网学习,百度(90%时间)等,发现能在一个地方全部解决问题的地方基本没有,只能一边尝试一边自己拼凑。这里吐槽一下官网,只有静态路由方式配置的讲解,完全没讲怎么扩展,更麻烦的是中文官网的翻译着实看不懂。

    talk is cheap,show me the request.先说我的需求,vnc请求中带一个id字段,用id字段去另一个服务A去换回region字段,根据region字段将websocket路由到不同的ip地址,region和ip地址的对应关系通过接口由服务B获取,没了。简单吧,明天上线没问题吧。我用了差不多2周半:D

    看主要代码和流程:

     1.gateway这个项目其实还是个静态代理,数据io是用netty实现的,但是不能动态的只能路由,只能根据事先定义好的规则匹配路由。官网给出的都是配置文件指定路由的方法,就不赘述。贴一下我的application.yml吧。spring gateway 动态路由学习笔记和基础加载流程

    这里指定的endpoint是引入了actuator,源码从GatewayControllerEndpoint暴露的地址,可以查看当前的所有路由,调试时候很有用,另外actuator还可以当作k8s的健康检查之用.

    2.定时刷新路由信息,每隔几分钟请求一下接口刷新路由信息,我这里就主要是region和ip的关系,开启定时任务后(@EnableScheduling),创建定时任务,任务就获取关系然后refresh这个Container。怎么获取数据就忽略了,各家自己的业务逻辑。

spring gateway 动态路由学习笔记和基础加载流程

    3.Container的任务主要就是缓存下自己已有的路由,好知道哪些需要添加,哪些需要删除或更新。唉吐槽的人呐,快去创造奇迹,gateway里用了大量的reactor包里的东西,看的十分困难。

spring gateway 动态路由学习笔记和基础加载流程

spring gateway 动态路由学习笔记和基础加载流程

4.通过实现RouteDefinitionRepository接口,实现路由的增加增删改查。这里我自定义了一些map,每个对应不同的业务逻辑,以后如果由其他业务接进来的话,至少不会直接影响。思路是这样,写法烂请留言吐槽

spring gateway 动态路由学习笔记和基础加载流程

spring gateway 动态路由学习笔记和基础加载流程

5.动态路由的配置更新基本上到这里就结束了,但是我还有一步是怎么标定我得路由,因为我的路由标记PredicateDefinition有两个条件,一个Path=ecs_vnc这个由调用实现,另一个Header的标记位就需要路由之前自己指定,这里我愁了很久,因为gateway的选择路由在filter之前,所以使用GatewayFilter根本决定不了我的路由选择。这时候nb同事的作用又体现出来了,既然gateway的filter不好使,用外部的filter不就行了嘛,一语惊醒梦中人。我最后选定的是springframework.web.wever.WebFilter,本来想用tomcat的filter,发现应该扩展性更好些。还有这里我用了个责任链,也是防着以后有别的业务接进来,代码中的fulfillRegion就是通过id请求到region,然后放到request的头部,看了另一篇帖子说这样response的长度会有限制,我暂时不需要那么大的reponse而且自己测了下也没什么问题,当然这个说法也是有解决方法的,详见这里,基本我gateway就是从他这里启蒙的

spring gateway 动态路由学习笔记和基础加载流程spring gateway 动态路由学习笔记和基础加载流程

我得需求到现在基本已经实现了,当然还有一些鉴权啊,协议判断什么的就不贴了,都是各自自家的业务而已。

 

多一个空格空开内容

  现在记录下gateway这么实现的原理,这个要从actuator开始讲起

spring gateway 动态路由学习笔记和基础加载流程

{host}:{port}/actuator/gateway/routes 这个接口可以列出所有的路由,请求过来的时候选择路由也就是从这些里选,这个是怎么来的呢。

spring gateway 动态路由学习笔记和基础加载流程

可以看到是通过这个this.routeLocator获取的,这个怎么来的接着进去看

spring gateway 动态路由学习笔记和基础加载流程

AbstractGatewayControllerEndpoint这个类直接构造方法传进来就是,那还是要看具体类,返回去看,也是构造方法,八成基本就是直接注入的了。看到GatewayAutoConfiguration,妥妥的入口类,到这里差点放弃,这个RouteLocator还要去找注入上下文的地方,所幸离得不远能继续追。

spring gateway 动态路由学习笔记和基础加载流程

这里有一个区别了,如果有OnverboseDisabledCondition就走下面的GatewayLegacyControllerEndpoint,没有就走上面的。什么意思呢,就是咱们不是开了actuator嘛,这个如果你只做查询用,就可以设置只读模式,也就是verbose.enable设置为true

,这里我们想用他动态修改路由,所以就是false,正好也就是默认值。

进去一看,卧槽原来找错了,刚在找的是只读的endpoint,这个才是咱们真正用的

spring gateway 动态路由学习笔记和基础加载流程

那我们的目标就是这个routerDefinitionLocator了,中间过程略过,又回到了gateway的bean注入文件。

spring gateway 动态路由学习笔记和基础加载流程

注意这个Primary的bean是整合了很多RouterDefinitionLocator,那这个RouterDefinitionLocator接口到底有哪些实现类呢

spring gateway 动态路由学习笔记和基础加载流程

到此,终于找到了自己的代码~~~~

所以这个链是自己定义的RouteDefinitionRepository -> RouterDefinitionLocator ->GatewayLegacyControllerEndpoint,路由由RouterDefinitionLocator的getRouteDefinitions()方法暴露。

但是后面发现个问题,这样动态加载的Predicate不支持or,后来发现 AsyncPredicate这个接口在gateway里类似一个工具类,根本不是核心功能,也就没什么办法直接用,想要的话只能自己写一个,好吧。