spring cloud gateway 实现redis动态路由及自动项目路由上报

前言

spring cloud gateway默认为内存存储策略,通过配置文件加载的方式生成路由定义信息
在这里插入图片描述
在这里插入图片描述

可以看到,RouteDefinitionRepository继承了两个父接口,分别为RouteDefinitionLocator和RouteDefinitionWriter,RouteDefinitionLocator定义了路由定义获取接口,而RouteDefinitionWriter则定义了路由定义保存更新save接口和删除接口。

可以看到RouteDefinitionRepository是核心接口,其默认的实现类有上图两个,分别是基于内存和基于redis的,如果需要redis实现动态路由则只需要实现RouteDefinitionRepository接口并注入bean即可

一、实现RouteDefinitionRepository接口

这里其实spring cloud gateway已经有默认实现了的基于redis 的路由定义相关的repository,但由于其并没有做本地缓存,是纯redis的方式读取写入,这样会导致在进行路由调用的时候频繁的调用其getRouteDefinitions方法,频繁的读取redis其实也是有一定的性能损耗,因此我们其实稍加利用,添加本地缓存即可(本示例使用咖啡因缓存),注意添加注解:@EnableCaching

/**
 * @classDesc:
 * @author: cyjer
 * @date: 2023/1/30 9:53
 */
@Component
@Slf4j
public class RedisRouteRepository implements RouteDefinitionRepository {
    private static final String ROUTE_DEFINITION_REDIS_KEY_PREFIX_QUERY = "routeDefinition::";
    private final ReactiveRedisTemplate<String, RouteDefinition> reactiveRedisTemplate;
    private final ReactiveValueOperations<String, RouteDefinition> routeDefinitionReactiveValueOperations;

    public RedisRouteRepository(ReactiveRedisTemplate<String, RouteDefinition> reactiveRedisTemplate) {
        this.reactiveRedisTemplate = reactiveRedisTemplate;
        this.routeDefinitionReactiveValueOperations = reactiveRedisTemplate.opsForValue();
    }

    @Override
    @Cacheable(cacheNames = "redisRoute")
    public Flux<RouteDefinition> getRouteDefinitions() {
        log.info("<<<<<<<<<<获取路由信息>>>>>>>>>>");
        return this.reactiveRedisTemplate.keys(this.createKey("*")).flatMap((key) -> {
            return this.reactiveRedisTemplate.opsForValue().get(key);
        }).onErrorContinue((throwable, routeDefinition) -> {
            if (log.isErrorEnabled()) {
                log.error("get routes from redis error cause : {}", throwable.toString(), throwable);
            }

        });
    }

    @Override
    @CacheEvict(cacheNames = "redisRoute")
    public Mono<Void> save(Mono<RouteDefinition> route) {
        log.info("<<<<<<<<<<保存路由信息>>>>>>>>>>");
        return route.flatMap((routeDefinition) -> {
            return this.routeDefinitionReactiveValueOperations.set(this.createKey(routeDefinition.getId()), routeDefinition).flatMap((success) -> {
                return success ? Mono.empty() : Mono.defer(() -> {
                    return Mono.error(new RuntimeException(String.format("Could not add route to redis repository: %s", routeDefinition)));
                });
            });
        });
    }

    @Override
    @CacheEvict(cacheNames = "redisRoute")
    public Mono<Void> delete(Mono<String> routeId) {
        log.info("<<<<<<<<<<删除路由信息>>>>>>>>>>");
        return routeId.flatMap((id) -> {
            return this.routeDefinitionReactiveValueOperations.delete(this.createKey(id)).flatMap((success) -> {
                return success ? Mono.empty() : Mono.defer(() -> {
                    return Mono.error(new NotFoundException(String.format("Could not remove route from redis repository with id: %s", routeId)));
                });
            });
        });
    }

    private String createKey(String routeId) {
        return ROUTE_DEFINITION_REDIS_KEY_PREFIX_QUERY + routeId;
    }

}
/**
 * @classDesc: 本地缓存
 * @author: cyjer
 * @date: 2023/1/30 9:53
 */
@Configuration
public class CaffeineConfig {
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<CaffeineCache> caches = new ArrayList<>();
        Map<String, Object> map = getCacheType();
        for (String name : map.keySet()) {
            caches.add(new CaffeineCache(name, (Cache<Object, Object>) map.get(name)));
        }
        cacheManager.setCaches(caches);
        return cacheManager;
    }

    /**
     * 初始化自定义缓存策略
     *
     * @return
     */
    private static Map<String, Object> getCacheType() {
        Map<String, Object> map = new ConcurrentHashMap<>();
        map.put("redisRoute", Caffeine.newBuilder().build());
        return map;
    }
}

到此其实基于redis的路由仓储service已经结束

基于zookeeper注册中心实现项目自动上报路由

为了实现动态路由,其实可以采用提供暴露接口的方式把相应的仓储接口提供出来,本示例提供的只是使用zk的监听机制,实现动态路由

关于zk的事件监听可以查看我的这篇文章:zookeeper相关操作

1、网关启动时添加zk监听事件

@Component
@Slf4j
@RequiredArgsConstructor
public class GatewayApplication implements ApplicationListener<ContextRefreshedEvent> {
    private final RouteDefinitionService routeDefinitionService;
    private final ZookeeperService zookeeperService;
    private final RouteProcesser routeProcesser;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //拉取网关配置
        routeDefinitionService.refreshRouteDefinition();
        log.info("<<<<<<<<<<刷新网关配置完成>>>>>>>>>>");
        zookeeperService.create(Constraint.ROUTE_DEFINITION, "init");
        zookeeperService.addWatchChildListener(Constraint.ROUTE_DEFINITION, routeProcesser);
        log.info("<<<<<<<<<<动态路由监听器配置完成>>>>>>>>>>");
    }
}

2、发布刷新事件:

    public void refreshRouteDefinition() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
        redisRouteRepository.getRouteDefinitions();
    }

3、 监听处理

/**
 * @classDesc: 
 * @author: cyjer
 * @date: 2023/2/10 11:13
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RouteProcesser extends AbstractChildListenerProcess implements ApiDefinitionConstraint {
    private static final String JTR = "/";
    private final static String ROUTE_CONFIG_ARGS_PREFIX = "_genkey_";
    private final RedisRouteRepository redisRouteRepository;
    private final RouteDefinitionService routeDefinitionService;

    @Override
    public void process(CuratorFramework curatorFramework, PathChildrenCacheEvent cacheEvent) {
        ChildData data = cacheEvent.getData();
        if (Objects.nonNull(data) && cacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {
            log.info("<<<<<<<<<<监听到动态路由变动申请>>>>>>>>>>");
            String content = new String(data.getData(), StandardCharsets.UTF_8);
            SiriusRouteDefinition siriusRouteDefinition = JSONObject.parseObject(content, SiriusRouteDefinition.class);
            RouteDefinition routeDefinition = new RouteDefinition();
            String serviceId = siriusRouteDefinition.getServiceId();
            routeDefinition.setId(serviceId);
            URI routeUri = UriComponentsBuilder.fromUriString("lb://" + serviceId).build().toUri();
            routeDefinition.setUri(routeUri);
            List<PredicateDefinition> predicates = getPredicates(JTR + serviceId + "/**");
            if (!predicates.isEmpty()) {
                routeDefinition.setPredicates(predicates);
            }
            // 获取过滤器
            List<FilterDefinition> filters = new ArrayList<>();
            Map<String, String> args = new LinkedHashMap<>();
            args.put(ROUTE_CONFIG_ARGS_PREFIX, "1");
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setName("StripPrefix");
            filterDefinition.setArgs(args);
            filters.add(filterDefinition);
            routeDefinition.setFilters(filters);
            redisRouteRepository.save(Mono.just(routeDefinition)).subscribe();
            routeDefinitionService.refreshRouteDefinition();
            log.info("<<<<<<<<<<刷新网关路由成功>>>>>>>>>>");
        }

    }


    /**
     * 获取地址断言
     *
     * @param routePaths 路由地址
     * @return java.util.List
     */
    private List<PredicateDefinition> getPredicates(String routePaths) {
        Map<String, String> args = new HashMap<>();
        String[] paths = routePaths.split(",");
        for (int i = 0; i < paths.length; i++) {
            args.put(ROUTE_CONFIG_ARGS_PREFIX + i, paths[i]);
        }
        PredicateDefinition predicate = new PredicateDefinition();
        predicate.setName("Path");
        predicate.setArgs(args);

        List<PredicateDefinition> predicates = new ArrayList<>();
        predicates.add(predicate);
        return predicates;
    }

}

4、非网关的其他项目启动时注册路由

/**
 * @classDesc: 注册应用路由信息
 * @author: cyjer
 * @date: 2023/2/11 20:47
 */
@Component
@Order(2)
@RequiredArgsConstructor
@Slf4j
public class ServiceRouteDefinitionReporter implements CommandLineRunner, Constraint {
    private final GatewayServiceProperties gatewayServiceProperties;
    private final ZookeeperService zookeeperService;

    @Override
    public void run(String... args) throws Exception {
        
            String applicationName = apiDefinitionReporter.getApplicationName();
            siriusRouteDefinition.setServiceId(applicationName);
            siriusRouteDefinition.setGatewayId(gatewayServiceProperties.getGatewayId());
            zookeeperService.create(ROUTE_DEFINITION + SPLIT + applicationName, JSONObject.toJSONString(siriusRouteDefinition));
            zookeeperService.update(ROUTE_DEFINITION + SPLIT + applicationName, JSONObject.toJSONString(siriusRouteDefinition));
            log.info("<<<<< successfully reported route information >>>>>");
        }
    
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Cloud Gateway中,我们可以使用Nacos来实现动态路由的更新。Nacos是一个服务发现和配置管理平台,可以用来实现服务注册和发现、配置管理、动态路由等功能。下面是一个使用Nacos实现动态路由更新的示例代码: ```java @Configuration public class GatewayConfig { @Autowired private NacosConfigProperties nacosConfigProperties; @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Autowired private NacosRouteDefinitionRepository nacosRouteDefinitionRepository; @Bean public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() { return new NacosRouteDefinitionRepository(nacosConfigProperties, nacosDiscoveryProperties); } @Bean public DynamicRouteServiceImpl dynamicRouteService() { return new DynamicRouteServiceImpl(nacosRouteDefinitionRepository); } @Bean public NacosGatewayProperties nacosGatewayProperties() { return new NacosGatewayProperties(); } @Bean public NacosGatewayPropertiesManager nacosGatewayPropertiesManager() { return new NacosGatewayPropertiesManager(nacosConfigProperties, nacosGatewayProperties()); } @Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); } @Bean public DynamicRouteDefinitionLocator dynamicRouteDefinitionLocator() { return new NacosDynamicRouteDefinitionLocator(nacosGatewayPropertiesManager(), gatewayProperties()); } @Bean public DynamicRouteDefinitionService dynamicRouteDefinitionService() { return new NacosDynamicRouteDefinitionService(nacosRouteDefinitionRepository()); } @Bean public RouteDefinitionWriter routeDefinitionWriter() { return new InMemoryRouteDefinitionRepository(); } @Bean public RouteDefinitionRepository routeDefinitionRepository() { return new CompositeRouteDefinitionRepository(routeDefinitionWriter(), nacosRouteDefinitionRepository()); } @Bean public RouteDefinitionLocator routeDefinitionLocator() { return new CompositeRouteDefinitionLocator(dynamicRouteDefinitionLocator(), new PropertiesRouteDefinitionLocator(gatewayProperties())); } @Bean public RouteRefreshListener routeRefreshListener() { return new RouteRefreshListener(dynamicRouteService()); } @Bean public RouteRefreshAutoConfiguration routeRefreshAutoConfiguration() { return new RouteRefreshAutoConfiguration(dynamicRouteService(), routeRefreshListener()); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public RouteDefinitionLocator routeDefinitionLocator() { return new CompositeRouteDefinitionLocator(dynamicRouteDefinitionLocator(), new PropertiesRouteDefinitionLocator(gatewayProperties())); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public RouteDefinitionWriter routeDefinitionWriter() { return new InMemoryRouteDefinitionRepository(); } @Bean public RouteDefinitionRepository routeDefinitionRepository() { return new CompositeRouteDefinitionRepository(routeDefinitionWriter(), nacosRouteDefinitionRepository()); } @Bean public RouteRefreshListener routeRefreshListener() { return new RouteRefreshListener(dynamicRouteService()); } @Bean public RouteRefreshAutoConfiguration routeRefreshAutoConfiguration() { return new RouteRefreshAutoConfiguration(dynamicRouteService(), routeRefreshListener()); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public RouteDefinitionLocator routeDefinitionLocator() { return new CompositeRouteDefinitionLocator(dynamicRouteDefinitionLocator(), new PropertiesRouteDefinitionLocator(gatewayProperties())); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); } @Bean public RouteDefinitionWriter routeDefinitionWriter() { return new InMemoryRouteDefinitionRepository(); } @Bean public RouteDefinitionRepository routeDefinitionRepository() { return new CompositeRouteDefinitionRepository(routeDefinitionWriter(), nacosRouteDefinitionRepository()); } @Bean public RouteLocator routeLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public RouteDefinitionLocator routeDefinitionLocator() { return new CompositeRouteDefinitionLocator(dynamicRouteDefinitionLocator(), new PropertiesRouteDefinitionLocator(gatewayProperties())); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public GatewayFilterChainFilter gatewayFilterChainFilter(RouteLocator routeLocator, List<GlobalFilter> globalFilters) { return new GatewayFilterChainFilter(routeLocator, globalFilters); } @Bean public GlobalFilterAdder globalFilterAdder(List<GlobalFilter> globalFilters) { return new GlobalFilterAdder(globalFilters); } @Bean @ConditionalOnMissingBean public NacosRouteDefinitionRepository nacosRouteDefinitionRepository() { return new NacosRouteDefinitionRepository(nacosConfigProperties, nacosDiscoveryProperties); } @Bean public NacosGatewayProperties nacosGatewayProperties() { return new NacosGatewayProperties(); } @Bean public NacosGatewayPropertiesManager nacosGatewayPropertiesManager() { return new NacosGatewayPropertiesManager(nacosConfigProperties, nacosGatewayProperties()); } @Bean public DynamicRouteDefinitionLocator dynamicRouteDefinitionLocator() { return new NacosDynamicRouteDefinitionLocator(nacosGatewayPropertiesManager(), gatewayProperties()); } @Bean public DynamicRouteDefinitionService dynamicRouteDefinitionService() { return new NacosDynamicRouteDefinitionService(nacosRouteDefinitionRepository()); } @Bean public RouteDefinitionWriter routeDefinitionWriter() { return new InMemoryRouteDefinitionRepository(); } @Bean public RouteDefinitionRepository routeDefinitionRepository() { return new CompositeRouteDefinitionRepository(routeDefinitionWriter(), nacosRouteDefinitionRepository()); } @Bean public RouteRefreshListener routeRefreshListener() { return new RouteRefreshListener(dynamicRouteService()); } @Bean public RouteRefreshAutoConfiguration routeRefreshAutoConfiguration() { return new RouteRefreshAutoConfiguration(dynamicRouteService(), routeRefreshListener()); } @Bean public RouteDefinitionRouteLocator routeDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public RouteLocator routeLocator(RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator); } @Bean public GatewayFilterChainFilter gatewayFilterChainFilter(RouteLocator routeLocator, List<GlobalFilter> globalFilters) { return new GatewayFilterChainFilter(routeLocator, globalFilters); } @Bean public GlobalFilterAdder globalFilterAdder(List<GlobalFilter> globalFilters) { return new GlobalFilterAdder(globalFilters); } } ``` 在上面的代码中,我们定义了一个GatewayConfig类,用来配置Spring Cloud Gateway的相关组件。首先,我们注入了NacosConfigProperties、NacosDiscoveryProperties和NacosRouteDefinitionRepository等相关组件。然后,我们定义了DynamicRouteServiceImpl、NacosGatewayProperties、NacosGatewayPropertiesManager、GatewayProperties、DynamicRouteDefinitionLocator、DynamicRouteDefinitionService、RouteDefinitionWriter、RouteDefinitionRepository、RouteRefreshListener、RouteRefreshAutoConfiguration、RouteDefinitionRouteLocator、RouteLocator、GatewayFilterChainFilter和GlobalFilterAdder等相关组件。 通过配置这些组件,我们可以实现基于Nacos的动态路由更新功能。具体来说,我们通过NacosRouteDefinitionRepository动态路由定义保存到Nacos配置中心,然后通过DynamicRouteServiceImpl实现动态路由的更新。在实际使用中,我们可以通过Nacos配置中心来管理动态路由的配置,从而实现路由的动态更新。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮特猫.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值