暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring Cloud Gateway教程[三]:用redis实现动态路由

CodingWithFun 2019-08-23
2406

gateway配置路由的主要方式有两种,一种是用yaml文件,一种是写代码里,这两种方式都不支持动态路由配置。如:

     public RouteLocator coustomerRouteLocator(RouteLocatorBuilder builder){
         return builder.routes().route("id-1", r->r.path("/**")
                .uri("lb://SERVER-DEMO"))
                .build();
     }
        cloud:
      gateway:
      routes:
      - id: header_route
      uri: https://example.org
      predicates:
      - Header=X-Request-Id, \d+

      下面就来看看gateway是如何来加载这些配置信息的。


      一、路由初始化

      无论是yaml还是代码,这些配置最终都是封装到RouteDefinition对象中。

      一个RouteDefinition有个唯一的ID,如果不指定,就默认是UUID,多个RouteDefinition组成了gateway的路由系统。

      所有路由信息在系统启动时就被加载装配好了,并存到了内存里。我们从源码来看看。

      圆圈里就是装配yml文件的,它返回的是PropertiesRouteDefinitionLocator,该类继承了RouteDefinitionLocator,RouteDefinitionLocator就是路由的装载器,里面只有一个方法,就是获取路由信息的。该接口有多个实现类,分别对应不同方式配置的路由方式。

        public interface RouteDefinitionLocator {
        Flux<RouteDefinition> getRouteDefinitions();
        }

        通过这几个实现类,再结合上面的AutoConfiguration里面的Primary信息,就知道加载配置信息的顺序。


        PropertiesRouteDefinitionLocator-->|配置文件加载初始化| CompositeRouteDefinitionLocator

        RouteDefinitionRepository-->|存储器中加载初始化| CompositeRouteDefinitionLocator

        DiscoveryClientRouteDefinitionLocator-->|注册中心加载初始化| CompositeRouteDefinitionLocator

        这是第一顺序,就是从CachingRouteLocator中获取路由信息,我们可以打开该类进行验证。

        不管发起什么请求,必然会走上面的断点处。请求一次,走一次。这是将路由信息缓存到了Map中。配置信息一旦请求过一次,就会被缓存到上图的CachingRouteLocator类中,再次发起请求后,会直接从map中读取。


        如果想动态刷新配置信息,就需要发起一个RefreshRoutesEvent的事件,上图的cache会监听该事件,并重新拉取路由配置信息。


        通过下图,可以看到如果没有RouteDefinitionRepository的实例,则默认用InMemoryRouteDefinitionRepository。而做动态路由的关键就在这里。即通过自定义的RouteDefinitionRepository类,来提供路由配置信息。

        二、基于数据库、缓存的动态路由

        核心实现类:

         1@Component
        2public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
        3
        4    public static final String GATEWAY_ROUTES = "geteway_routes";
        5
        6    @Resource
        7    private StringRedisTemplate redisTemplate;
        8
        9    @Override
        10    public Flux<RouteDefinition> getRouteDefinitions() {
        11        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        12        redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
        13                .forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class)));
        14        return Flux.fromIterable(routeDefinitions);
        15    }
        16
        17    @Override
        18    public Mono<Void> save(Mono<RouteDefinition> route) {
        19        return route.flatMap(r -> {
        20            redisTemplate.opsForHash().put(GATEWAY_ROUTES, r.getId(), JSON.toJSONString(r));
        21            return Mono.empty();
        22        });
        23    }
        24
        25    @Override
        26    public Mono<Void> delete(Mono<String> routeId) {
        27        return routeId.flatMap(id ->{
        28            redisTemplate.opsForHash().delete(GATEWAY_ROUTES, id);
        29            return Mono.empty();
        30        });
        31    }
        32
        33}


        主要是在get方法里,此处从redis里获取配置好的Definition。

        然后我们的工作就是将配置信息,放到redis里即可。

        三、通过REST接口配置网关

        我们可以定义一个rest接口来对网关进行动态配置,包含查询、删除、修改操作。

         1@RestController
        2public class RouteController {
        3
        4    @Autowired
        5    private DynamicRouteService dynamicRouteService;
        6
        7    @RequestMapping("/getall")
        8    public Flux<RouteDefinition> getRoutes(){
        9        return dynamicRouteService.getAllRoute();
        10    }
        11
        12    @DeleteMapping("/delete/{id}")
        13    public String deleteRoute(@PathVariable String id){
        14        return dynamicRouteService.delete(id);
        15    }
        16
        17    @PostMapping("/add")
        18    public String insertOrUpdate(@RequestBody RouteDefinition routeDefinition){
        19        return dynamicRouteService.add(routeDefinition);
        20    }
        21
        22}

        每次对网关操作完成后,都需要通知gateway进行主动刷新。刷新方式是发出RefreshRoutesEvent事件。

        完整的增删改查service代码如下:

         1@Service
        2public class DynamicRouteService implements ApplicationEventPublisherAware {
        3
        4    @Resource
        5    private RouteDefinitionRepository routeDefinitionWriter;
        6
        7    private ApplicationEventPublisher publisher;
        8
        9    private void notifyChanged() {
        10        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        11    }
        12
        13    public Flux<RouteDefinition> getAllRoute(){
        14        return routeDefinitionWriter.getRouteDefinitions();
        15    }
        16
        17
        18
        19    /**
        20     * 增加路由
        21     *
        22     */

        23    public String add(RouteDefinition definition) {
        24        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        25        notifyChanged();
        26        return "success";
        27    }
        28
        29
        30    /**
        31     * 更新路由
        32     */

        33    public String update(RouteDefinition definition) {
        34        try {
        35            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        36        } catch (Exception e) {
        37            return "update fail,not find route  routeId: " + definition.getId();
        38        }
        39        try {
        40            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        41            notifyChanged();
        42            return "success";
        43        } catch (Exception e) {
        44            return "update route  fail";
        45        }
        46
        47
        48    }
        49
        50    /**
        51     * 删除路由
        52     *
        53     */

        54    public String delete(String id) {
        55        try {
        56            this.routeDefinitionWriter.delete(Mono.just(id));
        57
        58            notifyChanged();
        59            return "delete success";
        60        } catch (Exception e) {
        61            e.printStackTrace();
        62            return "delete fail";
        63        }
        64
        65    }
        66
        67    @Override
        68    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        69        this.publisher = applicationEventPublisher;
        70    }
        71
        72}



        完成之后我们就可以用postman进行增删改查。

        或使用curl进行测试:

          curl -H "Content-Type:application/json" -X POST --data '{"filters":[],"id":"1","order":0,"predicates":[{"args":{"pattern":"/**"},"name":"Path"}],"uri":"lb://SERVER-DEMO"}' http://127.0.0.1:8080/add


          代码下载

          https://github.com/loveoobaby/blog_code/tree/master/spring_gateway


          原文地址:

          https://blog.csdn.net/tianyaleixiaowu/article/details/83412301


          文章转载自CodingWithFun,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

          评论