玩转Spring Cloud Alibaba之Gateway

目录

前言

一、为什么需要网关?

        来看一下微服务的架构图

二、快速入门

2.1核心概念

2.2工作流程

二、快速开始

2.1.搭建Gateway9527项目,并注册到eureka

2.2启动测试

2.2.1通过bean注入的方式来配置

三、断言(Predicate)的使用

3.1常见的路由断言工厂 

四、过滤器(filter)的使用

4.1过滤器的生命周期

4.2spring cloud gateway内置的过滤器工厂

4.3自定义过滤器 

方式一

方式二


前言

Gateway是建立在Spring强大的生态系统之上的网关服务,基于Spring 5、Spring Boot 2.0等技术,提供了一种简单有效的方式来对Api进行路由管理,同时还提供了一些强大的过滤器功能,如熔断,限流,重试等。Gateway基于WebFlux框架进行实现,底层则采用高性能的RPC框架Netty进行通信,相比于基于Servlet阻塞模型的zuul,它的性能更强大。

一、为什么需要网关?

我们都知道,微服务架构中往往由很多个微服务组成,面对那么多的微服务,客户端往往不知道该怎么去调用这些微服务,如果没有网关的存在,则客户端需要记录每个微服务的ip地址及端口,然后分别去调用。显然,这种方式在面对庞大的微服务集群显然是不适用的,所以就需要一个“司机”,只需告诉他我们想去的地方,便能带我们到预想的目的地,网关在微服务架构中便是充当这样的角色。

来看一下微服务的架构图

 可以看到,用户在使用我们系统的时候,可能会同时访问到多个微服务,如果不使用网关,这个模式会产生许多问题:

  • 认证复杂,接口都是登陆了才能访问,用户在打开购物车的时候已经登陆过了,点开订单页面又提示需要登陆,这将十分影响用户体验。
  • 重构难度大,随着项目迭代,可能需要重新划分该微服务
  • 其他微服务,如果采用了对浏览器不友好的协议,如RPC协议,客户端就很难请求
  • 安全问题,暴露微服务的ip/域名

而网关正是为了解决这些痛点而生

怎么解决

  • 在网关上统一进行登陆认证,然后验证后的用户信息转发至各个微服务
  • 对外暴漏的永远是一个网关的「域名」,客户端重构成本大大降低
  • 如果其他微服务采用的是对浏览器不友好的协议,可以在网关上转换协议(http、websocket) 

二、快速入门

2.1核心概念

为了更好的使用网关,首先来了解一下网关的三大核心概念

  • route(路由)——可以简单理解成一条转发规则。包括:id,目标url,predicate集合以及filter集合
  • predicate(断言),函数式编程的api,实现路由的匹配条件(控制这个请求能否要走这个路由的条件)
  • filter(过滤器),主要用于修改请求以及响应信息

2.2工作流程

        当客户端发起请求到gateway,会通过一些匹配条件,定位到真正要访问的微服务节点。并且在转发请求的过程前后,进行一些细粒度的控制(类似Spring的AOP思想)。

        其中断言就是我们的匹配条件,只有符合断言条件,才能进行路由的转发,而filter的作用就更加强大,可以让我们在请求前可以做一些参数校验,权限校验,流量监控,日志出书,协议转换等等,在请求后的过滤器中还可以做响应头,响应内容的修改。

        不仅如此,Gateway提供了一套十分优雅的方式来给我们扩展,可以说gateway是网关技术选型的不二之选。


二、快速开始

项目源码地址

2.1.搭建Gateway9527项目,并注册到eureka

在eureka界面看到我们搭建的网关微服务,说明项目环境已搭建好

搭建另外一个微服务,cloud-provider-payment8001

怎么做到通过gateway将请求转发到cloud-provider-payment8001呢?

这里采用配置yml的方式来进行配置(后面会介绍通过代码「bean」的方式来进行配置。由于是配置项,官方也推荐使用yml)

打开Gateway9527的配置文件,增加以下选项

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  #############################新增网关配置###########################
  cloud:
    gateway:
      routes:
        - id: pay_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/get/**         # 断言,路径相匹配的进行路由

2.2启动测试

增加网关前,访问  http://localhost:8001/pay/get/1

 增加网关后,访问  http://localhost:9527/pay/get/1 也能成功,说明我们的配置已经生效

此外,如果我们的某个微服务是集群部署的,gateway还支持负载均衡的方式来调用,只需要在配置项修改uri为lb开头即可

spring:
  application:
    name: cloud-gateway
  #############################新增网关配置###########################
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: pay_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service #负载均衡后提供服务的路由地址
          predicates:
            - Path=/pay/get/**         # 断言,路径相匹配的进行路由

2.2.1通过bean注入的方式来配置

@Configuration
public class GateWayConfig {
    /**
     * 配置一个id为path_route_oldou的路由规则
     * 当访问【localhost:9527/guonei】时,网关会自动将请求转发到【http://news.baidu.com/guonei】
     * @param routeLocatorBuilder /
     * @return /
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //这里的配置,实现的效果为:当访问【localhost:9527/guonei】时,网关会将请求转发到【http://news.baidu.com/guonei】
        routes.route("path_route_oldou",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();

        return routes.build();
    }

}

三、断言(Predicate)的使用

断言可以针对http请求中的各种属性,来进行匹配校验,只有匹配断言条件,gateway才会将请求转发到相关的微服务。

3.1常见的路由断言工厂

  • 时间相关
    • AfterRoutePredicateFactory
    • BeforeRoutePredicateFactory
    • BetweenRoutePredicateFactory
  • Cookie相关
    • CookieRoutePredicateFactory
  • Header相关
    • HeaderRoutePredicateFactory
    • HostRoutePredicateFactory
  • 请求相关
    • MethodRoutePredicateFactory
    • PathRequestPredicateFactory
    • QueryRoutePredicateFactory
    • RemoteAddrRoutePredicateFactory

After

示例

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: lb://user-center
          predicates:
            # 当且仅当请求时的时间After配置的时间时,才会转发到用户微服务
            # 目前配置不会进该路由配置,所以返回404
            # 将时间改成 < now的时间,则访问localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - After=2030-01-20T17:42:47.789-07:00[America/Denver]

TIPS

  • 技巧:时间可使用 System.out.println(ZonedDateTime.now()); 打印,然后即可看到时区。例如:2019-08-10T16:50:42.579+08:00[Asia/Shanghai]
  • 时间格式的相关逻辑:
    • 默认时间格式:org.springframework.format.support.DefaultFormattingConversionService#addDefaultFormatters
    • 时间格式注册:org.springframework.format.datetime.standard.DateTimeFormatterRegistrar#registerFormatters

Before

示例

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: lb://user-center
          predicates:
            # 当且仅当请求时的时间Before配置的时间时,才会转发到用户微服务
            # 目前配置不会进该路由配置,所以返回404
            # 将时间改成 > now的时间,则访问localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Before=2018-01-20T17:42:47.789-07:00[America/Denver]

Between

示例

spring:
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: lb://user-center
          predicates:
            # 当且仅当请求时的时间Between配置的时间时,才会转发到用户微服务
            # 因此,访问localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2027-01-21T17:42:47.789-07:00[America/Denver]

 Cookie

示例

spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: lb://user-center
          predicates:
            # 当且仅当带有名为somecookie,并且值符合正则ch.p的Cookie时,才会转发到用户微服务
            # 如Cookie满足条件,则访问http://localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Cookie=somecookie, ch.p

 Header

示例

spring:
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: lb://user-center
          predicates:
            # 当且仅当带有名为X-Request-Id,并且值符合正则\d+的Header时,才会转发到用户微服务
            # 如Header满足条件,则访问http://localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Header=X-Request-Id, \d+

Host

示例

spring:
  cloud:
    gateway:
      routes:
        - id: host_route
          uri: lb://user-center
          predicates:
            # 当且仅当名为Host的Header符合**.somehost.org或**.anotherhost.org时,才会转发用户微服务
            # 如Host满足条件,则访问http://localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Host=**.somehost.org,**.anotherhost.org

 Method

示例

spring:
  cloud:
    gateway:
      routes:
        - id: method_route
          uri: lb://user-center
          predicates:
            # 当且仅当HTTP请求方法是GET时,才会转发用户微服务
            # 如请求方法满足条件,访问http://localhost:8040/** -> user-center/**
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Method=GET

  Path

示例

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://user-center
          predicates:
            # 当且仅当访问路径是/users/*或者/some-path/**,才会转发用户微服务
            # segment是一个特殊的占位符,单层路径匹配
            # eg. 访问http://localhost:8040/users/1 -> user-center/users/1
            - Path=/users/{segment},/some-path/**

 Query

示例

spring:
  cloud:
    gateway:
      routes:
        - id: query_route
          uri: lb://user-center
          predicates:
            # 当且仅当请求带有baz的参数,才会转发到用户微服务
            # eg. 访问http://localhost:8040/users/1?baz=xx -> user-center的/users/1
            - Query=baz

  RemoteAddr

示例

spring:
  cloud:
    gateway:
      routes:
        - id: remoteaddr_route
          uri: lb://user-center
          predicates:
            # 当且仅当请求IP是192.168.1.1/24网段,例如192.168.1.10,才会转发到用户微服务
            # eg. 访问http://localhost:8040/users/1 -> user-center的/users/1
            - RemoteAddr=192.168.1.1/24

四、过滤器(filter)的使用

过滤器指的是Spring框架中,Gateway filter的实例,通过使用过滤器,可以实现在路由请求前(pre)和请求后(post)对请求进行修改。一般广泛应用在全局日志记录、参数校验、统一的网关鉴权等等。

4.1过滤器的生命周期

  • pre:gateway转发请求之前
  • post:gateway转发请求之后

4.2spring cloud gateway内置的过滤器工厂

包括:

  • AddRequestHeader GatewayFilter Factory
  • AddRequestParameter GatewayFilter Factory
  • AddResponseHeader GatewayFilter Factory
  • DedupeResponseHeader GatewayFilter Factory
  • Hystrix GatewayFilter Factory
  • FallbackHeaders GatewayFilter Factory
  • PrefixPath GatewayFilter Factory
  • PreserveHostHeader GatewayFilter Factory
  • RequestRateLimiter GatewayFilter Factory
  • RedirectTo GatewayFilter Factory
  • RemoveHopByHopHeadersFilter GatewayFilter Factory
  • RemoveRequestHeader GatewayFilter Factory
  • RemoveResponseHeader GatewayFilter Factory
  • RewritePath GatewayFilter Factory
  • RewriteResponseHeader GatewayFilter Factory
  • SaveSession GatewayFilter Factory
  • SecureHeaders GatewayFilter Factory
  • SetPath GatewayFilter Factory
  • SetResponseHeader GatewayFilter Factory
  • SetStatus GatewayFilter Factory
  • StripPrefix GatewayFilter Factory
  • Retry GatewayFilter Factory
  • RequestSize GatewayFilter Factory
  • Modify Request Body GatewayFilter Factory
  • Modify Response Body GatewayFilter Factory
  • Default Filters

得益于Spring家族强大的生态,Gateway已经整合到Spring Boot Config,用法与Predicatel类似,可以做到配置即生效,具体Demo参考官方文档GatewayFilter Factories篇

Spring Cloud Gateway

4.3自定义过滤器 

方式一

继承AbstractNameValueGatewayFilterFactory,重写apply方法

核心Api如下

  • exchange.getRequest().mutate().xxx        修改request
  • exchange.mutate().xxx        修改exchange
  • chain.filter(exchange)      传递给下一个过滤器处理
  • exchange.getResponse()       拿到响应
@Slf4j
@Component
public class PreLogGatewayFilterFactory
    extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return ((exchange, chain) -> {
            log.info("请求进来了...{},{}", config.getName(), config.getValue());
            ServerHttpRequest modifiedRequest = exchange.getRequest()
                .mutate()
                .build();
            ServerWebExchange modifiedExchange = exchange.mutate()
                .request(modifiedRequest)
                .build();

            return chain.filter(modifiedExchange);
        });
    }
}

方式二

实现GlobalFilter,Orderd接口

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;

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

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname==null){
            log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
            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、付费专栏及课程。

余额充值