1.spring Cloud Gateway入门

1. Spring Cloud Gateway入门

1.1 spring cloud gateway与Netflix Zuul的区别

(1)zuul的执行方式
在这里插入图片描述
Zuul会为每一个请求分配一条线程,然后通过执行不同类型的过滤器来完成路由的功能。但是这条线程会等route过滤器去调用后端服务,这样可能因为业务复杂导致服务响应缓慢,使得Zuul中的线程就需要执行比较长的时间,容易造成线程积压,导致性能变慢。
(2)spring cloud gateway的执行方式
在这里插入图片描述
spring cloud gateway执行方式可以分为下面三步:
①创建一条线程,通过类似Zuul的过滤器拦截请求
②对源服务器转发请求,但Gateway并不会等待请求调用源服务器的过程,而是将处理线程挂起,这样便不再占用资源了
③等源服务器返回消息后,再通过寻址的方式来响应之前客户端发送的请求
因此:Gateway线程活动的时间更短,线程积压的概率更低,性能也更好

1.2 入门案例

(1)创建一个微服务,让其对外提供一个接口,例如为:http://localhost:8080/testRoute/hello可以对外提供服务
(2)创建网关微服务,只需要引入spring cloud gateway依赖和配置yml文件即可

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
	<version>2.1.3.RELEASE</version>
</dependency>
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: testRoute        # 路由的唯一id
          uri: http://localhost:8080/    # 真正访问的URL
          predicates:
            - Path=/testRoute/**             # 将以/testRoute开始的服务,转发到http://localhost:8080/中
server:
  port: 8888

(3)访问http://localhost:8888/testRoute/hello即可实现相同的效果
注:本实例是采用直连的方式,企业级应用均是与注册中心结合起来使用。

1.3 spring cloud gateway基本组成

spring cloud gateway是通过netty服务器实现异步非阻塞功能的,原理如图:
在这里插入图片描述
由路由、断言和过滤器组成:
(1)路由(route)
路由是网关一个最基本的组件,它由ID、目标URI、断言集合和过滤器集合共同组成,当断言判定为true时,才会匹配到路由
(2)断言(predicate)
断言主要是判定当前请求如何匹配路由的。每个断言的入参都是Spring框架的ServerWebExchange对象类型。断言允许开发者匹配来自HTTP请求的任何内容,例如URL、请求头或请求参数,当这些断言都返回true时,才执行这个路由。
(3)过滤器(filter)
过滤器是使用特定工厂构造的SpringFrameworkGatewayFilter实例,其作用是在发送下游请求之前或者之后,修改请求和响应。过滤和断言均可以有多个。

1.4 断言类型

断言类必须实现RoutePredicateFactory接口,并且统一命名为:xxxRoutePredicateFactory
我们以java配置的方式为例结介绍各类断言(大家从官网看如何通过yml配置的方式配置断言)

1.4.1 Before路由断言

作用:路由在这个时间点之前有效,过了这个时间点无效

@Configuration
public class PredicateConfiguration {
 
	//1.Before路由断言
    @Bean
    public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
        ZonedDateTime dateTime = LocalDateTime.now()
                //一分钟后路由失效
                .plusMinutes(1)
                //定义UTC时间
                .atZone(ZoneId.systemDefault());
 
        return builder.routes()
                //匹配
                .route("1", r ->
                    //使用断言
                    r.before(dateTime)
                    //转发到具体的url
                    .uri("http://localhost:8080"))
                .build();
    }
}

1.4.2 After路由断言

作用:路由在该时间点之后有效

//After路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
    ZonedDateTime dateTime = LocalDateTime.now()
            //一分钟后路由生效
            .plusMinutes(1)
            .atZone(ZoneId.systemDefault());
 
    return builder.routes()
            //匹配
            .route("1", r ->
                //使用断言
                r.after(dateTime)
                .uri("http://localhost:8010"))
            .build();
}

1.4.3 Between路由断言

作用:在两个时间点之间有效

//Between路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
    ZonedDateTime dateTime1 = LocalDateTime.now()
            .atZone(ZoneId.systemDefault());
 
    ZonedDateTime dateTime2 = LocalDateTime.now()
            .plusMinutes(1)
            .atZone(ZoneId.systemDefault());
 
 
    return builder.routes()
            //匹配
            .route("1", r ->
                //使用断言
                r.between(dateTime1,dateTime2)
                .uri("http://localhost:8010"))
            .build();
    }

1.4.4 Cookie路由断言

作用:判定某个Cookie参数是否满足某个正则式,满足则匹配路由(不常用,因为用户可以关闭cookie)

@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
   return builder.routes()
            //匹配
            .route("1", r ->
                //使用断言
                r.cookie("cookies_id","abcd.*")
                .uri("http://localhost:8010"))
            .build();
}

1.4.5 Header路由断言

作用:判断Header中的参数是否满足某个正则式,满足则匹配路由

//Header路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
    return builder.routes()
            //匹配
            .route("1", r ->
                //使用断言
                r.header("id","^[0-9]*$")
                .uri("http://localhost:8010"))
            .build();
}

1.4.6 Host路由断言

作用:访问携带的host主机名称匹配才能访问路由

1.4.7 Method路由断言

作用:判断HTTP的请求类型是否匹配

//Method路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
 
    return builder.routes()
            //匹配
            .route("1", r ->
                //使用断言
                r.method(HttpMethod.GET)
                .uri("http://localhost:8010"))
            .build();
}

1.4.8 Path路由断言

作用:通过URI路径判断是否匹配路由

//Path路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
    String path1 = "/hello";
    String path2 = "/hello/index";
 
    return builder.routes()
            //匹配
            .route("1", r ->
                    //使用断言
                    r.path(path1,path2)
                    .uri("http://localhost:8010"))
            .build();
}

1.4.9 Query路由断言

作用:①判断是否存在某些请求参数,存在则匹配路由
②对请求参数值进行验证,符合正则式则匹配路由

//Query路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
    String regex = "^[0-9]*$";
 
    return builder.routes()
            //匹配
            .route("1", r ->
                    //使用断言
                    //r.query("id")  //存在id参数即可
                    r.query("id",regex)
                    .uri("http://localhost:8010"))
            .build();
}

1.4.10 RemoteAddr路由断言

作用:判断访问的远程服务器是否匹配,匹配则使用该路由

//RemoteAddr路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
    String addr1 = "10.33.68.18";
    String addr2 = "10.33.68.19";
    return builder.routes()
            //匹配
            .route("1", r ->
                    //使用断言
                    r.remoteAddr(addr1,addr2)
                    .uri("http://localhost:8010"))
            .build();
}

1.4.11 Weight路由断言

作用:当后端服务器为集群时,可以为不同的机器配置不同的权重,可以用来做灰度发布和负载均衡

//Weight路由断言
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder){
    String group_name = "group_one";
    String path = "/hello/**";
 
    return builder.routes()
            //第一个路由
            .route("1", r ->
                    r.path(path)
                           .and()
                           .weight(group_name,80)
                    .uri("http://localhost:8010"))
            //第二个路由
            .route("2", r ->
                    r.path(path)
                            .and()
                            .weight(group_name,20)
                            .uri("http://localhost:8011"))
            .build();
}

1.5 过滤器类型

三点解释:

  • 断言是为了路由的匹配,过滤器则是在请求源服务器之前或者之后对HTTP请求和响应的拦截,以便对请求和响应做出相应的修改,如请求头和响应头的修改。
  • Gateway过滤器分为全局过滤器和局部过滤器,全局过滤器针对所有的路由均有效,而局部过滤器只针对某些路由有效。全局过滤器需要实现GlobalFilter接口,而局部过滤器则需要实现GatewayFilter接口。
  • 在spring cloud Gateway中内置了不同的过滤器工厂(命名方式为:XXXGatewayFilterFactory)。

1.5.1 AddRequestHeader过滤器

作用:增加请求头的过滤器工厂,这样使得路由到资源服务器时就多了一个请求头参数

//AddRequestHeader过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/hello/**")
                     .filters(f -> f.addRequestHeader("id", "1"))
                     .uri("http://localhost:8010")
            ).build();
}

1.5.2 AddRequestParameter过滤器

作用:增加请求参数过滤器,这样使得路由到资源服务器时就多了一个请求参数

//AddRequestParameter过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/hello/**")
                     .filters(f -> f.addRequestParameter("id", "2"))
                     .uri("http://localhost:8010")
            ).build();
}

1.5.3 AddResponseHeader过滤器

作用:增加响应头参数,调完资源服务器时会添加响应头

//AddResponseHeader过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/hello/**")
                     .filters(f -> f.addResponseHeader("id", "3"))
                     .uri("http://localhost:8010")
            ).build();
}

1.5.4 Retry过滤器

作用:当某些请求异常时,自定义重试策略。有5个参数:
①retries:重试次数
②statuses:根据HTTP响应状态来断定是否重试
③methods:请求方法
④重试的状态码系列。取响应码的第一位,按HTTP响应状态码规范取值范围为1 ~ 5,其中,1代表消息,2代表成功,3代表重定向,4代表客户端错误,5代表服务端错误
⑤exceptions:请求异常列表,默认的情况下包含IOException和TimeoutException两种异常

//Retry过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/hello/**")
                     .filters(f -> f.retry(retryConfig -> {
                         //重试次数为2
                         retryConfig.setRetries(2);
                         //重试方法GET
                         retryConfig.setMethods(HttpMethod.GET);
                         //状态码500
                         retryConfig.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR);
                         //重试序列为服务器错误
                         retryConfig.setSeries(HttpStatus.Series.SERVER_ERROR);
                     }))
                     .uri("http://localhost:8010")
            ).build();
}

1.5.5 RequestRateLimiter过滤器

作用:限制请求流量,避免过大的流量进入系统,从而保证系统在一个安全的流量下可用。
使用方式:
(1)引入响应式redis包和连接池包(因为spring-boot-starter-data-redis-reactive不包含连接池)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.4.3</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
     <artifactId>commons-pool2</artifactId>
     <version>2.9.0</version>
</dependency>

(2)配置redis

redis:
    timeout: 10000
    host: 127.0.0.1
    port: 6379
    database: 1
    lettuce:
      pool:
        max-active: 1024
        max-wait: 10000
        max-idle: 200
        min-idle: 5

(3)定义RequestRateLimiter过滤器(使用配置文件方式较为简单)

spring:
  application:
    name: gateway-api
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: http://localhost:8010/   # 真正访问的URL
          predicates:
            - Path=/hello/**
          filters:
            - name: RequestRateLimiter
              args:
                # 每秒允许处理的请求数量  #  令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 每秒最大处理的请求数量# 令牌桶总容量
                redis-rate-limiter.burstCapacity: 2

这样就配置了对该Path的限流(令牌桶算法

1.5.6 StripPrefix过滤器(除前缀过滤器)

作用:在网关中,为了区分路由到的资源服务器,我们需要在路径前加入前缀,但是在访问资源服务器时需要去掉前缀,就需要用该StripPrefix过滤器

//StripPrefix过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/u/hello/**")
                            //去除一个前缀
                            .filters(f -> f.stripPrefix(1))
                            .uri("http://localhost:8010")
            ).build();
}

1.5.7 RewritePath过滤器(重写路径过滤器)

作用:和StripPrefix过滤器类似,但是功能更加强大,可以直接重写请求路径。

//RewritePath过滤器
@Bean
public RouteLocator customerRouters(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("1", r ->
                    r.path("/u/hello/**")
                            //去除一个前缀
                            .filters(f -> f.rewritePath("/u/(?<segment>.*)","/$\\{segment}"))
                            .uri("http://localhost:8010")
            ).build();
}

1.5.8 自定义过滤器

假设需求为:请求过来的url中必须携带参数company_name且值为beike才能访问
自定义过滤器MyTestGatewayFilterFactory:

@Slf4j
@Component
public class MyTestGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
 
    @Override
    public GatewayFilter apply(NameValueConfig config) {
 
        return ((exchange, chain) -> {
            System.out.println("进入自定义过滤器");
            String companyName = exchange.getRequest().getQueryParams().getFirst("company_name");
            //参数匹配上,放行
            if(config.getValue().equals(companyName)){
                return chain.filter(exchange);
            }else {
                //参数不匹配
                exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                return exchange.getResponse().setComplete();
            }
        });
    }
 
    public static class Config{
 
        private String companyName;
 
        public String getCompanyName() {
            return companyName;
        }
 
        public void setCompanyName(String companyName) {
            this.companyName = companyName;
        }
    }
}

正确的请求方式:
在这里插入图片描述
本文代码:
https://github.com/haiyang679/gatewayLearn

本文参考:
许进:重新定义Spring Cloud实战

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值