• Zuul网关简介以及使用


    一、概述简介

    官网:https://docs.spring.io/spring-cloud-netflix/docs/2.2.9.RELEASE/reference/html/#router-and-filter-zuul

    1.1. Zuul是什么

    官网解释:路由是微服务架构的一个组成部分。例如,/可能映射到您的 Web 应用程序、/api/users映射到用户服务和/api/shop映射到商店服务。 Zuul是来自 Netflix 的基于 JVM 的路由器和服务器端负载均衡器。

    Zuul就是一个网关,那么到底什么是网关?

    API网关为微服务架构中的服务提供了统一的访问入口,客户端通过API网关访问相关服务。API网关的定义类似于设计模式中的门面模式,它相当于整个微服务架构中的门面,所有客户端的访问都通过它来进行路由及过滤。它实现了请求路由、负载均衡、校验过滤、服务容错、服务聚合等功能。

    在这里插入图片描述

    假如系统特别庞大的情况下,会搭建多个API网关,API网关实现了微服务集群的负载均衡,而对于多个微服务网关,这时候一般会在他的上层通过Nginx来实现网关的负载均衡。

    在这里插入图片描述

    1.2. Zuul能干嘛

    Zuul包含了如下最主要的功能:路由、对请求过滤、负载均衡、灰度发布

    什么是路由?

    路由功能负责将外部请求转发到具体的服务实例上去,是实现统一访问入口的基础,可以理解为就是请求转发。

    什么是请求过滤?

    可以指定哪些请求允许访问,哪些请求不允许访问!

    什么是负载均衡?

    假如有多台机器部署了A服务,每次访问都访问到一台服务器上,那么多台机器根本没起到作用,搭建多台往往是为了减轻单机的压力,这时候就需要指定个策略,比如轮询、随机、权重等等,我们又称之为负载均衡策略

    网关为入口,由网关与微服务进行交互,所以网关必须要实现负载均衡的功能;
    网关会获取微服务注册中心里面的服务连接地址,再配合一些算法选择其中一个服务地址,进行处理业务。这个属于客户端侧的负载均衡,由调用方去实现负载均衡逻辑

    在这里插入图片描述
    什么是灰度发布?

    起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高。

    在这里插入图片描述

    在灰度发布开始后,先启动一个新版本应用,但是并不直接将流量切过来,而是测试人员对新版本进行线上测试,启动的这个新版本应用,就是我们的金丝雀。如果没有问题,那么可以将少量的用户流量导入到新版本上,然后再对新版本做运行状态观察,收集各种运行时数据,如果此时对新旧版本做各种数据对比,就是所谓的A/B测试。新版本没什么问题,那么逐步扩大范围、流量,把所有用户都迁移到新版本上面来。

    1.3. Zuul现状

    zuul截止cloud的 H.SR12 版本之后就彻底从官网移除了,假如你这时候还想使用zuul,需要注意cloud版本,springboot版本也需要注意,不可以高于2.3.12.RELEASE

    在这里插入图片描述

    二、实战练习

    2.1. 入门demo

    这里我用的是Eureka注册中心,如果你不了解eureka注册中心,但是了解其他的注册中心,比如consul、nacos用这些也是可以的。入门demo需要一个注册中心还有一个微服务(任意一个服务都可以,只要能访问接口就行),对于注册中心和微服务我这里就不搭建了,直接用Eureka注册中心,然后只搭建一个网关服务。

    Eureka注册中心:https://blog.csdn.net/weixin_43888891/article/details/125325794

    现在我有个8001服务然后注册到了Eureka注册中心当中,8001有以下接口,现在我要实现通过9527端口来访问这个接口。

    @RestController
    @Slf4j
    public class PaymentController {
    
        @Autowired
        private PaymentMapper paymentMapper;
    
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping(value = "/payment/get/{id}")
        public CommonResult<Payment> getPaymentById(HttpServletRequest request, @PathVariable("id") Long id) {
            String header = request.getHeader("X-Request-red");
            System.out.println(header);
            System.out.println(request.getHeader("CUSTOM-REQUEST-HEADER"));
            Payment payment = paymentMapper.selectById(id);
            log.info("*****查询结果:{}", payment);
            if (payment != null) {
                return new CommonResult(200, "查询成功, 服务端口:" + serverPort, payment);
            } else {
                return new CommonResult(444, "没有对应记录,查询ID: " + id + ",服务端口:" + serverPort, null);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.新建一个项目,可以是聚合,也可以是单独的一个springboot项目
    2.引入依赖,这里我用的cloud的Hoxton.SR12版本+springboot的2.3.12.RELEASE版本,也是cloud最后一个支持zuul的版本。

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <springboot.version>2.3.12.RELEASEspringboot.version>
        <springcloud.version>Hoxton.SR12springcloud.version>
    properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
               <groupId>org.springframework.bootgroupId>
               <artifactId>spring-boot-dependenciesartifactId>
               <version>${springboot.version}version>
               <type>pomtype>
               <scope>importscope>
           dependency>
            <dependency>
               <groupId>org.springframework.cloudgroupId>
               <artifactId>spring-cloud-dependenciesartifactId>
               <version>${springcloud.version}version>
               <type>pomtype>
               <scope>importscope>
           dependency>
        dependencies>
    dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-zuulartifactId>
        dependency>
    
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    3.yml配置

    server:
      port: 9527
    
    spring:
      application:
        name: cloud-zuul-gateway
    
    eureka:
      client:
        service-url:
          #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
          defaultZone: http://eureka7001.com:7001/eureka
      instance:
        instance-id: gateway-9527.com
        prefer-ip-address: true 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.启动类添加注解:@EnableZuulProxy
    5.测试:

    1. 启动注册中心
    2. 启动8001服务
    3. 启动9527网关服务

    不通过网关访问:http://localhost:8001/payment/get/1
    通过网关访问:http://localhost:9527/cloud-payment-service/payment/get/1

    zuul映射配置 + 注册中心注册后对外暴露的服务名称 + rest调用地址

    在这里插入图片描述

    2.2. 路由访问映射规则

    zuul在不添加配置的情况下,默认就是允许通过服务名称来调用其他服务的,zuul也可以指定url来访问

    (1)通过url来访问

    这样就是不通过注册中心来转发请求。

     zuul:
      routes:
        users:	# users是自己定义的路由名称
          path: /mypayment/**
          url: http://127.0.0.1:8001
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述刚刚我们可以根据服务名称来访问,这时候可以通过我们自定义的路由规则mypayment来访问。

    (2)配置路由

    下面配置就相当于给注册中心当中的cloud-payment-service服务名称配置了一个路由,只要访问mypayment就是访问cloud-payment-service服务名称的服务,一共有三种配置方式,如下所示:

    方式一:
    zuul:
      routes: # 路由映射配置
        mypayment.path: /mypayment/**                #/myusers被转发到服务名称为cloud-payment-service的服务
        mypayment.serviceId: cloud-payment-service   #注册进eureka服务器的服务名称
    
    方式二:(方式2和1其实就是一样的,只不过是将自定义的那个名字提到了上面)
    zuul:
      routes: # 路由映射配置
      	mypayment:  # mypayment就是自己取的名字,这样就是可以设置多个路由,通过名称来区分
        	path: /mypayment/**                #/myusers被转发到服务名称为cloud-payment-service的服务
       		serviceId: cloud-payment-service   #注册进eureka服务器的服务名称
    
    方式三:
    zuul:
      routes:
        cloud-payment-service: /mypayment/**  #/myusers被转发到服务名称为cloud-payment-service的服务
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    (3)关闭服务名称访问

    现在还有个问题,既然我已经自定义了路由地址,那么我肯定不希望他再通过服务名称来访问,这时候可以选择添加如下配置,关闭服务名称访问。

    zuul:
      ignored-services: cloud-payment-service		#关闭服务名称查询
    
    • 1
    • 2

    关闭后再访问直接404!

    在这里插入图片描述

    可以指定关闭具体的服务名称访问,也可以用 "*" 代表全部。

    zuul:
      ignored-services: "*"
    
    • 1
    • 2

    示例意味着所有调用(例如/myusers/101)都被转发到users服务上。但是,包括在内的呼叫/admin/无法访问。

     zuul:
      ignoredPatterns: /**/admin/**
      routes:
        users: /myusers/**
    
    • 1
    • 2
    • 3
    • 4

    ignoredPatterns就是可以 指定哪些请求 不允许进行路由。

    (4)负载均衡

    由于Zuul自动集成了Ribbon,所以Zuul天生就有负载均衡

    如下有两个服务名称为CLOUD-PAYMENT-SERVICE的,这时候调用的时候他会以轮询的负载均衡策略来调用。

    在这里插入图片描述

    (5)路由服务

    在这里插入图片描述

    (6)设置统一公共前缀

    zuul: 
      prefix: /hhh
    
    • 1
    • 2

    在这里插入图片描述
    设置完过后不添加前缀访问的时候会404!

    需要注意:假如设置为zuul的时候,添加zuul前缀访问也会404

    zuul: 
      prefix: /zuul
    
    • 1
    • 2
    分析:
    1. 分析后得知/zuul的默认context-path是/zuul。
    2. 之所以在默认的情况下我们不加zuul也可以请求成功是因为它帮我们做了url的裁剪。
    (通俗的讲就是不加zuul.prefix=/zuul配置的话,以下两个url都可以请求成功
    * http://localhost:16000/consumer/consumers/1
    * http://localhost:16000/zuul/consumer/consumers/1)
    
    解决:
    1. 将默认context-path设置为空:zuul.servlet-path=/
    2. 配置网关zuul的统一前缀:zuul.prefix=/zuul
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    zuul:
      prefix: /zuul
      servlet-path: /
    
    • 1
    • 2
    • 3

    设置完之后就必须通过前缀/zuul可以访问,不加前缀就访问不了

    (7)指定路由不设置前缀

    zuul:
      prefix: /myzuul #代表的是所有的路由前缀
      ignored-services: "*"
      routes: # 路由映射配置
        mypayment.path: /mypayment/**                 #IE地址栏输入的路径
        mypayment.serviceId: cloud-payment-service   #注册进eureka服务器的服务名称
        mypayment.stripPrefix: false  #默认是true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    prefix是设置全局的前缀,stripPrefix是针对单个路由是否要用前缀访问的设置,默认是true,这个是官网也有说明,但是问题是当设置为false的时候不管设置不设置前缀访问都是404,我认为是版本bug。

    2.3. 查看路由信息

    1.添加xml

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-actuatorartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    2.添加yml配置,开启查看路由的端点

    management:
      endpoints:
        web:
          exposure:
            include: 'routes' 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    2.4. Zuul Http 客户端

    Zuul 使用的默认 HTTP 客户端现在由 Apache HTTP 客户端支持。

    可以通过设置ribbon.restclient.enabled=true or ribbon.okhttp.enabled=true 来切换客户端。

    如果您想自定义 Apache HTTP 客户端或 OK HTTP 客户端,请提供类型为CloseableHttpClient的 bean 或 OkHttpClient 的 bean 。

    三、过滤器

    3.1. 什么是过滤器

    过滤功能负责对请求过程进行额外的处理。

    在这里插入图片描述

    3.2. 自定义 Zuul 过滤器

    过滤类型:

    1. pre: 在请求被路由到目标服务前执行,比如权限校验、打印日志等功能;
    2. routing: 在请求被路由到目标服务时执行
    3. post: 在请求被路由到自标服务后执行,比如给目标服务的响应添加头信息,收集统计数据等功能;
    4. error: 请求在其他阶段发生错误时执行。
    @Component
    @Slf4j
    public class PreLogFilter extends ZuulFilter {
    
        // 请求类型
        @Override
        public String filterType() {
            return "pre";
        }
    
        // 假如多个过滤器,会根据这个数字来进行排序执行
        @Override
        public int filterOrder() {
            return 1;
        }
    
        // 过滤器是否开启
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        // 执行自己的业务逻辑
        @Override
        public Object run() {
            RequestContext requestContext = RequestContext.getCurrentContext();
            HttpServletRequest request = requestContext.getRequest();
            String host = request.getRemoteHost();
            String method = request.getMethod();
            String uri = request.getRequestURI();
            log.info("=====> Remote host:{},method:{},uri:{}", host, method, uri);
            System.out.println("********" + System.currentTimeMillis());
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    调用前会执行。

    在这里插入图片描述

    通过配置文件当中,可以关闭过滤器。

    zuul:
      PreLogFilter:
        pre:
          disable: true # 关闭前置过滤器
    
    • 1
    • 2
    • 3
    • 4

    3.3. 其他过滤器

    在这里插入图片描述

    在ZuulFilter类的基础上还延伸了很多Filter,具体的可以根据自己的应用场景来选择。

    在这里插入图片描述

    四、超时时间设置

    如果您使用@EnableZuulProxy,则可以使用代理路径上传文件,只要文件很小,它应该可以工作。对于大文件接口访问慢,这时候需要设置超时时间,如下:

    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
    ribbon:
      ConnectTimeout: 3000
      ReadTimeout: 60000
    
    • 1
    • 2
    • 3
    • 4

    如果你想通过 Zuul 代理的请求,配置套接字超时和读取超时,有下面选项:

    zuul:
      host:
        connect-timeout-millis: 40000
        socket-timeout-millis: 40000
        connection-request-timeout-millis: 40000
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在我公司的项目当中这些超时时间都设置了,配置如下,仅供参考:

    zuul.host.connect-timeout-millis=40000
    zuul.host.socket-timeout-millis=40000
    zuul.host.connection-request-timeout-millis=40000
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=40000
    ribbon.ReadTimeout=10000
    ribbon.ConnectTimeout=10000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Gateway网关:https://blog.csdn.net/weixin_43888891/article/details/126396083

  • 相关阅读:
    Word控件Spire.Doc 【图像形状】教程(7): 如何使用 C# 在 Word 中替换图像
    Git基本操作(2)
    面试突击44:volatile 有什么用?
    2022年小美赛思路/2022年第十一届“认证杯”数学中国数学建模国际赛思路/2022年认证杯思路
    Spring整合MyBatis、Spring整合JUnit4(Spring纯注解开发完结篇)
    Github2024-04-25 开源项目日报Top10
    vue快速入门(四十四)自定义组件
    利用Jdk动态代理模拟MyBatis的Mapper功能
    leetcode经典题库(简单)
    JAVA毕业设计高校勤工助学管理系统计算机源码+lw文档+系统+调试部署+数据库
  • 原文地址:https://blog.csdn.net/weixin_43888891/article/details/126445571