SpringCloud之服务网关

一、GateWay新一代网关

1、概念简介
官网:点击访问
(1)Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;
但在2.x版本中,Zuul的升级一直跳票,SpringCloud最后自己研发了SpringCloud GateWay替代了Zuul。

(2)SpringCloud Gateway是基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效统一的API路由管理方式。

(3)SpringCloud Gateway作为SpringCloud生态系统中的网关,目标是替代Zuul,在SpringCloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而提升网关的性能,SpringCloud Gateway是基于WebebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

(4)SpringCloud Gateway的目标是提供统一的路由方式且基于Filter链的方式提供了网关的基本功能,例如:安全、监控/指标和限流等。

(5)能干嘛?
A、反向代理
B、鉴权
C、流量控制
D、熔断
E、日志监控

(6)微服务架构中网关在哪里?
在这里插入图片描述
(7)Zuul和GateWay
A、我们为什么选择GateWay
一方面因为Zuul 1.0已经进入维护阶段;而且GateWay是SpringCloud团队研发的值得信赖,使用起来简单便捷;而且很多功能是Zuul没有用起来的。
GateWay 是基于异步非阻塞模型进行开发的,性能方面不需要担心。虽然Netflix早就发布了最新的Zuul 2.x,但是SpringCloud并没有对其进行整合。而且Netflix公司很多组件都宣布进入维护期,前景堪忧。

B、SpringCloud Gateway具有以下特性

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

C、SpringCloud Gateway和Zuul的区别
在SpringCloud Finchley正式版之前,SpringCloud推荐的网关是Netflix提供的Zuul:

  • Zuul 1.x是一个基于阻塞I/O的API GateWay
  • Zuul 1.x基于Servlet 2.5使用阻塞架构,它不支持任何长连接(如WebScoket),Zuul的设计模式和Nginx较像:每次I/O操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,区别是Nginx是C++实现,Zuul是java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。
  • Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul 2.x 的性能较Zuul 1.x有较大的提升。在性能方面,根据官方提供的基准测试,SpringCloud GateWay的RPS(每秒请求数)是Zuul的1.6倍。
  • SpringCloud Gateway建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上,使用非阻塞的API。
  • SpringCloud Gateway还支持WebScoket,并且与Spring紧密集成拥有更好的开发体验。

D、Zuul1.x模型
SpringCloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。

  • Servlet由servlet container进行生命周期管理。
  • container启动时构造servlet对象并调用servlet init()进行初始化;
  • container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service();
  • container关闭时调用servlet destory()销毁servlet;
    在这里插入图片描述
    上述模式缺点
    servlet是一个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程,在并发布高的场景下这种模型是适用的。但是一遇到高并发(比如jemeter压),线程数量就会上涨,而线程资源代价是昂贵的(上下文切换,内存消耗大)严重影响请求的处理时间。在一些简单业务场景下,不希望为每一个request分配一个线程,只需要一个或几个线程就能应对极大并发请求,这种业务场景下servlet模型没有优势。
    所以Zuul 1.x是基于servlet之上的一个阻塞式处理模型,即spring实现了处理所有request请求的一个servlet(DispatcherServlet),并由该servlet阻塞式处理。所以SpringCloud Zuul无法摆脱servlet模型的弊端。

E、WebFlux是什么
传统Web框架,比如说:struts2,springmvc等都是在Servlet API与Servlet容器基础上运行的。
但是,在Servlet3.1之后有人异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor相关的API实现的。现对于传统的Web框架来说,它可以运行在诸如Netty、Undertow即支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)
Spring WebFlux是Spring 5.0引入的新响应式框架,区别于Springmvc,他不需要依赖Servlet API,它是完全异步非阻塞的,并且基于Reactor来实现响应式流规范。

2、三大核心概念
(1)Route路由
是构建网关的基本模块,它由ID、目标URL、一系列的断言和过滤器组成,如果断言为true则匹配该路由。
(2)Predicate断言
参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
(3)Filter过滤
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由之前或之后队请求进行修改。
在这里插入图片描述

  • web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。
  • predicate就是我们的匹配条件;filter就是一个无所不能的拦截器。
  • 有了这两个元素,再加上目标uri就可以实现一个具体的路由了。

3、GateWay工作流程
核心逻辑路由转发+执行过滤器链
在这里插入图片描述
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。

注:在没有端口的路由中定义的URI,HTTP和HTTPS URI的默认端口值分别为80和443。

4、GateWay8527搭建
(1)新建子模块cloud-gateway-gateway9527
(2)编写pom

<dependencies>
    <!-- gateway -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!-- eureka client -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- 引入自定义的api的通用包 -->
    <dependency>
        <groupId>com.atguigu.springcloud</groupId>
        <artifactId>cloud-api-commons</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- 一些通用配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </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>
</dependencies>

(3)编写appkication.yml

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

(4)编写启动类

@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GateWayMain9527.class,args);
    }
}

(5)在GateWay9527中添加路由映射规则,在application中添加配置

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

(6)启动eureka7001、payemnt8001,gateway9527模块测试网关
在这里插入图片描述
访问说明:
添加网关之前=>http://localhost:8001/payment/get/301在这里插入图片描述
添加网关之后=>http://localhost:9527/payment/get/301在这里插入图片描述
这样做的好处不会暴露真实服务提供微服务端口8001。

(7)GateWay网关路由有两种配置
A、在application.yml中配置
在这里插入图片描述
B、代码中注入RouteLocator的Bean
官方文档给定的示例:在这里插入图片描述
例:通过9527网关访问到外网的百度新闻网址

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator getRouteLocator(RouteLocatorBuilder builder){
        return builder.routes()
                .route("path_route_baidu",r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
    }
}

启动gateway9527访问 http://localhost:9527/guonei:在这里插入图片描述
可以看到路由映射成功!

5、通过微服务名实现动态路由
默认情况下GateWay会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
(1)启动子模块eureka7001+两个服务提供者payment8001、payment8002
(2)在dashboard9527子模块pom文夹中添加eureka-client依赖

<!-- eureka client -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

(3)修改dashboard9527子模块application.yml
开启从服务注册中的创建动态路由功能
在这里插入图片描述
启动dashboard9527子模块,访问http://localhost:9527/payment/lb进行测试:在这里插入图片描述在这里插入图片描述
会对微服务提供者payment8001和payment8002进行轮询访问。实现了通过微服务名进行动态创建路由的功能。

6、GateWay常用的Preredicate
在这里插入图片描述
(1)Loaded RoutePredicateFactory [After]
afte route predicate工厂接受一个参数:datatime。这个predicate匹配在指定的日期时间之后发生的请求。配置after route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

表示该路由匹配所有在2017年1月20日17时42分(丹佛山区时间)之后发起的请求。

(2)Loaded RoutePredicateFactory [Before]
before route predicate工厂接受一个参数:datatime。此predicate匹配在指定的日期时间之前发生的请求。配置before route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

表示该路由匹配所有在2017年1月20日17时42分(丹佛山区时间)之前发起的请求。

(3)Loaded RoutePredicateFactory [Between]
between route predicate工厂接受两个参数:datetime1和datetime2。这个predicate匹配在datetime1之后和datetime2之前发生的请求。datetime2参数必须在datetime1之后。配置between route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

此路由与2017年1月20日17:42(丹佛)之后和2017年1月21日17:42(丹佛)之前的任何请求匹配。它常用来维护windows。

(4)Loaded RoutePredicateFactory [Cookie]
cookie route predicate工厂接受两个参数:cookie名称和正则表达式。该predicate匹配具有给定名称且其值与正则表达式匹配的cookie。配置cookie route predicate工厂的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

该路由匹配具有一个名为chocolate的cookie的请求,该cookie的值与ch.p正则表达式匹配。

(5)Loaded RoutePredicateFactory [Header]
header route predicate工厂接受两个参数:头名称和一个正则表达式。此predicate与具有给定名称且其值与正则表达式匹配的头匹配。配置header route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

如果请求有一个名为X-Request-Id的头,它的值与\d+正则表达式匹配(也就是说,它的值为一个或多个数字),则此路由就匹配。

(6)Loaded RoutePredicateFactory [Host]
host route predicate工厂接受一个参数:主机名模式列表。是一个用"."作为分隔符的Ant-style的模式。该predicate匹配模式的主机头。配置host route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

也支持URI模板变量(例如{sub}.myhost.org)。
如果请求的主机报头的值为www.somehost.org或beta.somehost.org或www.anotherhost.org,则此路由会匹配。
该predicate提取URI模板变量(如前面示例中定义的sub)作为名称和值的映射,并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE中定义的键将其放在ServerWebExchange.getAttributes()中。然后GatewayFilter工厂可以使用这些值。

(7)Loaded RoutePredicateFactory [Method]
Method route predicate工厂接受一个或多个参数:要匹配的HTTP方法。配置method route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

该路由匹配请求方法是GET或POST的请求。

(8)Loaded RoutePredicateFactory [Path]
path route predicate工厂接受两个参数:一个Spring PathMatcher模式列表和一个名为matchOptionalTrailingSeparator的可选标志。配置path route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

如果请求路径为:/red/1、/red/blue或/blue/green,则该路由会匹配。
该谓词提取URI模板变量(例如,在前面的示例中定义的segment)作为名称和值的映射,并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE中定义的键将其放置到ServerWebExchange.getAttributes()中。然后GatewayFilter工厂可以使用这些值.
可以使用一个实用程序方法(称为get)来简化对这些变量的访问。下面的示例展示了如何使用get方法:

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");

(9)Loaded RoutePredicateFactory [Query]
query route predicate工厂接受两个参数:一个必需的参数和一个可选的regexp。配置query route predicate的示例如下:

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

该路由匹配请求中包含"green"查询参数(多参数可用逗号隔开)的请求。

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

如果请求中有"red"查询参数,且"red"查询参数的值为"gree.",则上述路由匹配。注意:green和greet都符合要求。

(10)Loaded RoutePredicateFactory [RemoteAddr]
RemoteAddr route predicate工厂接受一个cidr表示法(IPv4或IPv6)字符串列表(最小大小为1),例如192.168.0.1/16(其中192.168.0.1是IP地址,16是子网掩码)。配置RemoteAddr route predicate的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

如果请求的远程地址是192.168.1.10,则此路由是匹配的。

(11)Loaded RoutePredicateFactory [Weight]
weht route predicate工厂有两个参数:group和weight。权重按组计算。配置weight route predicate的示例如下:

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

该路由表示把大约80%的流量转发到weighthigh.org,把大约20%的流量转发到weighlow.org

7、GateWay的Filter
路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
SprongCloudGateWay内置了多种路由过滤器,他们都由GateWayFilter的工厂类来产生。
(1)生命周期两种:pre(前置)、post(后置)
(2)种类两种:GateWayFilter(单一过滤器)、GloblalFilter(全局过滤器)
(3)自定义全局过滤器
A、新建MyLogGateWayFilter类,实现GlobalFilter, Ordered接口,并重写他们的方法

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) {
        log.info("已进入--->MyLogGateWayFilter"+new Date());
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if(uname == null){
            log.info("用户名为空!");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);  //放行
    }
    @Override
    public int getOrder() {
        return 0;   //加载此过滤器的优先顺序(数字越小,优先级越高)
    }
}

B、测试
启动子模块GateWay8527,重新访问:http://localhost:9527/payment/lb,发现已经不能跟之前一样返回调用微服务的接口了:在这里插入图片描述在这里插入图片描述
可见执行了全局的Filter,逻辑是判断是否有参数uname且不为空!
重新用http://localhost:9527/payment/lb?uname=123发起请求,访问成功:在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值