目录
上一篇文章中介绍了通过配置文件配置网关路由信息,本篇介绍如何数据库动态配置网关路由及过滤器。
- CREATE TABLE `zf_gateway_route` (
- `route_id` varchar(16) NOT NULL DEFAULT '' COMMENT '路由id',
- `uri` varchar(64) DEFAULT NULL COMMENT '路由地址',
- `path_name` varchar(8) DEFAULT '' COMMENT 'path断言名称',
- `path_pattern` varchar(32) DEFAULT NULL COMMENT 'path断言匹配路径',
- `method_name` varchar(8) DEFAULT NULL COMMENT 'method断言名称',
- `method_pattern` varchar(8) DEFAULT NULL COMMENT 'method匹配方法',
- `msg_name` varchar(32) DEFAULT NULL COMMENT 'header断言名称',
- `msg_type` varchar(64) DEFAULT NULL COMMENT '消息类型,多个值之间用,分割',
- `filter_name` varchar(16) DEFAULT NULL COMMENT '过滤器名称',
- `new_path` varchar(64) DEFAULT NULL COMMENT '转发地址',
- `remark` varchar(32) DEFAULT NULL COMMENT '备注',
- `status` int(2) DEFAULT NULL COMMENT '状态:0、停用;1、启用',
- `index` int(2) DEFAULT NULL COMMENT '排序',
- PRIMARY KEY (`route_id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='网关路由信息表';
首先设计一张路由表,用来存储路由信息。
添加两条测试数据:
- INSERT INTO `zf_gateway_route` (`route_id`, `uri`, `path_name`, `path_pattern`, `method_name`, `method_pattern`, `msg_name`, `msg_type`, `filter_name`, `new_path`, `remark`, `status`, `index`) VALUES ('zhufeng-route-csdn', 'https://blog.csdn.net/', 'Path', '/csdn/**', 'Method', 'Get', NULL, NULL, 'MsgPath', '/nav/java', '第三方测试', 0, 1);
- INSERT INTO `zf_gateway_route` (`route_id`, `uri`, `path_name`, `path_pattern`, `method_name`, `method_pattern`, `msg_name`, `msg_type`, `filter_name`, `new_path`, `remark`, `status`, `index`) VALUES ('zhufeng-web-msg', 'lb://zhufeng-web-msg', 'Path', '/msg/info', 'Method', 'Get', NULL, NULL, NULL, NULL, '路由测试', 0, 2);
数据说明:
1、uri:路由地址,也就是通过配置的微服务或者http地址发送请求
2、path_name:断言规则,命名规则为: Name + RoutePredicateFactory,
比如:数据库配置为 Path ,gateway会自动找到 PathRoutePredicateFactory 。
3、path_pattern:请求的路径规则进行适配
4、method_name:断言规则 ,为方便演示,把method单独列了一项,
调用 MethodRoutePredicateFactory 来校验是否为Get或Post请求。
- package com.zhufeng.gateway.db.route;
-
- import com.alibaba.fastjson.JSONObject;
- import com.zhufeng.gateway.db.service.GatewayService;
- import org.springframework.cloud.gateway.filter.FilterDefinition;
- import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
- import org.springframework.cloud.gateway.route.RouteDefinition;
- import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
- import org.springframework.stereotype.Component;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
-
- import javax.annotation.PostConstruct;
- import javax.annotation.Resource;
- import java.net.URI;
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- * @ClassName: ZhufengRouteDefinitionRepository
- * @Description 动态配置路由规则
- * @author 月夜烛峰
- * @date 2022/9/15 19:38
- */
- @Component
- public class ZhufengRouteDefinitionRepository implements RouteDefinitionRepository {
-
- private List
routeDefinitions = new ArrayList(); -
- @Resource
- private GatewayService gatewayService;
-
- /**
- * 初始化参数
- */
- @PostConstruct
- public void init() {
- load();
- }
-
- /**
- * 通过数据库配置路由规则
- */
- private void load() {
- List
routeList = gatewayService.findRouteList(); - for(JSONObject routeJson:routeList) {
- //配置路由规则
- RouteDefinition route= new RouteDefinition();
- route.setId(routeJson.getString("routeId"));
- URI uri = URI.create(routeJson.getString("uri"));
- route.setUri(uri);
- //配置请求路径规则
- PredicateDefinition path = new PredicateDefinition();
- path.setName(routeJson.getString("pathName"));
- path.addArg("pattern", routeJson.getString("pathPattern"));
- route.getPredicates().add(path);
-
- //配置方法规则:Get、Post等
- PredicateDefinition method = new PredicateDefinition();
- method.setName(routeJson.getString("methodName"));
- String methodPattern = routeJson.getString("methodPattern");
- String[] methodArr = methodPattern.split(",");
- for(int i=0;i
- method.addArg("pattern"+0, methodArr[i]);
- }
- route.getPredicates().add(method);
-
-
- routeDefinitions.add(route);
-
- }
-
- }
-
- @Override
- public Flux
getRouteDefinitions() { - return Flux.fromIterable(routeDefinitions);
- }
-
- @Override
- public Mono
save(Mono route) { - // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public Mono
delete(Mono routeId) { - // TODO Auto-generated method stub
- return null;
- }
-
- }
Service比较简单:
- public interface GatewayService {
-
- public List
findRouteList(); - }
Service实现:
- @Service
- public class GatewayServiceImpl implements GatewayService {
-
- @Resource
- private GatewayMapper gatewayMapper;
-
- @Override
- public List
findRouteList() { - return gatewayMapper.findRouteList();
- }
- }
Mapper:
- @Mapper
- public interface GatewayMapper {
-
- public List
findRouteList(); - }
- "1.0" encoding="UTF-8"?>
-
- mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- <mapper namespace="com.zhufeng.gateway.db.mapper.GatewayMapper">
-
-
- <select id="findRouteList" resultType="com.alibaba.fastjson.JSONObject">
- SELECT
- route_id as routeId,
- uri,
- path_name as pathName,
- path_pattern as pathPattern,
- method_name as methodName,
- method_pattern as methodPattern,
- msg_name as msgName,
- msg_type as msgType,
- filter_name as filterName,
- new_path as newPath,
- remark
- FROM
- zf_gateway_route
- WHERE status = 0
- select>
- mapper>
application.properties配置文件:
- #服务端口
- server.port=9990
- #服务名称
- spring.application.name=zhufeng-gateway-db
-
- mybatis.type-aliases-package=com.zhufeng.gateway.db.mapper
- mybatis.mapper-locations=classpath:mapper/*.xml
3、效果演示
启动前需先启动nacos服务端,再创建两个微服务 zhufeng-web-user、zhufeng-web-msg,
《从0到1学SpringCloud——11 gateway网关路由配置详解》有具体过程。
启动后先测试第一个路由规则,访问 http://127.0.0.1:9990/csdn/test
根据路由规则,会被转发到 blog.csdn.net ,/csdn/test 会被替换为 /nav/java

虽然地址栏还是显示csdn/test,但是实际已经路由到了csdn的java博客专栏,通过标题可以看出Java博客,说明第一个配置生效。
然后测试第二个路由规则,请求被转发到 zhufeng-web-msg 微服务。
启动zhufeng-web-msg微服务,写一个简单的Controller
- /**
- * @ClassName: MsgController
- * @Description 消息路由测试
- * @author 月夜烛峰
- * @date 2022/7/22 20:33
- */
- @RequestMapping("msg")
- @RestController
- public class MsgController {
-
- @RequestMapping("info")
- public String showInfo() {
- return "good luck~";
- }
-
- }
为了方便演示Get请求和Post请求的不同,使用PostMan来测试,
分别请求 http://127.0.0.1:9990/msg/info
Get请求

Post请求

Get请求可以正常访问,Post请求被拦截,说明数据库配置的路由拦截条件生效。
三、自定义路由
1、代码实现
通过上述配置,简单了解了路由工厂的明明规范,我们可以遵循gateway定义自己的路由信息。

打开Gateway的源码,可以看到内部设置了很多断言规则,都有对应的路由工厂,途中标注的就是刚才用到的两个,仿照源码格式,我们写一个自己的路由工厂。
命名格式:Name+RoutePredicateFactory
Name:ZhufengVersion
工厂类:ZhufengVersionRoutePredicateFactory
校验逻辑:
当发起post请求时,如果Head中的版本号为 1.0 或者版本号不存在时放行,否则拦截。
ZhufengVersionRoutePredicateFactory代码:
- package com.zhufeng.gateway.db.route;
-
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
- import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
- import org.springframework.stereotype.Component;
- import org.springframework.validation.annotation.Validated;
- import org.springframework.web.server.ServerWebExchange;
-
- import java.util.Arrays;
- import java.util.List;
- import java.util.function.Predicate;
-
- /**
- * @ClassName: ZhufengVersionRoutePredicateFactory
- * @Description 动态路由版本判断
- * @author 月夜烛峰
- * @date 2022/9/15 19:49
- */
- @Slf4j
- @Component
- public class ZhufengVersionRoutePredicateFactory extends AbstractRoutePredicateFactory
{ -
- public static final String VERSION_KEY = "version";
-
- public ZhufengVersionRoutePredicateFactory() {
- super(Config.class);
- }
-
- @Override
- public List
shortcutFieldOrder() { - return Arrays.asList(VERSION_KEY);
- }
-
- @Override
- public Predicate
apply(Config config) { -
- return new GatewayPredicate() {
- @Override
- public boolean test(ServerWebExchange exchange) {
- String version = exchange.getRequest().getHeaders().getFirst("version");
- log.info("系统版本:{},当前请求版本:{}", config.version, version);
- if (config.version == null || config.version.length() == 0) {
- log.info("默认不拦截...");
- return true;
- }
-
- if (version == null || version.length() == 0) {
- log.info("header中版本为空,不拦截...");
- return true;
- }
- if (config.version.equals(version)) {
- log.info("校验通过,请求放行!当前请求版本:{},系统要求版本:{}", version, config.version);
- return true;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return String.format("version: %s", config.version);
- }
- };
- }
-
- @Validated
- public static class Config {
-
-
- private String version;
-
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
-
- }
-
- }
2、新增路由规则
修改ZhufengRouteDefinitionRepository代码,新增:

代码:
- //自定义版本断言(仅作为演示),request请求中,只有版本为1.0时放行
- PredicateDefinition version = new PredicateDefinition();
- version.setName("ZhufengVersion");
- version.addArg("version", "1.0");
- route.getPredicates().add(version);
3、效果演示
通过PostMan测试,在Headers中新增version字段
当version为1.0时

当version为2.0时

控制台打印: