• 从0到1学SpringCloud——12 gateway 动态配置网关路由规则


    目录

    一、前言

    二、动态路由

    1、数据库设计

    2、路由代码

    3、效果演示

    三、自定义路由

    1、代码实现

    2、新增路由规则

    3、效果演示


    一、前言

    上一篇文章中介绍了通过配置文件配置网关路由信息,本篇介绍如何数据库动态配置网关路由及过滤器。

    二、动态路由

    1、数据库设计

    1. CREATE TABLE `zf_gateway_route` (
    2. `route_id` varchar(16) NOT NULL DEFAULT '' COMMENT '路由id',
    3. `uri` varchar(64) DEFAULT NULL COMMENT '路由地址',
    4. `path_name` varchar(8) DEFAULT '' COMMENT 'path断言名称',
    5. `path_pattern` varchar(32) DEFAULT NULL COMMENT 'path断言匹配路径',
    6. `method_name` varchar(8) DEFAULT NULL COMMENT 'method断言名称',
    7. `method_pattern` varchar(8) DEFAULT NULL COMMENT 'method匹配方法',
    8. `msg_name` varchar(32) DEFAULT NULL COMMENT 'header断言名称',
    9. `msg_type` varchar(64) DEFAULT NULL COMMENT '消息类型,多个值之间用,分割',
    10. `filter_name` varchar(16) DEFAULT NULL COMMENT '过滤器名称',
    11. `new_path` varchar(64) DEFAULT NULL COMMENT '转发地址',
    12. `remark` varchar(32) DEFAULT NULL COMMENT '备注',
    13. `status` int(2) DEFAULT NULL COMMENT '状态:0、停用;1、启用',
    14. `index` int(2) DEFAULT NULL COMMENT '排序',
    15. PRIMARY KEY (`route_id`)
    16. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='网关路由信息表';

    首先设计一张路由表,用来存储路由信息。

    添加两条测试数据:

    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-route-csdn', 'https://blog.csdn.net/', 'Path', '/csdn/**', 'Method', 'Get', NULL, NULL, 'MsgPath', '/nav/java', '第三方测试', 0, 1);
    2. 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请求。

    2、路由代码

    1. package com.zhufeng.gateway.db.route;
    2. import com.alibaba.fastjson.JSONObject;
    3. import com.zhufeng.gateway.db.service.GatewayService;
    4. import org.springframework.cloud.gateway.filter.FilterDefinition;
    5. import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
    6. import org.springframework.cloud.gateway.route.RouteDefinition;
    7. import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
    8. import org.springframework.stereotype.Component;
    9. import reactor.core.publisher.Flux;
    10. import reactor.core.publisher.Mono;
    11. import javax.annotation.PostConstruct;
    12. import javax.annotation.Resource;
    13. import java.net.URI;
    14. import java.util.ArrayList;
    15. import java.util.List;
    16. /**
    17. * @ClassName: ZhufengRouteDefinitionRepository
    18. * @Description 动态配置路由规则
    19. * @author 月夜烛峰
    20. * @date 2022/9/15 19:38
    21. */
    22. @Component
    23. public class ZhufengRouteDefinitionRepository implements RouteDefinitionRepository {
    24. private List routeDefinitions = new ArrayList();
    25. @Resource
    26. private GatewayService gatewayService;
    27. /**
    28. * 初始化参数
    29. */
    30. @PostConstruct
    31. public void init() {
    32. load();
    33. }
    34. /**
    35. * 通过数据库配置路由规则
    36. */
    37. private void load() {
    38. List routeList = gatewayService.findRouteList();
    39. for(JSONObject routeJson:routeList) {
    40. //配置路由规则
    41. RouteDefinition route= new RouteDefinition();
    42. route.setId(routeJson.getString("routeId"));
    43. URI uri = URI.create(routeJson.getString("uri"));
    44. route.setUri(uri);
    45. //配置请求路径规则
    46. PredicateDefinition path = new PredicateDefinition();
    47. path.setName(routeJson.getString("pathName"));
    48. path.addArg("pattern", routeJson.getString("pathPattern"));
    49. route.getPredicates().add(path);
    50. //配置方法规则:Get、Post等
    51. PredicateDefinition method = new PredicateDefinition();
    52. method.setName(routeJson.getString("methodName"));
    53. String methodPattern = routeJson.getString("methodPattern");
    54. String[] methodArr = methodPattern.split(",");
    55. for(int i=0;i
    56. method.addArg("pattern"+0, methodArr[i]);
    57. }
    58. route.getPredicates().add(method);
    59. routeDefinitions.add(route);
    60. }
    61. }
    62. @Override
    63. public Flux getRouteDefinitions() {
    64. return Flux.fromIterable(routeDefinitions);
    65. }
    66. @Override
    67. public Mono save(Mono route) {
    68. // TODO Auto-generated method stub
    69. return null;
    70. }
    71. @Override
    72. public Mono delete(Mono routeId) {
    73. // TODO Auto-generated method stub
    74. return null;
    75. }
    76. }

    Service比较简单:

    1. public interface GatewayService {
    2. public List findRouteList();
    3. }

    Service实现:

    1. @Service
    2. public class GatewayServiceImpl implements GatewayService {
    3. @Resource
    4. private GatewayMapper gatewayMapper;
    5. @Override
    6. public List findRouteList() {
    7. return gatewayMapper.findRouteList();
    8. }
    9. }

    Mapper:

    1. @Mapper
    2. public interface GatewayMapper {
    3. public List findRouteList();
    4. }
    1. "1.0" encoding="UTF-8"?>
    2. mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    3. <mapper namespace="com.zhufeng.gateway.db.mapper.GatewayMapper">
    4. <select id="findRouteList" resultType="com.alibaba.fastjson.JSONObject">
    5. SELECT
    6. route_id as routeId,
    7. uri,
    8. path_name as pathName,
    9. path_pattern as pathPattern,
    10. method_name as methodName,
    11. method_pattern as methodPattern,
    12. msg_name as msgName,
    13. msg_type as msgType,
    14. filter_name as filterName,
    15. new_path as newPath,
    16. remark
    17. FROM
    18. zf_gateway_route
    19. WHERE status = 0
    20. select>
    21. mapper>

    application.properties配置文件:

    1. #服务端口
    2. server.port=9990
    3. #服务名称
    4. spring.application.name=zhufeng-gateway-db
    5. mybatis.type-aliases-package=com.zhufeng.gateway.db.mapper
    6. 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

    1. /**
    2. * @ClassName: MsgController
    3. * @Description 消息路由测试
    4. * @author 月夜烛峰
    5. * @date 2022/7/22 20:33
    6. */
    7. @RequestMapping("msg")
    8. @RestController
    9. public class MsgController {
    10. @RequestMapping("info")
    11. public String showInfo() {
    12. return "good luck~";
    13. }
    14. }

    为了方便演示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代码:

    1. package com.zhufeng.gateway.db.route;
    2. import lombok.extern.slf4j.Slf4j;
    3. import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
    4. import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
    5. import org.springframework.stereotype.Component;
    6. import org.springframework.validation.annotation.Validated;
    7. import org.springframework.web.server.ServerWebExchange;
    8. import java.util.Arrays;
    9. import java.util.List;
    10. import java.util.function.Predicate;
    11. /**
    12. * @ClassName: ZhufengVersionRoutePredicateFactory
    13. * @Description 动态路由版本判断
    14. * @author 月夜烛峰
    15. * @date 2022/9/15 19:49
    16. */
    17. @Slf4j
    18. @Component
    19. public class ZhufengVersionRoutePredicateFactory extends AbstractRoutePredicateFactory {
    20. public static final String VERSION_KEY = "version";
    21. public ZhufengVersionRoutePredicateFactory() {
    22. super(Config.class);
    23. }
    24. @Override
    25. public List shortcutFieldOrder() {
    26. return Arrays.asList(VERSION_KEY);
    27. }
    28. @Override
    29. public Predicate apply(Config config) {
    30. return new GatewayPredicate() {
    31. @Override
    32. public boolean test(ServerWebExchange exchange) {
    33. String version = exchange.getRequest().getHeaders().getFirst("version");
    34. log.info("系统版本:{},当前请求版本:{}", config.version, version);
    35. if (config.version == null || config.version.length() == 0) {
    36. log.info("默认不拦截...");
    37. return true;
    38. }
    39. if (version == null || version.length() == 0) {
    40. log.info("header中版本为空,不拦截...");
    41. return true;
    42. }
    43. if (config.version.equals(version)) {
    44. log.info("校验通过,请求放行!当前请求版本:{},系统要求版本:{}", version, config.version);
    45. return true;
    46. }
    47. return false;
    48. }
    49. @Override
    50. public String toString() {
    51. return String.format("version: %s", config.version);
    52. }
    53. };
    54. }
    55. @Validated
    56. public static class Config {
    57. private String version;
    58. public String getVersion() {
    59. return version;
    60. }
    61. public void setVersion(String version) {
    62. this.version = version;
    63. }
    64. }
    65. }

    2、新增路由规则

    修改ZhufengRouteDefinitionRepository代码,新增:

    代码:

    1. //自定义版本断言(仅作为演示),request请求中,只有版本为1.0时放行
    2. PredicateDefinition version = new PredicateDefinition();
    3. version.setName("ZhufengVersion");
    4. version.addArg("version", "1.0");
    5. route.getPredicates().add(version);

    3、效果演示

    通过PostMan测试,在Headers中新增version字段 

    当version为1.0时

    当version为2.0时

    控制台打印:

     

  • 相关阅读:
    【计算机网络】UDP/TCP 协议
    代理IP与Socks5代理:跨界电商智能爬虫的引擎与安全壁垒
    【Redis】hash类型-内部编码&使用场景
    微信小程序带来了新的创业机会?
    【云原生】Kubernetes PDB(Pod Disruption Budget)介绍与简单使用
    JSP在Scriptlet中编写java代码的形式
    企业微信、飞书、钉钉机器人消息发送工具类
    Airtest-Selenium实操小课③:下载可爱猫猫图片
    王道操作系统___第三章01
    源码剖析:Elasticsearch 段合并调度及优化手段
  • 原文地址:https://blog.csdn.net/yueyezhufeng/article/details/126883661