Java后端技术-Spring Cloud Gateway学习笔记

Spring Cloud Gateway简介

Spring Cloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。Spring Cloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。Spring Cloud GateWay 使用的是WebFlux中的reactor-netty响应式编程组件,底层使用了Neety通讯框架。

image-20210430211913188

三大核心概念

路由Route

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

断言Predicate

开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由

过滤Filter

指的是Spring框架中GateWayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改

工作流程

image-20210501085919074

客户端向Spring Cloud GateWay发送请求。然后在GateWay Handler Mapping 中找到与请求相匹配的路由,将其发送到GateWay Web Handler。GateWay Web Handler再通过指定的过滤器链来将请求发送到实际的服务执行业务逻辑然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或之后(post)执行业务逻辑

Filter在pre类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等

Filter在post类型的过滤器可以做响应内容、修改响应头、日志的输出、流量监控等

Spring Cloud Gateway网关搭建

引入依赖

注意不要导入web和actuator的依赖,否则会报错Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at thistime. Please remqve spring-boot-starter-web dependency.

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--gateway无需web和actuator-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

配置文件

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
      routes:
        - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
          #匹配后提供服务的路由地址
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
            #- After=2017-01-20T17:42:47.789-07:00[America/Denver]
            #- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
            #- Cookie=username,zzyy
            #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
            #- Host=**.atguigu.com
            #- Method=GET
            #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
          # 过滤
          #filters:
          #  - AddRequestHeader=X-Request-red, blue
        - id: payment_route2
          uri: http://localhost:8001
          predicates:
            Path=/payment/lb/** #断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

主启动类

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {

Spring Cloud Gateway配置路由的两种方式

配置文件方式

spring.cloud.gateway.routes[0].id=service-edu
spring.cloud.gateway.routes[0].uri=lb://service-edu
spring.cloud.gateway.routes[0].predicates=Path=/eduservice/**

spring.cloud.gateway.routes[1].id=service-oss
spring.cloud.gateway.routes[1].uri=lb://service-oss
spring.cloud.gateway.routes[1].predicates=Path=/eduoss/**
routes:
  - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
    #匹配后提供服务的路由地址
    uri: http://localhost:8001
    predicates:
      - Path=/payment/get/** # 断言,路径相匹配的进行路由

JavaConfig方式

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		// 访问localhost:9527/guonei会转发到http://news.baidu.com/guonei
        RouteLocator path_route = builder.routes()
                .route("path_route", r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        return path_route;
    }
}

Spring Cloud Gateway通过微服务名实现动态路由

默认情况下GateWay会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由转发,从而实现动态路由动能

GateWay的uri的协议为lb,表示启用GateWay的负载均衡功能

第一步:开启从注册中心动态创建路由的功能,利用微服务名称进行路由

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由

第二步:lb创建负载均衡uri

routes:
  - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
    #匹配后提供服务的路由地址
    uri: lb://CLOUD-PAYMENT-SERVICE
    predicates:
      - Path=/payment/get/** # 断言,路径相匹配的进行路由

Spring Cloud Gateway解决跨域问题

跨域:浏览器对于javascript的同源策略的限制 。

以下情况都属于跨域:

跨域原因说明示例
域名不同www.jd.com 与 www.taobao.com
域名相同,端口不同www.jd.com:8080 与 www.jd.com:8081
二级域名不同item.jd.com 与 miaosha.jd.com

如果域名和端口都相同,但是请求路径不同,不属于跨域,如:
www.jd.com/item
www.jd.com/goods
http和https也属于跨域

为什么有跨域问题?

跨域不一定都会有跨域问题。

因为跨域问题是浏览器对于ajax请求的一种安全限制:一个页面发起的ajax请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。

因此:跨域问题 是针对ajax的一种限制。

但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,怎么办?

全局配置类实现跨域问题

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

Spring Cloud Gateway常用Predicate

image-20210501094544567

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway包含许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合。Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋给Route。Spring Cloud Gateway包含许多内置的Route Pred Factories。所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and

image-20210501095918435

总的来说,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理

After Route Predicate:Before和Between使用一样

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        # 在这个时间段之后才能访问
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

Cookie Route Predicate

Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。路由规则会通过获取对应的Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        # 请求中的cookie要有username属性并且为xiaoyixiao0628
        - Cookie=username, xiaoyixiao0628

Header Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        # 请求头要有X-Request-Id属性并且值为整数的正则表达式
        - Header=X-Request-Id, \d+

Host Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        # 请求头要有Host属性并且满足**.somehost.org,**.anotherhost.org
        - Host=**.somehost.org,**.anotherhost.org

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个ant分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。

Method Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        # 请求方法必须是GET或者POST
        - Method=GET,POST

Path Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        # 访问地址为:ip地址:端口号/red/{segment}才能访问
        - Path=/red/{segment}

Query Route Predicate

支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

RemoteAddr Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        # 如果请求的远端地址是192.168.1.10,则此路由匹配。
        - RemoteAddr=192.168.1.1/24

Weight Route Predicate

这条路线将把80%的流量转发到weighthigh.org, 20%的流量转发到weighlow.org

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

Spring Cloud Gateway过滤器FIlter

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway内置了多种路由过滤器,都由GateWayFilter的工厂类来产生。

Filter的声明周期:Pre请求之前和POST请求之后

常用过滤器https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

全局过滤器https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters

自定义全局过滤器

实现发送请求时候没带uname参数网关拦截非法用户

第一步:实现两个接口org.springframework.cloud.gateway.filter.GlobalFilter, org.springframework.core.Ordered

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("***** 进入全局过滤器 *****" + new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if (uname == null) {
            log.info("***** 用户名为null,非法用户 *****");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值