• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

spring cloud——03gateway、zuul

武飞扬头像
乌鱼鸡汤
帮助1

(一)前置知识:

(1)BIO(同步阻塞)

传统BIO,一个连接一个线程,客户端有连接请求时服务器端就需要启动一个线程进行处理。线程开销大。数据的读写必须阻塞在一个线程内,等待其完成。

伪异步IO:将请求连接放入线程池,一对多,但线程还是很宝贵的资源,但底层还是同步阻塞IO。具体是将客户端的Socket请求封装成一个task任务(实现Runnable类)然后投递到线程池中去,配置相应的队列实现。

存在的问题:在读取数据较慢时(比如数据量大、网络传输慢等),大量并发的情况下,其他接入的消息,只能一直等待,这就是最大的弊端。这就是阻塞IO存在的弊端,对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性。

(2)NIO(同步非阻塞)

一个请求一个线程,但客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。主要解决BIO高负载、高并发的(网络)应用的性能问题。
Buffer是一个对象,包含一些要写入或者读出的数据(读写都要经过Buffer缓冲区),实际上是一个数组,提供对数据结构化访问以及维护读写位置等信息。
对数据的读取和写入要通过Channel通道,通道与流不同之处在于通道时双向的(可以读、写或者同时进行),而流只在一个方向移动。一般使用的SocketChannle和ServerSocketChannle一般都是SelectableChannel,用于网络读写的Channel,用于文件操作的Channel是FileChannel。
多路复用器 Selector:当Channel管道注册到Selector选择器以后,Selector会分配给每个管道一个key值唯一标识,Selector选择器是以轮询的方式进行查找注册的所有Channel,当我们的Channel准备就绪或者监听到相应的事件状态的时候,selector就会识别这个事件状态,通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。
(3)AIO(异步非阻塞)

一个有效请求一个线程,客户端的I/O请求都是由OS先完成了,再通知服务器应用去启动线程进行处理。

在NIO编程之上引入了异步通道的概念,并提供了异步文件和异步套接字的实现,从而真正实现了异步非阻塞。AIO它不需要通过多路复用器对注册的通道进行轮询操作即可以实现异步读写,从而简化了NIO编程模型。

适用于连接数目多且连接比较长(重操作)的架构,充分调用OS参与并发操作,编程比较复杂;而 NIO方式适用于连接数目多且连接比较短(轻操作)的架构,并发局限于应用中。

(二) gateway 与zuul的区别

Zuul: 【BIO】
是netflix公司的项目,本质上是web servlet,基于JavaEE Servlet技术栈 (tomcat),使用阻塞API,处理的是http请求,没有提供异步支持,不支持任何长连接,比如websocket。
gateway: 【 AIO】
Spring Boot和Spring Webflux提供的Netty底层环境,不能和传统的Servlety游戏容器一起使用,也不能打包成一个WAR包,使用非阻塞API,支持websocket。

1、gateway对比zuul多依赖了spring-webflux,内部实现了限流、负载均衡等,扩展性也更强,但同时也限制了仅适合于Spring Cloud套件。
zuul则可以扩展至其他微服务框架中,其内部没有实现限流、负载均衡等。
  
2、zuul仅支持同步,gateway支持异步。

3、gateway线程开销少,支持各种长连接、websocket,spring官方支持,但运维复杂,
zuul编程模型简单,开发调试运维简单,有线程数限制,延迟堵塞会耗尽线程连接资源。

netty与tomcat的区别

(三)路由、断言、过滤

Gateway 的三大概念
Route(路由):路由是构建网关的基本模块,它由 ID、目标 URI、一系列的断言和过滤器组成,若是断言为 true 则匹配该路由

Predicate(断言):参考的是 Java8 中的 java.util.function.Predicate。开发人员能够匹配 HTTP 请求中的全部内容(例如请求头或请求参数),若是请求与断言相匹配则进行路由

Filter(过滤):指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,能够在请求被路由以前或以后对请求进行修改。

cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由。如果微服务是集群必须开启,开启后由注册中心进行负载均衡,决定请求哪个微服务
      routes:
        - id: payment_routh1    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001   #微服务是单机  (真实请求的地址,这个地址会进行拼接:http://localhost:8001/hmoe/div1/** ,相当于将网关的ip:port套接字换成uri的)
# 比如:浏览器输入url :http://localhost:9001/payment/get/1 成功经网关转发后会变成请求 http://localhost:8001/payment/get/1 ,相当于更换了套接字。
          uri: lb://cloud-payment-service #微服务是集群 注意前面一定要有lb://  (真实请求的地址)
          predicates: # 断言,断言全部满足才进行转发
            - Path=/hmoe/div1/**   # 浏览器输入的路径地址(相当于假的地址)
            - Header=X-Request-Id, \d   # 请求头有多个数字(\d  为正则表达式)
            - Cookie=chocolate, ch.p # 请求需要携带名字为chocolate的 Cookie 
            - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 请求的时间必须在指定时间之后

       - id: payment_routh2    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001   #微服务是单机  (真实请求的地址)
          uri: lb://cloud-payment-service #微服务是集群 注意前面一定要有lb://  (真实请求的地址, 这个地址会进行拼接:http://cloud-payment-service/hmoe/div2/** ,相当于将端口号换掉)
          predicates: # 断言,断言全部满足才进行转发
            - Path=/hmoe/div2/**   # 浏览器输入的路径地址(相当于假的地址)
            - Header=X-Request-Id, \d   # 请求头有多个数字(\d  为正则表达式)
            - Cookie=chocolate, ch.p # 请求需要携带名字为chocolate的 Cookie 
            - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 请求的时间必须在指定时间之后

学新通

比如上面的配置Gateway : 浏览器输入url:https://123.com/hmoe/div1/123asdf?username=lihua&pwd=123
这个url请求会先根据请求携带的路径(/hmoe/div1/123asdf)找到(匹配到)具体的路由。比如:上面的配置就会找到id为payment_routh1的路由。然后过滤器处理请求。比如username为null 直接过滤掉 ,当请求满足过滤器后。根据路由要求的断言进行判断当满足所有断言时就会根据指定的uri: http://localhost:8001(lb://cloud-payment-service,微服务集群就写注册中心的服务名,由注册中心负载均衡) 进行转发。

简单的例子:
Gateway 配置如下:

server:
  port: 9527
spring:
  application:
    name: cloud-gateway9527
  cloud:
    gateway:
      routes:
        - id: news						# 路由id
          uri: http://news.百度.com	# 真实调用地址
          predicates:
            - Path=/guonei				# 断言,符合规则进行路由

浏览器虽然输入 localhost:9527/guonei,却会转发到指定的地址(这个转发不是重定向,浏览器的地址还是不会变的,相当于 localhost:9527/guonei 这个请求去代理请求了http://news.百度.com/guonei):http://news.百度.com/guonei

(四)通过yml配置路由

1、导入pom

<dependencies>
    <!-- web -->
	<!--<dependency>
	     <groupId>org.springframework.boot</groupId>
	     <artifactId>spring-boot-starter-web</artifactId>
	 </dependency>-->
    <!--gateway-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--eureka-client, gateway也是一种微服务需要注册到eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>
学新通

注意:这里不能导入web包,因为Gateway的底层是webflux,而webflux和web是冲突的,只能有一个,需要注意!

什么是webflux 与 web

spring 官网了解响应式编程

  • 什么是反应式处理?
    响应式处理是一种范例,它使开发人员能够构建可以处理背压(流控制)的非阻塞、异步应用程序。

  • 为什么要使用反应式处理?
    反应式系统更好地利用现代处理器。此外,在反应式编程中包含背压可确保解耦组件之间具有更好的弹性。

开发人员从阻塞代码转向非阻塞代码的主要原因之一是效率。反应式代码用更少的资源做更多的工作。Project Reactor 和 Spring WebFlux 使开发人员能够利用多核下一代处理器——处理潜在的大量并发连接。通过反应式处理,您可以用更少的微服务实例满足更多的并发用户。

学新通
学新通

2、配置yml——静态路由(即微服务为单机版)

cloud:
    gateway:
      routes:
        - id: payment_routh1    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001 # 微服务是单机  
          # uri指定的地址为真实请求的地址,这个地址会进行拼接:http://localhost:8001/hmoe/div1/** ,
          # 相当于将网关的ip:port套接字换成uri的)
          # 比如:浏览器输入url :http://localhost:9001/payment/get/1 成功经过网关转发后会变成请求http://localhost:8001/payment/get/1 ,相当于更换了套接字。
          predicates: # 断言,断言全部满足才进行转发
            - Path=/hmoe/div1/**   # 浏览器输入的路径地址(相当于假的地址)
            - Header=X-Request-Id, \d   # 请求头有多个数字(\d  为正则表达式)
            - Cookie=chocolate, ch.p # 请求需要携带名字为chocolate的 Cookie 
            - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 请求的时间必须在指定时间之后

       - id: payment_routh2    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #微服务是单机  (真实请求的地址)
          predicates: # 断言,断言全部满足才进行转发
            - Path=/hmoe/div2/**   # 浏览器输入的路径地址(相当于假的地址)
            - Header=X-Request-Id, \d   # 请求头有多个数字(\d  为正则表达式)
            - Cookie=chocolate, ch.p # 请求需要携带名字为chocolate的 Cookie 
            - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 请求的时间必须在指定时间之后

学新通

3、测试

  • 启动7001

  • 启动8001-cloud-provider-payment8001

  • 启动9001网关

访问说明

添加网关前 - http://localhost:8001/payment/get/1
添加网关后 -访问 http://localhost:9527/payment/get/1相当于访问http://localhost:8001/payment/get/1
两者访问成功,返回相同结果

3、配置yml——动态路由(即微服务为集群版)

改为动态路由的前提是微服务是集群,通过微服务在注册中心的名字进行负载均衡,由注册中心决定转发到哪个具体的微服务。
改为动态路由只需要改配置文件的两个地方:

  1. 修改 spring.cloud.gateway.discovery.locator.enabled 默认为 false ,true 为开启动态
    #开启从注册中心动态创建路由的功能,利用微服务名进行路由
    spring.cloud.gateway.discovery.locator.enabled = true
  2. 修改uri
uri: lb://cloud-payment-service  #在注册中心中的微服务名 注意:前面一定要有lb://

4、测试

  • 启动7001

  • 启动8001-cloud-provider-payment8001

  • 启动8002-cloud-provider-payment8002

  • 启动9001网关

访问说明

修改为动态路由后,访问 http://localhost:9527/payment/get/1,会轮询的访问 http://localhost:8001/payment/get/1 、 http://localhost:8002/payment/get/1

(五)通过config配置路由

不推荐使用

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 customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
    {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();

        routes.route("path_route_atguigu",
                r -> r.path("/guonei")
                        .uri("http://news.百度.com/guonei")).build();

        return routes.build();
    }
学新通

(六)配置断言

predicates属性用来配置断言,只有当配置的断言全部(为true)满足时,才会进行转发

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
		predicates: # 断言,断言全部满足才进行转发
            - Path=/hmoe/div1/**   # 浏览器输入的路径地址(相当于假的地址)
            - Header=X-Request-Id, \d   # 请求头有多个数字(\d  为正则表达式)
            - Cookie=chocolate, ch.p # 请求需要携带名字为chocolate的 Cookie 
            - After=2017-01-20T17:42:47.789-07:00[America/Denver] # 请求的时间必须在指定时间之后

官网查看全部断言

测试:

C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8001","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8002","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8001","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8002","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8001","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8002","data":{"id":1,"serial":"1111"}}
C:\Users\15594>curl http://localhost:9001/payment/get/1 --cookie "chocolate=chip"
{"code":200,"message":"查询成功 8001","data":{"id":1,"serial":"1111"}}
C:\Users\15594>

(七)过滤器(过滤链)

两个概念:

  • Filter种类: Spring Cloud Gateway 的 Filter 分为两种:GatewayFilter 与 GlobalFilter。GlobalFilter 会应用到所有的路由上,而 GatewayFilter 将应用到单个路由或者一个分组的路由上。
  • 生命周期: Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。
    PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
    POST: 这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

1、GatewayFilter (单个、分组路由)

  1. PRE 路由转发前(Request)
cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址 注意前面一定要有lb://
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
            - After=2021-08-26T10:04:15.914 08:00[Asia/Shanghai] # 在这个时间之后才能起作用
            - Cookie=chocolate, ch.p
          filters: # 过滤器
            - AddRequestHeader=X-Request-red, blue # 在路由转发前像请求头添加键值对(X-Request-red, blue)
            - AddRequestHeader=key, value # k,名字随意都行
学新通

过滤器修改请求时可以传递变量进行修改,可以不写死。如下:

predicates:
            - Path=/payment/get/{id}/**         # 断言,路径相匹配的进行路由
            - After=2021-08-26T10:04:15.914 08:00[Asia/Shanghai] # 在这个时间之后才能起作用
            - Cookie=chocolate, ch.p
          filters: # 过滤器
            - AddRequestHeader=X-Request-red, blue-{id} # 在路由转发前像请求头添加键值对(X-Request-red, blue)
            - AddRequestHeader=key, value{id} # k,名字随意都行

更多配置查看官网

测试:
过滤请求,向请求中加入一些内容

学新通

学新通

  1. Post 路由转发后(Response)
cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址 注意前面一定要有lb://
          predicates:
            - Path=/payment/get/{id}/**         # 断言,路径相匹配的进行路由
            - After=2021-08-26T10:04:15.914 08:00[Asia/Shanghai] # 在这个时间之后才能起作用
            #- Cookie=chocolate, ch.p
          filters: # 过滤器
              # 路由前 Pre(Request)
            - AddRequestHeader=X-Request-red, blue-{id} # 在路由转发前像请求头添加键值对(X-Request-red, blue)
            - AddRequestHeader=key, value:{id}
              # 路由后Post(Response):
            - AddResponseHeader=X-Response-Red, Blue # 在路由转发后,在响应头中插入(k,v)
            - AddResponseHeader=user_name, lihua {id}
学新通

更多配置查看官网

测试:
学新通

2、GlobalFilter(全局路由)

GlobalFilter - 有10种

注意:全局过滤器(GlobalFilter)比GatewayFilter 优先级高,也就是先执行全局在执行单个
自定义全局过滤器:

 - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
#          uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
/**
 * 自定义全局过滤器
 * @author 15594
 */

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //全局日志
        log.info("***********come in MyLogGateWayFilter:  " new Date());
        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;
    }
}
学新通
@GetMapping("/payment/get")
    public CommonResult<Payment> getPaymentById( Long id, HttpServletRequest request,String name){
        Payment payment = paymentService.getPaymentById(id);
        log.info("****插入结果*****" payment);
        //查看过滤器是否向请求头插入X-Request-red键
        String header = request.getHeader("X-Request-red");
        System.out.println(request.getHeader("key"));
        System.out.println(header);
        if (payment!=null){
            return new CommonResult(200,"查询成功 8001",payment);
        } else {
            return new CommonResult(444,"没有这条记录 8001,id=",id);
        }

    }

测试:
满足全局过滤器要求:
学新通
不满足:
学新通

3、Filter结合Hystrix使用

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbgkcg
系列文章
更多 icon
同类精品
更多 icon
继续加载