Spring Cloud Gateway(十三):基于MongoDB实现动态路由

x33g5p2x  于2021-12-20 转载在 Go  
字(6.2k)|赞(0)|评价(0)|浏览(344)

1、概述

Spring Cloud Gateway 默认的RouteDefinitionWriter实现类是org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository,Route信息保存在当前实例的内存中,这在集群环境中会存在同步问题。本文就基于MongoD自定义一个RouteDefinitionWriter。

基本要点:

1、路由规则存储在MongoDB中(也可以是别的存储介质)

2、网关启动读取已配置好的路由规则

3、首次读取从MongoDB中加载,同时存在缓存中,以后读取直接从缓存读取

4、通过api动态添加路由规则,同时添加到缓存和MongoDB中

2、MongoRouteDefinition

import lombok.Data;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.data.mongodb.core.mapping.Field;

import java.util.ArrayList;
import java.util.List;

/**
 * 存储在mongodb中的自定义路由信息
 *
 * @author lkf
 * @date 2018-11-29 10-51
 */
@Data
public class MongoRouteDefinition {

    /**
     * 路由id
     */
    @Field("route_id")
    private String routeId;
    /**
     * 路由谓词
     */
    private List<PredicateDefinition> predicates = new ArrayList<>();
    /**
     * 过滤器
     */
    private List<FilterDefinition> filters = new ArrayList<>();
    /**
     * 跳转地址uri
     */
    private String uri;
    /**
     * 路由顺序
     */
    private int order;

}

3、自定义RouteDefinitionRepository

package com.lkf.gateway.repository;

import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.data.mongodb.core.MongoTemplate;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.*;

import static java.util.Collections.synchronizedMap;

/**
 * 使用Mongo保存自定义路由配置(代替默认的InMemoryRouteDefinitionRepository)
 * <p/>
 * 首次调用会把自定义路由配置信息加载到缓存中,以后的每次调用都从缓存返回
 *
 * @author lkf
 * @date 2018-11-29 10-48
 */
public class MongoRouteDefinitionRepository implements RouteDefinitionRepository {
    private final Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());

    private final MongoTemplate mongoTemplate;
    private final String mongoCollection;

    public MongoRouteDefinitionRepository(MongoTemplate mongoTemplate,String mongoCollection ) {
        this.mongoTemplate = mongoTemplate;
        this.mongoCollection = mongoCollection;
    }

    /**
     * 获取自定义路由信息
     *
     * @author lkf
     * @date 2018/11/29 12:00
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        //判断本地缓存是否为空,不为空直接返回
        if (Objects.nonNull(routes) && routes.size() > 0) {
            return getRoutes();
        }
        //从mongodb获取自定义路由信息
        List<MongoRouteDefinition> mongoRouteDefinitionList = mongoTemplate.findAll(MongoRouteDefinition.class, mongoCollection);
        List<RouteDefinition> routeDefinitions = new ArrayList<>();
        //转换为路由定义对象并存入缓存中
        mongoRouteDefinitionList.forEach(mongoRouteDefinition -> {
            RouteDefinition routeDefinition = new RouteDefinition();
            routeDefinition.setId(mongoRouteDefinition.getRouteId());
            routeDefinition.setFilters(mongoRouteDefinition.getFilters());
            routeDefinition.setPredicates(mongoRouteDefinition.getPredicates());
            routeDefinition.setUri(URI.create(mongoRouteDefinition.getUri()));
            routeDefinition.setOrder(mongoRouteDefinition.getOrder());
            routes.put(routeDefinition.getId(), routeDefinition);
        });
        return Flux.fromIterable(routeDefinitions);
    }

    /**
     * 新增路由信息
     *
     * @param route 路由定义对象
     * @return reactor.core.publisher.Mono<java.lang.Void>
     * @author lkf
     * @date 2018/11/29 13:08
     */
    @Override
    public Mono<Void> save( Mono<RouteDefinition> route ) {
        return route.flatMap(routeDefinition -> {
            MongoRouteDefinition mongoRouteDefinition = new MongoRouteDefinition();
            mongoRouteDefinition.setRouteId(routeDefinition.getId());
            mongoRouteDefinition.setPredicates(routeDefinition.getPredicates());
            mongoRouteDefinition.setFilters(routeDefinition.getFilters());
            mongoRouteDefinition.setUri(routeDefinition.getUri().toString());
            mongoRouteDefinition.setOrder(routeDefinition.getOrder());
            mongoTemplate.save(mongoRouteDefinition, mongoCollection);
            routes.put(routeDefinition.getId(), routeDefinition);
            return Mono.empty();
        });

    }

    /**
     * 删除路由信息
     *
     * @param routeId 路由id
     * @return reactor.core.publisher.Mono<java.lang.Void>
     * @author lkf
     * @date 2018/11/29 13:09
     */
    @Override
    public Mono<Void> delete( Mono<String> routeId ) {
        return routeId.flatMap(id -> {
            if (routes.containsKey(id)) {
                routes.remove(id);
                return Mono.empty();
            }
            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
        });
    }

    private Flux<RouteDefinition> getRoutes() {
        return Flux.fromIterable(routes.values());
    }

}

4、自定义网关属性实体 CmpGatewayProperties

/**
 * 自定义网关属性实体
 *
 * @author lkf
 * @date 2018-11-29 13-13
 */
@Data
@ConfigurationProperties(prefix = "cmp.gateway.route")
public class CmpGatewayProperties {
    private String collection;
}

5、添加配置

## MongoDB 连接地址
spring.data.mongodb.uri=mongodb://账号:密码@ip:37017/库名
## 存放路由规则的 collection
cmp.gateway.route.collection=gateway_route

6、验证

6.1、MongoDB 添加路由规则

{
    "_id" : ObjectId("5bff4e9c12cc87ac0f888ddc"),
    "route_id" : "service-001",
    "predicates" : [ 
        {
            "name" : "Path",
            "args" : {
                "pattern" : "/user/**"
            }
        }
    ],
    "filters" : [ 
        {
            "name" : "RewritePath",
            "args" : {
                "regexp" : "/user-service/(?<remaining>.*)",
                "replacement" : "/${remaining}"
            }
        }
    ],
    "uri" : "lb://user-service",
    "order" : 0
}

6.2、api 获取路由信息

通过网关暴露的api地址:

http://localhost:9000/actuator/gateway/routes

{
    "route_id": "service-001",
    "route_definition": {
        "id": "service-001",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "pattern": "/user/**"
                }
            }
        ],
        "filters": [
            {
                "name": "RewritePath",
                "args": {
                    "regexp": "/user-service/(?<remaining>.*)",
                    "replacement": "/${remaining}"
                }
            }
        ],
        "uri": "lb://user-service",
        "order": 0
    },
    "order": 0
}

6.3、api 新增路由信息

http://localhost:9000/actuator/gateway/routes/service-002

curl -H "Content-Type:application/json" -X POST --data '{
    "predicates" : [ 
        {
            "name" : "Path",
            "args" : {
                "pattern" : "/user/**"
            }
        }
    ],
    "filters" : [ 
        {
            "name" : "RewritePath",
            "args" : {
                "regexp" : "/user-service/(?<remaining>.*)",
                "replacement" : "/${remaining}"
            }
        }
    ],
    "uri" : "lb://user-service",
    "order" : 0
}' http://localhost:9000/actuator/gateway/routes/service-002

再次查询路由,结果如下,已经新增成功:

相关文章