zoukankan      html  css  js  c++  java
  • SpringCloud实战十四:Gateway之 Spring Cloud Gateway 动态路由进阶

    1.上篇 对网关动态路由配置做了简单的分享,本篇将对动态路由进行更深层的实现,实现思路如下:
    • 1.创建一个路由信息维护的项目(dynamic-route),实现增删改查路由信息到mysql
    • 2.提供发布功能,发布后将路由信息与版本信息保存到redis中,对外提供 rest 接口获取路由信息
    • 3.网关(gateway-dynamic-route)开启定时任务,定时拉取 rest 接口中发布的最新版的路由信息,对比版本号,如果网关的版本号与rest接口中的不一致,则获取路由信息后更新网关路由,这样网关发布多个实例后,都会单独的去拉取维护的路由信息
    • 4.整体架构设计如下:
      在这里插入图片描述

    根据上面的思路进行代码实现,下面将创建2个项目,都要注册到eureka上

    • dynamic-route,路由管理,维护路由信息,并存储到mysql与redis中
    • gateway-dynamic-route,网关,通过rest接口定时拉取最新路由信息并更新到网关中
    • consumer-service,消费者服务,测试动态路由配置后将请求转发到此服务
    2.创建一个路由信息维护的项目 dynamic-route,实现对路由信息的增删改查和发布功能,信息保存到mysql中,发布后保存到redis,对外提供路由数据时返回redis中的路由信息(使用缓存来应对网关定时任务读请求)
    • 有2张表,路由信息表与版本发布表,表结构如下:
      在这里插入图片描述
    • 增删改查的实现因篇幅原因就不一一展开了,可查看项目代码,已上传到码云,下面贴出项目的几个页面,页面比较简单
      在这里插入图片描述
      在这里插入图片描述
      下面是提供的 rest 接口返回版本信息与路由信息,网关先获取版本号,与本地版本号对比,如不一致则通过此接口拉取路由信息
      在这里插入图片描述
      获取维护的路由信息
      在这里插入图片描述
    3.重点是网关项目,创建 gateway-dynamic-route 项目,网关启动时设置默认的版本号为0,通过定时任务每60秒拉取一次远程路由项目提供最新版本号,与网关的版本号对比,如不一致,则拉取远程路由项目最新的路由信息,更新到网关路由上去,同时把本地版本号覆盖为最新的版本号

    因代码较多,博客主要分享实现思路和贴出关键代码,已提交至码云,可下载项目查看全部代码

    • 1.启动定时任务,注册到erueka,添加RestTemplate Bean且以负载均衡形式访问路由项目
    @EnableScheduling
    @EnableDiscoveryClient
    @SpringBootApplication
    public class GatewayDynamicRouteApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(GatewayDynamicRouteApplication.class, args);
    	}
    	
    	@Bean
    	@LoadBalanced
    	public RestTemplate restTemplate(){
    		return new RestTemplate();
    	}
    }
    • 2.定时任务实现类
    /**
     * 定时任务,拉取路由信息
     * 路由信息由路由项目单独维护
     */
    @Component
    public class DynamicRouteScheduling {
    
        @Autowired private RestTemplate restTemplate;
        @Autowired private DynamicRouteService dynamicRouteService;//动态路由实现类,与前篇博客中的实现类代码是一样的
    
        private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        private static final String dynamicRouteServerName = "dynamic-route-service";
    
        //发布路由信息的版本号
        private static Long versionId = 0L;
    
        //每60秒中执行一次
        //如果版本号不相等则获取最新路由信息并更新网关路由
        @Scheduled(cron = "*/60 * * * * ?")
        public void getDynamicRouteInfo(){
            try{
                System.out.println("拉取时间:" + dateFormat.format(new Date()));
                //先拉取版本信息,如果版本号不想等则更新路由
                Long resultVersionId = restTemplate.getForObject("http://"+ dynamicRouteServerName +"/version/lastVersion" , Long.class);
                System.out.println("路由版本信息:本地版本号:" + versionId + ",远程版本号:" + resultVersionId);
                if(resultVersionId != null && versionId != resultVersionId){
                    System.out.println("开始拉取路由信息......");
                    String resultRoutes = restTemplate.getForObject("http://"+ dynamicRouteServerName +"/gateway-routes/routes" , String.class);
                    System.out.println("路由信息为:" + resultRoutes);
                    if(!StringUtils.isEmpty(resultRoutes)){
                        List<GatewayRouteDefinition> list = JSON.parseArray(resultRoutes , GatewayRouteDefinition.class);
                        for(GatewayRouteDefinition definition : list){
                            //更新路由
                            RouteDefinition routeDefinition = assembleRouteDefinition(definition);
                            dynamicRouteService.update(routeDefinition);
                        }
                        versionId = resultVersionId;
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    
        //把前端传递的参数转换成路由对象
        private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {
            RouteDefinition definition = new RouteDefinition();
            definition.setId(gwdefinition.getId());
            definition.setOrder(gwdefinition.getOrder());
    
            //设置断言
            List<PredicateDefinition> pdList=new ArrayList<>();
            List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();
            for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {
                PredicateDefinition predicate = new PredicateDefinition();
                predicate.setArgs(gpDefinition.getArgs());
                predicate.setName(gpDefinition.getName());
                pdList.add(predicate);
            }
            definition.setPredicates(pdList);
    
            //设置过滤器
            List<FilterDefinition> filters = new ArrayList();
            List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters();
            for(GatewayFilterDefinition filterDefinition : gatewayFilters){
                FilterDefinition filter = new FilterDefinition();
                filter.setName(filterDefinition.getName());
                filter.setArgs(filterDefinition.getArgs());
                filters.add(filter);
            }
            definition.setFilters(filters);
    
            URI uri = null;
            if(gwdefinition.getUri().startsWith("http")){
                uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
            }else{
                uri = URI.create(gwdefinition.getUri());
            }
            definition.setUri(uri);
            return definition;
        }
    }
    • 3.网关提供获取所有路由信息的Controller
    /**
     * 查询网关的路由信息
     */
    @RestController
    @RequestMapping("/route")
    public class DynamicRouteController {
    
        @Autowired private RouteDefinitionLocator routeDefinitionLocator;
    
        //获取网关所有的路由信息
        @RequestMapping("/routes")
        public Flux<RouteDefinition> getRouteDefinitions(){
            return routeDefinitionLocator.getRouteDefinitions();
        }
    }
    • 4.启动eureka、dynamic-route、gateway-dynamic-route、consumer-service

    • 5.通过 dynamic-route 项目新增一条路由信息,路由的 uri 是consumer-service,且发布路由
      在这里插入图片描述
      发布路由时,会将版本表最新的版本号与路由信息添加到redis中,下图是redis中保存的路由信息,网关定时获取版本信息与路由信息都是先从redis中取出来的
      在这里插入图片描述

    • 6.因网关没有配置路由信息,可通过 localhost:9999/route/routes 获取所有路由信息,此时有两条路由信息,是网关从eureka上拉下来的默认的路由信息,不是配置的路由信息
      在这里插入图片描述

    • 7.等待60秒后,网关通过 RestTemplate 拉取dynamic-route项目的最新版本号,发现不一致,则拉取最新路由信息,并更新到网关路由中
      在这里插入图片描述

    • 8.通过网关访问 consumer-service,验证路由信息是否已更新到网关中,访问:http://localhost:9999/zy/hello?name=zy
      在这里插入图片描述

    • 9.再次查看所有路由信息,可看到consumer-service已被设置到网关路由中了
      在这里插入图片描述

    • 10.每隔60秒再次对比版本号,当有新的版本号发布后,就会拉取维护的路由信息,没有则网关不会更新路由
      在这里插入图片描述

    • 11.修改一下路由信息,把断言Path=/zy/** , 改为Path=/consumer/** ,并发布,隔了60秒后,网关的控制台拉取到最新的路由信息
      在这里插入图片描述
      此时访问 http://localhost:9999/zy/hello?name=zy 报错,因为路由断言path被更改了路由不到
      在这里插入图片描述
      需要通过 http://localhost:9999/consumer/hello?name=zy 访问,正常返回
      在这里插入图片描述
      好了,动态路由的进阶实现已完成了,因篇幅原因没有全部贴出所有代码,博客主要介绍动态路由的实现思路,代码可根据自己的习惯进行实现

    代码已上传至码云:

    项目版本信息如下:

    - SpringBoot 2.0.6.RELEASE
    - SpringCloud Finchley.SR2
    转自:https://blog.csdn.net/zhuyu19911016520/article/details/86562615?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-3&spm=1001.2101.3001.4242
  • 相关阅读:
    MATLAB函数大全 .
    让隐藏的virtualBox菜单重新显示
    【转载】matlab 脚本文件和函数文件
    Ubuntu下用命令行快速打开各类型文件
    关于存货的成本计价方式
    C# .net asp学习笔记
    在DLL中怎么共用一个全局变量
    如果比较表中同类型多列值是否相等?
    统计报表(用ROLLUP 汇总数据)
    UML建模
  • 原文地址:https://www.cnblogs.com/javalinux/p/14376398.html
Copyright © 2011-2022 走看看