Gateway动态路由实现

作者: 天生小包 | 来源:发表于2021-08-05 19:01 被阅读0次

    一、Gateway路由主要有两种方式

    1、用yml配置文件
      routes:
        - id: ijep-service-sys  #基础服务
          uri: lb://ijep-service-sys
          predicates:
            - Path=/api/sys/**
          filters:
            - StripPrefix=2
    
    2、写在代码里
    image.png

    无论是哪一种,在启动网关后将无法修改路由配置,如有新服务要上线,则需要先把网关下线,修改 yml 配置后,再重启网关。

    二、动态路由解析

    Gateway网关启动时,路由信息默认会加载内存中,路由信息被封装到 RouteDefinition 对象中,配置多个RouteDefinition组成Gateway的路由系统。

    package org.springframework.cloud.gateway.route; 
    @Validated
    public class RouteDefinition {
    
    @NotEmpty
    private String id = UUID.randomUUID().toString();
    
    @NotEmpty
    @Valid
    private List<PredicateDefinition> predicates = new ArrayList<>();
    
    @Valid
    private List<FilterDefinition> filters = new ArrayList<>();
    
    @NotNull
    private URI uri;
    
    private int order = 0;
    
    public RouteDefinition() {
    }
    
    .....此处省略......
    
    }
    
    • RouteDefinition中的字段与上面代码配置方式比较对应
    package org.springframework.cloud.gateway.route;
    
    import reactor.core.publisher.Flux;
    
    /**
     * @author Spencer Gibb
    */
    public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
    }
    

    *RouteDefinitionLocator是个接口,在org.springframework.cloud.gateway.route包下,如果想查看网关中所有的路由信息,可以调用此方法;后面还有另外一种查看方式,是Spring Cloud Gateway 的Endpoint端点提供的方法

    三、Gateway Endpoint 端点

    Endpoint 端点有暴露路由信息、获取所有路由、刷新路由、查看单个路由、删除路由等方法,源码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中,访问端点中的方法需要修改pom和配置文件:
    *添加pom文件依赖:

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    

    *yml配置

    # 暴露端点
    management:
     endpoints:
       web:
        exposure:
        include: '*'
     endpoint:
        health:
        show-details: always
    

    四、动态路由实现

    准备:
    * Spring Cloud:Greenwich.SR1
    * Spring Boot: 2.1.1.RELEASE
    * 注册中心(Consul)
    * 网关Gateway
    * 业务服务(ijep-service-sys)

    1、创建路由模型

    *过滤器模型

    public class GatewayFilterDefinition {
    //Filter Name
    private String name;
    
    //对应的路由规则
    private Map<String, String> args = new LinkedHashMap<>();
    
       ......此处省略Get、Set方法......
    }
    

    *路由断言模型

    public class GatewayPredicateDefinition {
    
    //断言对应的Name
    private String name;
    
    //配置的断言规则
    private Map<String, String> args = new LinkedHashMap<>();
           
     ......此处省略Get、Set方法......
    }
    

    *路由模型

    public class GatewayRouteDefinition {
    
    //路由的Id
    private String id;
    
    //路由断言集合配置
    private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
    
    //路由过滤器集合配置
    private List<GatewayFilterDefinition> filters = new ArrayList<>();
    
    //路由规则转发的目标uri
    private String uri;
    
    //路由执行的顺序
    private int order = 0;
    
    ......此处省略Get、Set方法......
    }
    
    2、编写态路由实现类,实现ApplicationEventPublisherAware接口
    @Service
    public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware{
    
        @Autowired
        private RouteDefinitionWriter       routeDefinitionWriter;
    
        private ApplicationEventPublisher   publisher;
    
      @Override
      public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }
    
    /**
     * 增加路由
     * @param definition
     * @return
     */
      public String add(RouteDefinition definition) {
          routeDefinitionWriter.save(Mono.just(definition)).subscribe();
          this.publisher.publishEvent(new RefreshRoutesEvent(this));
          return "success";
      }
    
    /**
     * 更新路由
     * @param definition
     * @return
     */
    public String update(RouteDefinition definition) {
        try {
            delete(definition.getId());
        } catch (Exception e) {
            return "update fail,not find route  routeId: "+definition.getId();
        }
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "update route  fail";
        }
    }
    
    /**
     * 删除路由
     * @param id
     * @return
     */
      public Mono<ResponseEntity<Object>> delete(String id) {
        return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
            return Mono.just(ResponseEntity.ok().build());
        })).onErrorResume((t) -> {
            return t instanceof NotFoundException;
        }, (t) -> {
            return Mono.just(ResponseEntity.notFound().build());
        });
      }
    }
    
    3、编写API接口,实现动态路由功能
    @RestController
    @RequestMapping("/api/route")
    public class RouteRestController {
    
       @Autowired
       private DynamicRouteServiceImpl dynamicRouteService;
    
    /**
     * 增加路由
     * @param gatewayRouteDefinition 路由模型
     *  报文:{"filters":[{"name":"StripPrefix","args":{"_genkey_0":"2"}}],"id":"ijep-service-sys-hyk11","uri":"lb://ijep-service-sys","order":2,"predicates":[{"name":"Path","args":{"_genkey_0":"/api/hyk11/**"}}]}
     * @return
     */
    @PostMapping("/add")
    public RestResponse add(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
        String flag = "fail";
        try
        {
            RouteDefinition definition = assembleRouteDefinition(gatewayRouteDefinition);
            flag = this.dynamicRouteService.add(definition);
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return RestResponseBuilder.restResponse().data(flag).build();
    }
    
    
    /**
     * 更新路由
     * @param gatewayRouteDefinition 路由模型
     *  报文:{"filters":[{"name":"StripPrefix","args":{"_genkey_0":"2"}}],"id":"ijep-service-sys-hyk11","uri":"lb://ijep-service-sys","order":2,"predicates":[{"name":"Path","args":{"_genkey_0":"/api/hyk11/**"}}]}
     * @return
     */
    @PostMapping("/update")
    public RestResponse update(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
    
        RouteDefinition definition = assembleRouteDefinition(gatewayRouteDefinition);
        String flag = this.dynamicRouteService.update(definition);
    
        return RestResponseBuilder.restResponse().data(flag).build();
    }
    
    /**
     * 删除路由
     * @param id 路由Id
     * @return
     */
    @DeleteMapping("/routes/{id}")
    public RestResponse delete(@PathVariable String id) {
    
        Mono<ResponseEntity<Object>> responseEntityMono = this.dynamicRouteService.delete(id);
        return RestResponseBuilder.restResponse().data(responseEntityMono).build();
    }
    
    
    /**
     * 把传递进来的参数转换成路由对象
     * @param gatewayRouteDefinition 路由模型
     * @return
     */
    private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gatewayRouteDefinition) {
        RouteDefinition definition = new RouteDefinition();
        definition.setId(gatewayRouteDefinition.getId());
        definition.setOrder(gatewayRouteDefinition.getOrder());
    
        //设置断言
        List<PredicateDefinition> pdList = new ArrayList<>();
        List<GatewayPredicateDefinition> gatewayPredicateDefinitionList = gatewayRouteDefinition.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 = gatewayRouteDefinition.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(gatewayRouteDefinition.getUri().startsWith("http")){
            uri = UriComponentsBuilder.fromHttpUrl(gatewayRouteDefinition.getUri()).build().toUri();
        }else{
            // uri为 lb://consumer-service 时使用下面的方法
            uri = URI.create(gatewayRouteDefinition.getUri());
        }
        definition.setUri(uri);
        return definition;
    }
    }
    
    4、启动网关服务

    查询所有的路由地址信息,访问地址:http://localhost:9000/actuator/gateway/routes

    image.png
    5、访问业务接口(ApiPost)
    image.png
    6、新增路由信息(ApiPost)
    image.png
    7、新增后,访问业务接口(ApiPost)
    image.png

    项目地址:无权访问

    相关文章

      网友评论

        本文标题:Gateway动态路由实现

        本文链接:https://www.haomeiwen.com/subject/afzmvltx.html