Gateway网关的学习

本文仅用于记录自己学习Spring Cloud Gateway,若介绍有误,还请手下留情,敬请批评指正,遵循共同学习进步的原则。           

 一、项目搭建gateway

建module

引入依赖pom

<dependencies>
        <!--gateway-->
        
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
        
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
        
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 

导入配置yml consul服务注册

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}

二、路由映射

1.不用feign 配置网关

业务模块测试接口

@RestController
public class PayGateWayController
{
    @Resource
    PayService payService;

    @GetMapping(value = "/pay/gateway/get/{id}")
    public ReturnData<Pay> getById(@PathVariable("id") Integer id)
    {
        Pay pay = payService.getById(id);
        return ReturnData.success(pay);
    }

    @GetMapping(value = "/pay/gateway/info")
    public ReturnData<String> getGatewayInfo()
    {
        return ReturnData.success("gateway info test:"+ IdUtil.simpleUUID());
    }
}

加接口 路由端口为8001

@RestController
public class PayGateWayController
{
    @Resource
    PayService payService;

    @GetMapping(value = "/pay/gateway/get/{id}")
    public ReturnData<Pay> getById(@PathVariable("id") Integer id)
    {
        Pay pay = payService.getById(id);
        return ReturnData.success(pay);
    }

    @GetMapping(value = "/pay/gateway/info")
    public ReturnData<String> getGatewayInfo()
    {
        return ReturnData.success("gateway info test:"+ IdUtil.simpleUUID());
    }
}

yml配置 

server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  
cloud:
    consul#配置consul地址
      
host: localhost
      port: 8500
      discovery:
        prefer-ip-addresstrue
        service-name
: ${spring.application.name}
    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          
uri: http://localhost:8001                #匹配后提供服务的路由地址
          
predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由


        id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          
uri: http://localhost:8001                #匹配后提供服务的路由地址
          
predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

2.用feign 配置网关 

业务模块测试接口

@RestController
public class PayGateWayController
{
    @Resource
    PayService payService;

    @GetMapping(value = "/pay/gateway/get/{id}")
    public ReturnData<Pay> getById(@PathVariable("id") Integer id)
    {
        Pay pay = payService.getById(id);
        return ReturnData.success(pay);
    }

    @GetMapping(value = "/pay/gateway/info")
    public ReturnData<String> getGatewayInfo()
    {
        return ReturnData.success("gateway info test:"+ IdUtil.simpleUUID());
    }
}

暴露接口模块部分需要加及接口

@FeignClient(value = "cloud-gateway") //对应的服务名字
    /**
     * GateWay进行网关测试案例01
     * @param id
     * @return
     */
    @GetMapping(value = "/pay/gateway/get/{id}")
    public ReturnData getById(@PathVariable("id") Integer id);

    /**
     * GateWay进行网关测试案例02
     * @return
     */
    @GetMapping(value = "/pay/gateway/info")
    public ReturnData<String> getGatewayInfo();

调用接口

@RestController
public class OrderGateWayController
{
    @Resource
    private PayFeignApi payFeignApi;

    @GetMapping(value = "/feign/pay/gateway/get/{id}")
    public ReturnData getById(@PathVariable("id") Integer id)
    {
        return payFeignApi.getById(id);
    }

    @GetMapping(value = "/feign/pay/gateway/info")
    public ReturnData<String> getGatewayInfo()
    {
        return payFeignApi.getGatewayInfo();
    }
}

三、其他高级特性

1.Route用微服务名字动态获取服务的URI

yml配置更改

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
    gateway:
      routes:
#        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
#          predicates:
#              - Path=/pay/**
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service               #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由

        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service               #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

服务主启动类需要加以达到动态刷新

@SpringBootApplication
@MapperScan("com.lllmark.cloud.mapper")
@EnableDiscoveryClient
@RefreshScope
public class Main8001 {
    public static void main(String[] args) {
        SpringApplication.run(Main8001.class,args);
        System.out.println("Main8001 start!");
    }
}

2.Predicates配置

(1) After 超过该时间放开接口

          between 一定时间段内可以访问

          before 在此时间之前可以访问

注:时间格式由ZonedDateTime类得出

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    gateway:
      routes:
#        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
#          predicates:
#              - Path=/pay/**
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001                #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
#            - After=2024-08-10T16:53:27.605069400+08:00[Asia/Shanghai]
#            - Between=2024-08-10T16:53:27.605069400+08:00[Asia/Shanghai],2024-08-10T16:56:17.605069400+08:00[Asia/Shanghai]
            - Before=2024-08-10T16:57:00.605069400+08:00[Asia/Shanghai]

(2)Cookie

routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
            - Cookie=username,pigbd

 - Header=X-Request-Id, \d+  请求头要有X-Request-Id属性并且值为整数的正则表达式

(3)Header  

   predicates:
            - Path=/pay/gateway/get/**              断言,路径相匹配的进行路由
            - Header=X-Request-Id, \d+  请求头要有X-Request-Id属性并且值为整数的正则表达式
 

(4)Host

predicates:
            - Path=/pay/gateway/get/**              断言,路径相匹配的进行路由
            - Host=**.baidu.com 

(5)Path

         predicates:
            - Path=/pay/gateway/info/**              断言,路径相匹配的进行路由

(6)query

  predicates:
            - Path=/pay/gateway/get/**              断言,路径相匹配的进行路由
            - Query=username, \d+  要有参数名username并且值还要是整数才能路由

(7)RemoteAddr

predicates:
            - Path=/pay/gateway/get/**              断言,路径相匹配的进行路由
            - RemoteAddr=192.168.124.1/24 # 外部访问我的IP限制,最大跨度不超过32,目前是1~24它们是 CIDR 表示法。

(8) Method

predicates:
  - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由
  - Method=POST,GET  #限制路径请求方式,只能用GET/POST

3.自定义Predicates 

规则类名需为 自定义名称+RoutePredicateFactory

后 extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>

后编写构造方法

public MyRoutePredicateFactory(){
    super(MyRoutePredicateFactory.Config.class);
}

编写配置

@Validated //验证
public static class Config{
    public Config() {
    }

    @Setter@Getter@NotEmpty
    private String userType; //用户类型 会员/非会员
}

重写shortcutFieldOrder

@Override
public List<String> shortcutFieldOrder() {
    return Collections.singletonList("userType"); //配置内部类的变量可以为多个
}

重写apply test方法返回值为控制是否可以访问该uri

@Override
public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
    return new Predicate<ServerWebExchange>() {
        @Override
        public boolean test(ServerWebExchange serverWebExchange) {
            String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
            //查询有无此参数,没有返回false
            if (userType == null) {
                return false;
            }
            //查询有无此参数,没有返回是否符合文件的配置
            if (userType.equalsIgnoreCase(config.getUserType())){
                return true;
            }
            return false;
        }
    };
}

四.Filter 过滤器

1.Request相关组

配置

        - id: pay_routh3 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
            #          uri: http://localhost:8001                #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - AddRequestHeader=X-Request-lllmark1,lllmarkValue1  #添加请求头值
            - AddRequestHeader=X-Request-lllmark2,lllmarkValue2
            - RemoveRequestHeader=sec-fetch-site  #删除请求头
            - SetRequestHeader=sec-fetch-mode,Update #修改请求头

 测试代码:

@GetMapping(value = "/pay/gateway/filter")
    public ReturnData<String> getGatewayFilter(HttpServletRequest request)
    {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements())
        {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-lllmark1")
                    || headName.equalsIgnoreCase("X-Request-lllmark2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }
        return ReturnData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }

2.RequestParameter

配置

          filters:
            - AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
            - RemoveRequestParameter=customerName   # 删除url请求参数customerName

测试代码:

@GetMapping(value = "/pay/gateway/filter")
    public ReturnData<String> getGatewayFilter(HttpServletRequest request)
    {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements())
        {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-lllmark1")
                    || headName.equalsIgnoreCase("X-Request-lllmark2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }

        System.out.println("=============================================");
        String customerId = request.getParameter("customerId");
        System.out.println("request Parameter customerId: "+customerId);

        String customerName = request.getParameter("customerName");
        System.out.println("request Parameter customerName: "+customerName);
        System.out.println("=============================================");

        return ReturnData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }

3.ResponseHeader

配置

          filters:
            - AddResponseHeader=X-Response-lllmark, add # 新增请求参数X-
            - SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
            - RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除
@GetMapping(value = "/pay/gateway/filter")
    public ReturnData<String> getGatewayFilter(HttpServletRequest request)
    {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements())
        {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-lllmark1")
                    || headName.equalsIgnoreCase("X-Request-lllmark2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }

        System.out.println("=============================================");
        String customerId = request.getParameter("customerId");
        System.out.println("request Parameter customerId: "+customerId);

        String customerName = request.getParameter("customerName");
        System.out.println("request Parameter customerName: "+customerName);
        System.out.println("=============================================");

        return ReturnData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }

4. 前缀和路径相关

配置

(1) 原访问路径 /pay/gateway/filter

        现访问路径 /gateway/filter

        - id: pay_routh3    #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
#            - Path=/pay/gateway/filter/**              # 原路径
            - Path=/gateway/filter/**              # 过滤后的路径
          filters:
            - PrefixPath=/pay #过滤的路径

(2) 原访问路径/pay/gateway/filter

        现访问路径 /XYZ/abc/filter  

        {}为占位符可改变,如{abc} ,但是需要相对应

        - id: pay_routh3 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/XYZ/abc/{segment}             # 断言,路径相匹配的进行路由
          filters:
            - SetPath=/pay/gateway/{segment}

 (3)请求转发,重定向某个界面

        此处访问 /pay/gateway/filter,跳转到www.baidu.com 

        - id: pay_routh3  #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - RedirectTo=302, http://www.baidu.com

5.其他

        Default Filter 此处添加到所有的接口,即全局配置

        

6.自定义过滤器

(1) 自定义全局Filter



@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {

    private static  final  String BEGIN_VISIT_TIME = "begin_visit_name";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.现记录访问接口的开始时间
        exchange.getAttributes().put(BEGIN_VISIT_TIME,System.currentTimeMillis());
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if (beginVisitTime != null){
                log.info("访问接口主机:"+exchange.getRequest().getURI().getHost());
                log.info("访问接口主机:"+exchange.getRequest().getURI().getPort());
                log.info("访问接口主机:"+exchange.getRequest().getURI().getPath());
                log.info("访问接口主机:"+exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口主机:" + (System.currentTimeMillis() - beginVisitTime) + "毫秒");
                log.info("==============");
                System.out.println();
            }
        }));
    }

    /**
     * 数字越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

配置:

        - id: pay_routh3 #pay_routh2  
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:

(2) 自定义条件Filter 


@Component
@Slf4j
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {

    public MyGatewayFilterFactory(){
        super(MyGatewayFilterFactory.Config.class);
    }

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                System.out.println("进入了网关过滤器" + config.getStatus());
                if (request.getQueryParams().containsKey("lllmark")){
                    return chain.filter(exchange);
                }else {
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        List<String> list = new ArrayList<String>();
        list.add("status");
        return list;
    }

    public static  class  Config{
        @Getter@Setter
        private String status;
    }
}

配置

        - id: pay_routh3  #路由的ID(类似mysql主键ID),没有固定规
          uri: lb://cloud-payment-service              #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
          filters:
            - My=lllmark

备注配置中My=lllmark 其中的My 由 MyGatewayFilterFactory分割My + GatewayFilterFactory

在自定义时候也需要按照此  自定义配置名+GatewayFilterFactory方式来命名

关于Spring Cloud Gateway的实战,有很多方面可以探索和实践。以下是一些常见的实战主题和示例: 1. 路由配置:使用Spring Cloud Gateway进行路由配置,将请求转发到不同的后端服务。可以通过YAML或Java代码方式进行配置,并可以使用各种条件和断言来实现动态路由。 2. 过滤器:利用Spring Cloud Gateway的过滤器功能,对请求进行预处理或后处理。常见的过滤器包括鉴权、请求转发修改、请求日志记录等。 3. 限流和熔断:使用Spring Cloud Gateway的限流和熔断功能,保护后端服务免受过载和故障的影响。可以使用内置的限流和熔断策略,或者集成第三方限流和熔断组件。 4. 请求重试:在网络不稳定的情况下,使用Spring Cloud Gateway的请求重试功能,自动重新发送请求,提高系统的可靠性和容错性。 5. 跨域支持:通过Spring Cloud Gateway配置跨域资源共享(CORS),允许跨域访问资源,提高前后端分离架构的灵活性。 6. 动态路由:结合服务注册中心(如Eureka或Consul)和配置中心(如Spring Cloud Config),实现动态路由的管理和配置。 7. 监控和日志:使用Spring Cloud Gateway的监控和日志功能,对请求进行统计和分析,了解系统的性能和健康状况。 以上只是一些常见的实战主题,实际上Spring Cloud Gateway还有更多功能和扩展性可供实践。你可以根据自己的需求和场景,选择适合的实战方向,深入学习和应用Spring Cloud Gateway
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值