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

news2024/12/23 19:10:57

前言

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 >>>>>");
        }
    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/359019.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java并发知识点

文章目录1. start()和run()方法的区别&#xff1f;2. volatile关键字的作用&#xff1f;使用volatile能够保证&#xff1a;防止指令重排3. sleep方法和wait方法有什么区别&#xff1f;sleep()方法4. 如何停止一个正在运行的线程&#xff1f;方法一&#xff1a;方法二&#xff1…

MindFusion Diagramming for Java, 最新版 Crack

Diagramming for Java, V4.6.1 A unique Java Swing library for any type of flowchart.您需要的每一个图表功能 图表、方案、图形、网络、算法、树、图表 - 所有这些都是使用 MindFusion Diagramming for Java 工具快速轻松地构建的。结果令人着迷。 Java Dagram 库&#xff…

论文阅读 | Real-Time Intermediate Flow Estimation for Video Frame Interpolation

前言&#xff1a;ECCV2022 快速插帧方法 Real-Time Intermediate Flow Estimation for Video Frame Interpolation 引言 进行视频插帧目前比较常见的方法是基于光流法&#xff0c;分为两个步骤&#xff1a;1.通过光流对齐输入帧&#xff0c;融合对齐的帧 光流并不能直接同于…

CS224W课程学习笔记(三):DeepWalk算法原理与说明

引言 什么是图嵌入&#xff1f; 图嵌入&#xff08;Graph Embedding&#xff0c;也叫Network Embedding&#xff09; 是一种将图数据&#xff08;通常为高维稠密的矩阵&#xff09;映射为低微稠密向量的过程&#xff0c;能够很好地解决图数据难以高效输入机器学习算法的问题。…

arxiv2017 | 用于分子神经网络建模的数据增强 SMILES Enumeration

论文标题&#xff1a;SMILES Enumeration as Data Augmentation for Neural Network Modeling of Molecules论文地址&#xff1a;https://arxiv.org/abs/1703.07076代码地址&#xff1a;https://github.com/Ebjerrum/SMILES-enumeration一、摘要摘要中明显提出&#xff1a;先指…

TCP/IP网络编程——多播与广播

完整版文章请参考&#xff1a; TCP/IP网络编程完整版文章 文章目录第 14 章 多播与广播14.1 多播14.1.1 多播的数据传输方式以及流量方面的优点14.1.2 路由&#xff08;Routing&#xff09;和 TTL&#xff08;Time to Live,生存时间&#xff09;&#xff0c;以及加入组的办法14…

STM32开发(11)----CubeMX配置独立看门狗(IWDG)

CubeMX配置独立看门狗&#xff08;IWDG&#xff09;前言一、独立看门狗的介绍二、实验过程1.STM32CubeMX配置独立看门狗2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对独立看门狗定时器进行配置的方法。门狗本质上是一个定时器&#xff0c;提供了更高的安…

华为云计算之容灾技术

容灾是物理上的容错技术&#xff0c;不是逻辑上的容错同步远程复制&#xff1a;主备距离≤200km&#xff0c;只有在主备设备上都写成功&#xff0c;才会告诉主机写成功&#xff0c;不会丢失数据异步远程复制&#xff1a;主备距离&#xff1e;200km&#xff0c;只要主设备上写成…

掌握MySQL分库分表(二)Mysql数据库垂直分库分表、水平分库分表

文章目录垂直分表拆分方法举例垂直分库水平分表水平分库小结垂直角度&#xff08;表结构不一样&#xff09;水平角度&#xff08;表结构一样&#xff09;垂直分表 需求&#xff1a;商品表字段太多&#xff0c;每个字段访问频次不⼀样&#xff0c;浪费了IO资源&#xff0c;需要…

标题标题标题

图床&#xff08;Typora uPic/PicGo 七牛云&#xff09; 图床&#xff08;Typora uPic/PicGo 七牛云&#xff09; 笔者平时使用 Typora 编写 markdown 文档&#xff0c;文档中常常会放置图片&#xff0c;如果文档不需要分享的话&#xff0c;其实讲图片存放在本地就可以了…

SpringCloud alibaba-Sentinel服务降级策略

文章目录RT&#xff1a;异常比例&#xff1a;异常数&#xff1a;RT&#xff1a; 平均响应时间 (DEGRADE_GRADE_RT)&#xff1a;当 1s 内持续进入 N 个请求&#xff0c;对应时刻的平均响应时间&#xff08;秒级&#xff09;均超过阈值&#xff08;count&#xff0c;以 ms 为单位…

一文吃透 Spring 中的IOC和DI(二)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【前端八股文】浏览器系列:单进程与多进程、浏览器进程、异步、事件循环、同源策略、输入URL回车后、TCP三次挥手四次握手

文章目录概述单进程VS多进程浏览器进程主要分为异步场景事件循环同源策略与跨域问题输入URL回车后过程URL几大部分TCP连接与释放TCP三次握手TCP四次挥手参考本系列目录&#xff1a;【前端八股文】目录总结 是以《代码随想录》八股文为主的笔记。详情参考在文末。 代码随想录的博…

AI又进化了,突破性革命来了

大家好&#xff0c;我是 Jack。 2023 年&#xff0c;AI 真的杀疯了。短短不到一年的时间&#xff0c;当我们还在感慨 AI 一键生成的二次元画作精美万分的时候&#xff0c;它已经进化到了写实美照也能手到擒来的地步。 更多的效果&#xff0c;可以看刚刚发布的视频&#xff0c;…

Java爬虫系列 - 爬虫补充内容+ElasticSearch展示数据

一&#xff0c;定时任务Cron表达式Component public class TaskTest {Scheduled(cron "0/5 * * * * *") // 从0秒开始&#xff0c;每个五秒 执行一次 { 秒 分 时 天 月 周 }public void test(){System.out.println("定时任务执行了");} }二&#xff0c;网…

第一章 初识 Spring Security

第一章 初识 Spring Security 1、权限管理 权限管理 基本上涉及到用户参与的系统都要进行权限管理&#xff0c;权限管理属于系统安全的范畴&#xff0c;权限管理实现了对用户访问系统的控制&#xff0c;按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资…

【白话科普】聊聊网络架构变革的关键——SDN

最近二狗子在网上冲浪的时候&#xff0c;不小心将 CDN 搜索成了 SDN&#xff0c;结果跳出来了一大堆相关的知识点。 好学的二狗子当然不会随随便便糊弄过去&#xff0c;于是认认真真学习了好久&#xff0c;终于了解了 SDN 是什么。 原来&#xff0c;SDN 的全称是 Software De…

第十一届“泰迪杯”数据挖掘挑战赛携“十万”大奖火热来袭

第十一届“泰迪杯”数据挖掘挑战赛 竞赛组织 主办单位&#xff1a; 泰迪杯数据挖掘挑战赛组织委员会 承办单位&#xff1a; 广东泰迪智能科技股份有限公司 人民邮电出版社 协办单位&#xff1a; 重庆市工业与应用数学学会、广东省工业与应用数学学会、广西数学学会、河北省工业…

心跳机制Redis

 进入命令传播阶段候&#xff0c;master与slave间需要进行信息交换&#xff0c;使用心跳机制进行维护&#xff0c;实现双方连接保持在线 master心跳&#xff1a; 指令&#xff1a;PING 周期&#xff1a;由repl-ping-slave-period决定&#xff0c;默认10秒 作用&#…

4|无线传感器网络与应用|无线传感器网络原理及方法-许毅版|第3章:无线传感器网络通信-3.1协议结构 3.2物理层|青岛科技大学|课堂笔记

第3章&#xff1a;无线传感器网络通信3.1协议结构3.1.1 OSI参考模型1.网络通信协议MAC层和物理层采用IEEE 802.15.4协议*(1)物理层wsn物理层负责信号的调制和数据的收发&#xff0c;传输介质&#xff1a;无线电、红外线、光波等。(2)数据链路层wsn数据链路层负责数据成帧、帧检…