05:服务网关GetWay

GetWay概念

Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
一句话概括:SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。

核心概念(组成)

  • 路由
    路由是网关中最基础的部分,路由信息包括ID、目的URL、一组断言工厂、一组Filter组成,如果断言为真,说明请求的URL与配置的路由匹配。
  • 断言
    断言函数允许开发者去自定义匹配Http request中的任何信息,比如请求头和参数等等。
  • 过滤器
    SpringCloud GetWay中filter分为GetWay Filter和Global Filter,filter可以对请求和响应进行处理。

Spring Cloud Gateway 具有如下特性:

  • 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
  • 动态路由:能够匹配任何请求属性;
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  • 集成Hystrix的断路器功能;
  • 集成 Spring Cloud 服务发现功能;
  • 易于编写的 Predicate(断言)和 Filter(过滤器);
  • 请求限流功能;
  • 支持路径重写。

服务网关GetWay实现原理-流程图

在这里插入图片描述

  • 动态路由+过滤链实现的,再yml中配置过滤,断言,可以过滤掉一些非法请求。Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
  • 1:请求进来通过webflux+reactor(响应式编程)
  • 2:然后调用DispatcherHandler.handle(请求接受转发处理器)
  • 3:这个方法中调用了mapping.getHandler方法,这个方法主要就是获取我们配置文件中配置的路由信息。包括断言,过滤的信息。将其转为Route对象,过滤一下然后返回。然后将这个route对象放入请求上下文的Getway_Route_Attribut里面。
  • 4:然后调用invokeHandler,这个方法就是拿到请求上下文中的Getway_Route_Attribut里面的Route对象。就可以得到我们Route对象中的过滤器,将这个过期加入全局过滤器List集合中,然后返回一个过滤器链条,这链条里面就是放的全局过滤器List集合中的过滤器。遍历这个链条执行filter过滤方法。然后调用一个具体的服务。
  • 5:在通过我们的路由和过滤后,filter过滤方法就会调用到loadBalancer的chooes方法。
  • 6:这个choose方法中的getserver方法getLoadBalancer通过轮询或者我们修改后的负载均衡的策略找到我们对应的一台应用实例,
  • 7:然后通过loadBalancer的reconstruactURI方法将getway的url替换为对应的后台机器的url。然后继续调用过滤链中的下一个filter过滤器。最后调用到NettyRoutingFilter。通过httpclient调用url到具体的机器。

在这里插入图片描述

代码示例git仓库-cloud-gateway-gateway9527模块

自定义路由断言工厂

  • 自定义路由断言工厂需要继承AbstractRoutePredicateFactory,重写apply方法,在apply方法中可以通过exchange.getRequest()拿到ServerHttpRequest对象,从而可以获取到请求的参数,请求方式,请求头等信息。
  • 1:此类必须是spring能扫描到的即bean
  • 2:类必须加上RoutePredicateFactory作为结尾。
  • 3:类必须继承AbstractRoutePredicateFactory,重写apply方法。
  • 4:必须声明静态内部类,声明属性来接受配置文件中对应的断言信息。
  • 5:需要结合shortcutFieldOrder进行绑定。
  • 6:通过apply方法进行判断,true就是匹配成功,false就是匹配失败。
    代码:
package com.zgs.springcloud.predicate;

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;

@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

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

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("");
    }

    @Override
    public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                if (config.getName().equalsIgnoreCase("test")) {
                    return true;
                } else {
                    return false;
                }
            }
        };
    }


    //用来接受配置文件中断言信息
    @Validated
    public static class Config {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

断言配置

spring:
	cloud:
		gateway:
			routes:
				 - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址 lb 使用负载均衡策略
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
            #- After=2021-08-10T05:04:07.722+08:00[Asia/Shanghai]
            #- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
            #- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai]
            #- Cookie=username,zgs
            #- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
            #- Host=**.zgs.com
            #- Method=GET
            - Query=username, \d+  # 要有参数名username并且值还要是整数才能路由

自定义过滤器工厂

  • 需要继承AbstractGatewayFilterFactory,名称必须由GatewayFilterFactory结束并且交给spring管理
package com.zgs.springcloud.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

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

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

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("value");
    }

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange,
                                     GatewayFilterChain chain) {
                String name = exchange.getRequest().getQueryParams().getFirst("name");

                if (Objects.equals(name, config.getValue())) {
                    return chain.filter(exchange);
                } else {
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {

        private String value;

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }

}

全局过滤器

package com.zgs.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.UUID;

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("################" + new Date() + ":执行了全局过滤器:[" + UUID.randomUUID() + "]###################");
        String zgs = exchange.getRequest().getQueryParams().getFirst("zgs");
        if (zgs == null) {
            log.info("****用户名为null,无法登录");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

过滤配置

spring:
	cloud:
		gateway:
			routes:
				- id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
	#         uri: http://localhost:8001          #匹配后提供服务的路由地址
	          uri: lb://cloud-payment-service #匹配后提供服务的路由地址 lb 使用负载均衡策略
	          filters:
	            - RewritePath=/payment_routh/(?<segment>.*), /$\{segment}
	#            - Addrequestheader=X-Request-color,red #添加请求头
	            - name: RequestRateLimiter
	              args:
	                # 使用SpEL从容器中获取对象pathKeyResolver,根据这个对象来进行限流
	                key-resolver: '#{@pathKeyResolver}'
	                # 令牌桶每秒填充平均速率
	                redis-rate-limiter.replenishRate: 1
	                # 令牌桶的总容量
	                redis-rate-limiter.burstCapacity: 3

局部过滤器与全局过滤器的区别
局部:局部针对某个路由,需要在路由中进行配置。
全局:针对所有路由请求,一旦定义就会生效。

GetWay跨域处理

1:配置文件

spring:
	cloud:
	    gateway:
	      globalcors:
	        cors-configurations:
	          '[/**]':  #允许跨域访问的资源
	            allowedOrigins: "*" #跨域允许来源
	            allowedMethods:
	              - GET
	              - POST
	              - PUT

2:配置类

package com.zgs.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("*/**", corsConfiguration);
        return new CorsWebFilter(source);
    }
}

GetWay整合Nacos

 #配合使用nacos 避免写死路径,使用nacos可以从nacos中根据服务名获取路径
spring:
	cloud:
		nacos:
	      discovery:
	        server-addr: localhost:8848
	        uaername: nacos
	        password: nacos

GetWay整合sentinel流控降级

1:添加依赖

  <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-alibaba-sentinel-gateway -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
            <version>2021.0.4.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
            <version>2021.1</version>
        </dependency>

2:添加配置

spring:
  application:
    name: cloud-gateway
    # get配置
  cloud:
    # 配置sentinel
    sentinel:
      transport:
        dashboard: localhost:8858

详细教程查看sentinel文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值