栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringCloud Gateway服务网关从概念到应用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringCloud Gateway服务网关从概念到应用

参考尚硅谷

文章目录

1.什么是Gateway?为什么用Gateway?

1.1什么是Gateway?以及他的特性1.2为什么要用Gateway? 2.Gateway相关概念

2.1 Route(路由)2.2 Predicate(断言)2.3 Filter(过滤) 3.工作流程4.Gateway路由配置

4.1通过yml文件进行配置4.2硬编码方式配置路由4.3动态路由 5.Predicate的使用

5.1 After5.2 cookie5.3 Header 6.Filter

1.什么是Gateway?为什么用Gateway? 1.1什么是Gateway?以及他的特性

Gateway是在Spring生态系统之上构建的API网关服务,是SpringCloud的亲儿子。基于Spring5,SpringBoot2和Project Reactor等技术。

动态路由:能够匹配任何请求属性;

可以对路由指定Predicate(断言)和Filter(过滤器)

集成Hystrix的断路器功能;

集成SpringCloud服务发现的功能;

易于编写的Predicate和Filter;

请求限流功能;

支持路径重写。

1.2为什么要用Gateway?

Gateway是一个服务网关,之前服务网关都用Netflix的zuul,然后他说要更新,然后就一直鸽,据说内部大佬也被挖了,就相当于"创业未半而中道崩殂",值此"危急存亡之秋",SpringCloud自己弄了一个服务网关就是Gateway。Gateway针对之前的zuul进行了一些改造。

1.Zuul 1.x,是一个基于阻塞I/O的API。

2.Zuul 1.x基于Servlet2.5使用阻塞架构他不支持人和长链接(如WebSocket)Zuul的设计模式和Nginx较像,每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx用C++实现,Zuul用Java实现,而JVM本身会有第一次使用加载较慢的情况,使得Zuul的性能相对较差。

3.Zuul 2.x理念更先进,像基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合,Zuul 2.x的性能较Zuul 1.x有较大提升。在性能方面,根据官方提供的基准测试,SpringCloud Gateway的RPS是Zuul的1.6倍。

4.SpringCloud Gateway建立在 Spring framework5、Project Reactor和SpringBoot2之上,使用非阻塞API。

5.SpringCloud Gateway还支持WebSocket,并且与Spring紧密集成的更好的开发体验。

服务网关位于Nginx负载均衡和微服务之间属于微服务的入口,如图所示

2.Gateway相关概念 2.1 Route(路由)

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

2.2 Predicate(断言)

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

2.3 Filter(过滤)

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

3.工作流程

官网截图

客户端首先向SpringCloud Gateway发出请求。然后在Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到Gateway Web Handler。

Handler再通过指定的过滤器可以做参数、权限校验、流量监控、日志输出、协议转换等。

Filter在"pre"类型得过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。在"post"类型的过滤器中可以做响应内容、响应头的修改、日志的输出,流量监控等有着非常重要的作用。

4.Gateway路由配置

比如我有一个端口号为8001的服务提供者这时候我每次访问都得访问8001但是我不想将8001端口暴露出去,所以我可以在8001之前加一个9527的网关让请求先经过网关。

然后我就可以新建一个9527的微服务。

首先在pom中添加gateway的依赖。

        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
4.1通过yml文件进行配置

在yml中规定他的端口号以及应用名称并把它注册到Eureka中,同时规定服务地址以及具体的提供服务的路径

server:
  port: 9527

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

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
eureka:
  instance:
    hostname: cloud-gateway-service
  client: #服务提供者provider注册进eureka服务列表内
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka



关于这个配置文件尤其是配置路由的地方他是这样的外面套了一层9527所以你肯定得先访问9527然后在9527底下看看能不能找http://localhost:8001这个地址,然后断言看看这个地址下面有没有相应的接口路径如果有则成功没有则失败。

主启动类没啥特别的

配置好了之后启动7001,8001,以及服务网关9527,然后访问对应接口以前我们访问接口都是这样访问的,http://localhost:8001/payment/get/31但是我现在添加了服务网关我们就不用把8001这个端口暴露出来只需要访问http://localhost:9527/payment/get/31。

如果之后配置的路由越来越多了那么yml文件就会越写越长,所以还有一种硬编码的方式进行路由配置。

4.2硬编码方式配置路由

加入以下配置类

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("wjz_path",r->r.path("/zijie").uri("https://www.bytedance.com/")).build();
        return routes.build();
    }
}

我们可以规定自己的path,uri是目标访问的网址比如这是字节官网,我套上9527之后进行访问

结果是能正确访问到字节官网。

4.3动态路由

之前在没有服务网关的时候负载均衡由Ribbon做的

加入服务网关Gateway之后

现在我们统一对外暴露的是9527然后之前我们在yml里面已经把路由写死了所以它只会访问8001,这时候如果服务越来越多,这样的配置就显得不够灵活了。所以我们要根据微服务的名字来实现动态路由,默认情况下Gateway会根据注册中心注册服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。

在yml文件中的gateway下面加上这段

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

同时将uri改成lb://cloud-payment-service利用服务名作为路由。(lb是负载均衡),然后我们再访问localhost:9527/payment/lb(这个接口会返回端口号)看看什么结果。

可以看出一次8001一次8002这就达到了动态路由的目的。

5.Predicate的使用

在之前的yml配置文件中路由的配置找到uri后回向下找方法的具体路径,这个Path写在了predicate下面,找到uri之后再向下寻找断言,如果断言返回true就执行对应的方法,但是断言类型有很多种上面只配置了Path一种,进一步丰富断言的类型能让我们更快更准确的找到相应的服务。

5.1 After

在什么什么时间之后才能访问相应的接口他的格式是这样的

官网上给的是丹佛时间而且这个时间必须得满足这个格式,我们可以通过Java8的一个新特性得到这个串串。

public class GenerateTime {
    public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println(now);
    }
}
//一执行就能得到当前时期的当前时间
//2022-02-06T13:32:33.079+08:00[Asia/Shanghai]

然后我们在配置文件上加上这段时间测试一下接口。我写这段话需要时间所以我启动访问接口的时候一定是在这个时间之后所以我应该能访问成功。

确实能访问成功。我们再将时间改成一个月之后3月6号那估计得报错。

访问同样的接口由于断言错误所以这次报错。

Before,Between跟这个差不多,Between:时间1,时间2传两个时间就是在这两个时间中间的意思。

5.2 cookie

官网举例

按照官网的提示我在我自己的yml添加一下cookie的配置

          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
            - After=2022-02-06T13:32:33.079+08:00[Asia/Shanghai]
            - cookie=username,wjz

然后重启一下9527,用curl(点此安装)进行测试

我们可以看出如果你配置了cookie但是你没加cookie那就啥也找不到404NotFound,加上cookie才能访问成功。

5.3 Header

官网举例

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, d+ #-H属性必须得是X-Request-Id,然后后面的必须得是整数

这个Header断言需要配置一个请求头属性和正则表达式,现在将官网给的这个- Header属性复制粘贴到自己的yml文件上测试。

后面的举一反三不一一列举了。

6.Filter

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能由指定路由进行使用。

Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。

过滤器[官网](Spring Cloud Gateway)上提供了一些同样是在yml文件当中加filters:属性种类比较多。还有一种是自定义过滤器。

首先我们加上一个过滤器的配置类

然后实现两个接口GlobalFilter, Ordered。

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    //全局过滤器
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("***********come in MyLogGateWayFilter:" + new Date());
        //判断每个请求带不带uname
        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;
    }
}

这个也就是说你下次访问的时候后面必须带上uname如果不带直接拦截。

加上这个uname

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/732930.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号