SpringCloudAlibaba-Gateway网关

一、什么是网关

网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等。
Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul1.0。相比 Zuul 来说,Spring Cloud  Gateway 提供更优秀的性能,更强大的有功能。
Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。 它不能在传统的 servlet 容器中工作,也不能构 建成 war 包 。
Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

 其他的网关组件:

在SpringCloud微服务体系中,有个 很重要的组件就是网关 ,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul, 那就是 SpringCloud Gateway
网上很多地方都说Zuul是阻塞的,Gateway是非阻塞的,这么说是不严谨的,准确的讲Zuul1.x是阻塞的,而在2.x的版本中,Zuul也是基于Netty,也是非阻塞的,如果一定要说性能,其实这个真没多大差距。

二、Spring Cloud Gateway 功能特征 

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

路由(route) 


        路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。


断言(predicates) 


Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。


过滤器(Filter) 


SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。

 三、Gateway初体验

1、pom依赖

    <!--nacos服务注册发现-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--gateway的依赖 springcloud开发-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
<!--    sentinel整合gateway-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

2、application.yml 

server:
  port: 8016
  jetty:
    accesslog:
      append: false
spring:
  application:
    name: api-gateway

  cloud:
    #gateway的配置
    gateway:
      #路由规则
      routes:
        - id: order_route    #路由的唯一标识
#          uri: lb://order-service/add             #需要转发的路径 使用nacos中的本地负载均衡策略 order-service服务名
          uri: lb://order-seata             #需要转发的路径 使用nacos中的本地负载均衡策略 order-service服务名
          #断言规则 用于路由规则的匹配
          predicates:
#            - Path=/order_serv/**
            - Path=/order/**
#            application.yml.bak路由到-
#             http://localhost:8020/order-serv/order/add
#            - After=2020-12-31T23:59:59.789+08:00[Asia/Shanghai]
#            - Header=X-Request-Id,\d+
#            - Method=Get
#            - Query=name,tz|tianzhen
#            - CheckAuth=tz #自定义
          filters:
#            - AddRequestHeader=X-Request-color,red #添加请求头
            - StripPrefix=1 #转发之前去掉第一层路径
#            - PrefixPath=/mail-order #添加前缀 对应微服务需要配置context-path
#           - CheckAuth=tz  #自定义过滤器

          #http://localhost:8020/order/add
          #跨域配置
      globalcors:
        cors-configurations:
          '[/**]': #允许跨域访问得资源
#            allowedOrigins: "https://docs.spring.io" #跨域允许来源
            allowedOrigins: "*" #跨域允许来源
            allowedMethods:
              - Get

    #配置nacos
    nacos:
      server-addr: 127.0.0.1:8848
      discovery:
#        server-addr: 192.168.195.128:8847
        username: nacos
        password: nacos
        namespace: public

浏览器进行访问http://http://localhost:localhost:8016/order/order/addhttp://localhost:即可转发到你的http://localhost:8036/order/add

运行即可,发现我们服务可以正常访问。

四、断言工厂

4.1、基于Datetime类型


此类型断言根据时间做判断,主要由三个:

AfterRoutePredicateFactory:接受一个日期参数,判断请求日期是否晚于指定日期

BeforeRoutePredicateFactory:接受一个日期参数,判断请求日期是否早于指定日期

BetweenRoutePredicateFactory:接受一个日期参数,判断请求日期是否在指定时间段内


案例一(晚于指定日期)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

案例二(早于指定日期)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

案例三(指定日期之间)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

4.2、基于远程地址

RemoteAddrRoutePredicateFactory: 接收一个ip地址段,判断请求主机是否在地址端中

- RemoteAddr=192.168.1.1/24

 4.3、基于Cookie

CookieRoutePredicateFactory: 接收两个参数,cookie名字和一个正则表达式,判断请求cookie是否具有给定名称且与正则表达式匹配。

- Cookie=chocolate, ch.p

 4.4、基于Header

HeaderRoutePredicateFactory: 接收两个参数,标题名称和一个正则表达式,判断请求Header是否具有给定名称且与正则表达式匹配。

- Header=X-Request-Id, \d+

4.5、基于Method请求方法

MethodRoutePredicateFactory: 接收一个参数,判断请求类型是否跟指定的类型匹配

- Method=GET,POST

 4.6、基于Path匹配请求路径

{segment}:需要携带参数

- Path=/red/{segment},/blue/{segment}

五、自定义断言工厂

自定义断言工厂需要继承AbstractRoutePredicateFactory类,重写apply方法的逻辑,在apply方法中可以通过exchange.getRequest()拿到ServerHttpRequest对象,从而可以获取到请求的参数、请求方式、请求头等信息

要求:

必须spring组件bean
类必须加上RoutePredicateFactory作为结尾
继承AbstractRoutePredicateFactory
必须声明静态内部类,声明属性来接受 配置文件中的信息
需要结合shortcutFieldOrder来进行绑定
通过apply进行逻辑编写,true就是匹配成功,false就是匹配失败

package com.xinzhi.predicates;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;

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

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

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

    public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            public boolean test(ServerWebExchange exchange) {
                if(config.getName().equals("tz")){
                    return true;
                }
                return false;
            }
        };
    }
    //用于接收配置文件中 断言的信息
    @Validated
    public static class Config {
        private String name;

        public String getName() {
            return name;
        }

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

 yml文件加上

- CheckAuth=tz #自定义

6、内置过滤器工厂(局部)

由于内置过滤器工厂太多,我这里随便列几个,想看全部内置过滤器工厂,可去官网查看

6.1、添加请求头 

- AddRequestHeader=X-Request-red, red

 进行测试

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-red") String color){
    return color;
}

结果

成功返回red

6.2、添加请求参数

- AddRequestParameter=color, blue
@RequestMapping("/parameter") 
public String parameter(@RequestParam("color") String color){ return color; }

6.3、为匹配的路由统一添加前缀

- PrefixPath=/mall-order #前缀,对应微服务需要配置context-path 

order-service中需要配置 

server:
  port: 8010
  #应用名称(nacos会将该名称当作服务名称)
#  servlet:
#    context-path: /mail-order

6.4、重定向

- RedirectTo=302, https://www.baidu.com/ #重定向到百度

 7、自定义过滤器工厂(局部)

  • 继承AbstractGatewayFilterFactory
  • 自定义的名称要以GatewayFilterFactory结尾
  • 交给spring管理

例子:请求后面的参数name=tz才可以 

package com.xinzhi.filters;

import java.net.URI;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.StringUtils;
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;

/**
 * 自定义过滤工厂
 */
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {

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

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

    @Override
    public GatewayFilter apply(Config config) {
        GatewayFilter gatewayFilter1 = (ServerWebExchange exchange, GatewayFilterChain chain) -> {
            /**
             * 请求带上name参数
             */
            String name =exchange.getRequest().getQueryParams().getFirst("name");
            if (StringUtils.isNotBlank(name)){
                if (config.getValue().equals(name)){
                    chain.filter(exchange);
                }else {
                    //返回404
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
            //正常请求

            //1.获取name参数
            //2.如果不等于value就失败
            //3.否则正常访问

            return   chain.filter(exchange);
        };
        return gatewayFilter1;
    }

    public static class Config {
        private String value;

        public String getValue() {
            return value;
        }

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

}

 八、自定义全局过滤器

只需要实现GlobalFilter接口即可

@Component
public class LogGlobalFilter implements GlobalFilter {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        logger.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

九整合sentinel流控降级

网关作为内部系统外的一层屏障,对内起到一定的保护作用,限流便是其中之一,网关层的限流可以简单的针对不同路由进行限流,也可针对业务的接口进行限流,或者根据接口的特征分组限流。

1、添加依赖 

<!--sentinel整合gateway的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.在网关服务中添加yml

  sentinel:
      transport:
        dashboard: 127.0.0.1:8858

3、运行并进入控制台

测试简单流控规则(一秒钟访问两次将会被限流):

 在浏览器进行访问并快速刷新,触发流控规则

名词解读

  • Busrst size 宽容次数,比如设置为1,表示一秒钟访问3次以上被限流
  • 间隔 比如间隔设置为2,则表示2秒时间内访问次数大于2则被流控

 

Client IP 根据客户端ip地址进行流控

  • 精确 ------- 精确匹配你填的匹配串进行流控
  • 子串 --------包含匹配串
  • 正则 --------- 可以填入正则表达式

Remote Host 根据远程域名进行流控

Header:根据请求头名字匹配对应值进行流控

URL参数和Cookie方式与上述雷同

新建api分组(针对API接口流控)

 

 新增流控规则,选择刚刚新加的api分组

 进行测试发现,针对这两个接口发生流控,其他接口无流控

降级规则

 

自定义流控异常返回信息

配置类方式:

package com.xinzhi.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;

@Configuration
public class Gatewayconfig {

    @PostConstruct
    public void  init(){
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable t) {
                System.out.println(t);

                HashMap<String,String> map = new HashMap<>();
                map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
                map.put("message","限流了");
                //自定义异常处理
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值