Skip to content

ThinkingJava/dynroute

Repository files navigation

动态网关路由

1 前言

​ 上个月入职了新公司,发现中台网关路由配置是放在apollo上的,所以这次目的是设计一款动态路由配置的网关。

2 设计

​ 由于Gateway 的传统的通过配置中心及配置文件的方式配置路由略显得笨拙,通过api或者图形界面修改,则显得相对灵活的多。

2.1 技术选型

通过重写 spring cloud gateway 实现API动态更改路由,非常便捷的实现控制请求接入管理。路由信息使用mysql作为持久化存储,初始化启动加载,redis作为临时存储并发布订阅监听。

2.2 模块划分

将项目划分为以下几个模块

名称描述
dynroute-api一些公共的代码,常量,异常类等。
dynroute-core测试应用功能模块
dynroute-gateway网关应用,鉴权,负载均衡,路由转发等
dynroute-server测试应用功能模块

3 重构网关说明

3.1 网关接入数据库加载路由信息

  • 创建数据库表用于保存路由配置

    CREATETABLE `gateway_route_config` ( `id`int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `route_name`varchar(30) DEFAULT NULL, `route_id`varchar(30) DEFAULT NULL, `predicates` json DEFAULT NULL COMMENT '断言', `filters` json DEFAULT NULL COMMENT '过滤器', `uri`varchar(50) DEFAULT NULL, `order`int(2) DEFAULT '0' COMMENT '排序', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT NULLONUPDATECURRENT_TIMESTAMP COMMENT '修改时间', `del_flag`char(1) DEFAULT '0', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='路由配置表';
  • 初始化网关配置为空

    /** * 配置文件设置为空 redis 加载为准 * @return */@BeanpublicPropertiesRouteDefinitionLocatorpropertiesRouteDefinitionLocator(){returnnewPropertiesRouteDefinitionLocator(newGatewayProperties())}
  • 初始化路由配置

    publicvoidinitRoute(){redisTemplate.delete(CacheConstants.ROUTE_KEY); log.info("开始初始化网关路由"); gatewayRouteConfigService.list().forEach(route ->{RouteDefinitionVovo = newRouteDefinitionVo(); vo.setRouteName(route.getRouteName()); vo.setId(route.getRouteId()); vo.setUri(URI.create(route.getUri())); vo.setOrder(route.getOrder()); JSONArrayfilterObj = JSONUtil.parseArray(route.getFilters()); vo.setFilters(filterObj.toList(FilterDefinition.class)); JSONArraypredicateObj = JSONUtil.parseArray(route.getPredicates()); vo.setPredicates(predicateObj.toList(PredicateDefinition.class)); log.info("加载路由ID:{},{}", route.getRouteId(), vo); redisTemplate.setHashValueSerializer(newJackson2JsonRedisSerializer<>(RouteDefinitionVo.class)); redisTemplate.opsForHash().put(CacheConstants.ROUTE_KEY, route.getRouteId(), vo)}); // 通知网关重置路由redisTemplate.convertAndSend(CacheConstants.ROUTE_JVM_RELOAD_TOPIC, "路由信息,网关缓存更新"); log.debug("初始化网关路由结束 ")}
  • 设置监听路由配置,并监听订阅

publicRedisMessageListenerContainerredisContainer(RedisConnectionFactoryredisConnectionFactory){RedisMessageListenerContainercontainer = newRedisMessageListenerContainer(); container.setConnectionFactory(redisConnectionFactory); container.addMessageListener((message, bytes) ->{log.warn("接收到重新JVM 重新加载路由事件"); RouteCacheHolder.removeRouteList(); // 发送刷新路由事件SpringContextHolder.publishEvent(newRefreshRoutesEvent(this))}, newChannelTopic(CacheConstants.ROUTE_JVM_RELOAD_TOPIC)); returncontainer}
  • 路由配置查询及修改
/** * 路由配置查询及修改 * @param serverRequest * @return */@SneakyThrows@OverridepublicMono<ServerResponse> handle(ServerRequestserverRequest){HttpMethodmethod = serverRequest.method(); if (method == HttpMethod.GET){returnServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(objectMapper.writeValueAsString(Response.ok(gatewayRouteConfigService.list()))))} elseif (method == HttpMethod.PUT){Mono<Response> routeStatus = serverRequest.bodyToMono(JSONArray.class).flatMap(updateRoutes()); BodyInserterbodyInserter = BodyInserters.fromPublisher(routeStatus, Response.class); returnServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(bodyInserter)} returnServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON) .body(BodyInserters.fromValue(objectMapper.writeValueAsString(Response.failed())))}

4 本地调试

4.1 本地启动nacos服务。

从 Github 上下载源码方式

git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U ls -al distribution/target/ // change the $version to your actual path cd distribution/target/nacos-server-$version/nacos/bin //启动nacos startup.cmd -m standalone

4.2 Redis和Mysql

4.3 往mysql插入路由配置

INSERT INTO`gateway_route_config`(`id`,`route_name`,`route_id`,`predicates`,`filters`,`uri`,`order`,`create_time`,`update_time`,`del_flag`) VALUES (1,'动态网关测试','dynroute-server','[{\"args\":{\"_genkey_0\": \"/server/**\"}, \"name\": \"Path\"}]','[]','lb://dynroute-server',0,'2021-05-04 16:44:41','2021-05-05 04:48:51','0');

4.2 启动网关Gateway

实例1配置: 在启动参数VM options 添加 -Dserver.port=9999

4.3 启动dynroute-core

4.5 启动dynroute-server

4.6 postman调试

这个时候服务core服务是不通的。

image

新增网关路由配置

[{"routeId": "dynroute-server", "routeName": "动态网关测试", "predicates": [{"args":{"_genkey_0": "/server/**" }, "name": "Path" } ], "filters": [], "uri": "lb://dynroute-server", "order": 1, "delFlag": "0" },{"routeId": "dynroute-core", "routeName": "动态网关测试", "predicates": [{"args":{"_genkey_0": "/core/**" }, "name": "Path" } ], "filters": [], "uri": "lb://dynroute-core", "order": 2, "delFlag": "0" } ]

新增路由配置后,则可访问

About

一款动态配置的路由网关

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages