zoukankan      html  css  js  c++  java
  • 微服务网关Zuul

    1 Zuul简介

    • Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon以及Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

    • 1️⃣动态路由:动态将请求路由到不同后端集群。

    • 2️⃣压力测试:逐渐增加指向集群的流量,以了解性能。

    • 3️⃣负载分配:为每一种负载类型分配对应容量,并弃用超过限定值的请求。

    • 4️⃣静态响应处理:边缘位置进行响应,避免转发到内部集群。

    • 5️⃣身份认证和安全:识别每一个资源的验证要求,并拒绝那些不符合要求的请求。

    • SpringCloud对Zuul进行了整合和增强。

    2 搭建Zuul网关服务器

    2.1 创建工程并导入相关依赖的Maven坐标

    • 修改部分:
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    
    • 完整部分:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>spring_cloud_demo</artifactId>
            <groupId>org.sunxiaping</groupId>
            <version>1.0</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>api_zuul_server7006</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>
        </dependencies>
        
    </project>
    

    2.2 编写配置

    • 创建并配置application.yml:
    server:
      port: 7006
    
    spring:
      application:
        name: api-zuul-server
    

    2.3 编写启动类

    • 启动类:
    package com.sunxiaping.zuul;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableZuulProxy //开启Zuul网关功能
    public class Zuul7006Application {
        public static void main(String[] args) {
            SpringApplication.run(Zuul7006Application.class, args);
        }
    }
    

    3 Zuul中的路由转发

    3.1 概述

    • 最直观的理解:“路由”是根据请求URL,将请求分配到对应的处理程序。

    • 在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。

    • 示例:

    • 在application.yml中配置路由规则

    server:
      port: 7006
    
    spring:
      application:
        name: api-zuul-server
    
    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        product-service: # 路由的id,随便写
          path: /product-service/** # 这里是映射路径
          url: http://localhost:9004 # 映射路径对应的实际URL地址
          sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
    
    • 配置好Zuul路由之后启动服务,在浏览器中输入http://localhost:7006/product-service/product/findById/1,即可访问到商品微服务。

    3.2 面向服务的路由

    3.2.1 概述

    • 微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例来进行处理。如果对每个服务实例手动指定一个唯一的访问地址,然后根据URL去手动实现请求匹配,这样做显然不合理。
    • Zuul支持和Eureka整合开发,根据serviceId自动的从注册中心获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。

    3.2.2 步骤

    • 1️⃣添加Eureka的依赖。
    • 2️⃣开启Eureka的客户端服务发现。
    • 3️⃣在Zuul网关服务中配置Eureka注册中心的相关配置。
    • 4️⃣修改路由中的映射配置。

    3.2.3 应用示例

    • 添加Eureka的依赖:
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    • 开启Eureka的客户端服务发现:
    package com.sunxiaping.zuul;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    
    @SpringBootApplication
    @EnableZuulProxy //开启Zuul网关功能
    @EnableEurekaClient //开启Eureka的客户端服务发现
    public class Zuul7006Application {
        public static void main(String[] args) {
            SpringApplication.run(Zuul7006Application.class, args);
        }
    }
    
    • 在Zuul网关服务中配置Eureka注册中心的相关配置(application.yml):
    # 配置 eureka
    eureka:
      instance:
        # 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
        instance-id: api-zuul-server:${server.port}
        # 显示IP信息
        prefer-ip-address: true
      client:
        service-url: # 此处修改为 Eureka Server的集群地址
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
    
    • 修改路由中的映射配置(application.yml):
    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        product-service: # 路由的id,随便写
          path: /product-service/** # 这里是映射路径
          serviceId: service-product # 配置转发的微服务名称
          sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
    
    • 完整的application.yml:
    server:
      port: 7006
    
    spring:
      application:
        name: api-zuul-server
    
    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        product-service: # 路由的id,随便写
          path: /product-service/** # 这里是映射路径
          serviceId: service-product # 配置转发的微服务名称
          sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
    
    # 配置 eureka
    eureka:
      instance:
        # 主机名称:服务名称修改,其实就是向eureka server中注册的实例id
        instance-id: api-zuul-server:${server.port}
        # 显示IP信息
        prefer-ip-address: true
      client:
        service-url: # 此处修改为 Eureka Server的集群地址
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
    

    3.3 简化的路由配置

    • 在上面的配置中,我们的规则是这样的:
    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        product-service: # 路由的id,随便写
          path: /product-service/** # 这里是映射路径
          serviceId: service-product # 配置转发的微服务名称
          sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
    
    • 使用zuul.routes.<route>.path=/xxx/**:来指定映射路径。<route>是自定义的路由名。

    • 使用zuul.routes.<route>.serviceId=xxx:来指定服务名。

    • 但是绝大多数情况下,我们的<route>路由名往往和服务名是一样的。因此Zuul就提供了一种简化配置语法:zuul.routes.<serviceId>=path

    • 上面的配置可以简化:

    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        #    product-service: # 路由的id,随便写
        #      path: /product-service/** # 这里是映射路径
        #      serviceId: service-product # 配置转发的微服务名称
        #      sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
        service-product: /product-service/** # 如果路由id和对应微服务的serviceId一致,就可以使用简化的路由配置
    

    3.4 默认的路由规则

    • 在使用Zuul的过程中,简化的路由配置已经大大简化了我们手动配置。但是当服务较多的时候,配置也是比较繁琐的,因此Zuul就 指定了默认的路由规则。
    • 默认情况下,一切服务的映射路径就是服务名本身。比如:服务名是service-product,则默认的映射路径就是:/service-product/**
    # 路由配置
    zuul:
      routes:
        # 以商品微服务为例
        #    product-service: # 路由的id,随便写
        #      path: /product-service/** # 这里是映射路径
        #      serviceId: service-product # 配置转发的微服务名称
        #      sensitiveHeaders: #默认zuul会屏蔽Cookie,Cookie不会传到下游服务器,这里设置为空则Cookie可以传递到下游的服务器
        service-product: /product-service/** # 如果路由id和对应微服务的serviceId一致,就可以使用简化的路由配置
        # zuul中的默认路由规则,如果当前的微服务名是service-order,那么对应的映射路径就是/service-order/**
    

    4 Zuul加入后的架构

    uul加入后的架构

    5 Zuul中的过滤器

    5.1 概述

    • 根据前面的知识,我们知道Zuul包含了两个核心功能:对请求的路由过滤。其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能则负责对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础。其实,路由功能在真正运行时,它的路由映射和请求转发同样也由不同的过滤器完成的。所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。

    5.2 ZuulFilter简介

    • Zuul中的过滤器和我们之前使用的javax.servlet.Filter不一样,javax.servlet.Filter只有一种类型,可以通过配置urlPatterns来拦截对应的请求。而Zuul中的过滤器总共有4种类型,且每种类型都有对应的使用场景。

      • 1️⃣PRE:这种过滤器在请求被路由之前调用。我们可以利用这种过滤器实现身份验证、在救你中选择请求的微服务、记录调试信息等。
      • 2️⃣ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache的HttpClient或Netflix的Ribbon请求微服务。
      • 3️⃣POST:这种过滤器在路由到微服务以后执行。这种过滤器可以用来响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等等。
      • 4️⃣ERROR:在其他阶段发生错误时执行该过滤器。
    • Zuul提供了自定义过滤器的功能实现也十分简单,只需要编写一个类去实现Zuul提供的接口。

    public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
        
        //返回字符串,代表过滤器的类型。包含4种:pre、routing、post、error
        abstract public String filterType();
        
        //通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高
        abstract public int filterOrder();
        
        //返回一个boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
        boolean shouldFilter(); //来自IZuulFilter
        
        //过滤器的具体业务逻辑。
        Object run() throws ZuulException; //来自IZuulFilter
        
    }    
    

    5.3 生命周期

    uul的生命周期

    • 正常流程:
      • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器,而后返回响应。
    • 异常流程:
      • 整个过程中,pre或routing过滤器出现异常,都会直接进入error过滤器,error过滤器处理完毕后,会将请求交给post过滤器,最后返回给用户。
      • 如果error过滤器自己出现异常,最终也会进入post过滤器,而后返回。
      • 如果是post过滤器出现异常,会跳转到error过滤器,但是和pre以及routing不同的是,请求不会再到达post过滤器了。
    • 不同过滤器的场景:
      • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了。
      • 异常处理:一般会在error类型和post类型过滤器中结合处理。
      • 服务调用时长统计:pre和post结合使用。
    • 所有内置的过滤器列表:

    所有内置的过滤器列表

    5.4 自定义过滤器

    • 需求:如果请求中有access-token参数,则认为请求有效,放行。
    package com.sunxiaping.zuul.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 自定义身份认证过滤器
     *
     * @author 许大仙
     * @version 1.0
     * @since 2020-10-05 13:24
     */
    @Component
    public class LoginFilter extends ZuulFilter {
        /**
         * 用来定义过滤器类型
         * pre、routing、post、error
         *
         * @return 过滤器类型
         */
        @Override
        public String filterType() {
            return "pre";
        }
    
        /**
         * 指定过滤器的执行顺序,值越小,执行顺序越高
         *
         * @return
         */
        @Override
        public int filterOrder() {
            return 1;
        }
    
        /**
         * 当前过滤器是否生效
         *
         * @return 如果是true,此过滤器生效。如果是false,此过滤器不生效。
         */
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        /**
         * 执行过滤器中的业务逻辑
         * 身份认证:
         * ①所有的请求需要携带一个参数:access-token
         * ②获取request请求
         * ③通过request请求对象获取access-token
         * ④判断token是否为空
         * 如果token == null,身份验证失败
         * 如果token != null,执行后续操作
         * 在Zuul网关中,通过RequestContext的上下文对象,可以获取对象request对象。
         *
         * @return
         * @throws ZuulException
         */
        @Override
        public Object run() throws ZuulException {
            RequestContext currentContext = RequestContext.getCurrentContext();
            HttpServletRequest request = currentContext.getRequest();
    
            String token = request.getHeader("access-token");
    
            //如果token为空,身份认证失败
            if (StringUtils.isEmpty(token)) {
                currentContext.setSendZuulResponse(false); //拦截请求
                currentContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); //设置响应的状态码
            }
            //如果token不为空,执行后续操作
            return null;
        }
    }
    

    6 Zuul网关存在的问题

    • 在实际使用中我们会发现直接使用Zuul存在诸多问题,包括:
    • 1️⃣性能问题:Zuul 1.x版本本质上就是一个同步的Servlet,采用多线程阻塞的模型进行请求转发。简单的讲,每一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其他事情。我们知道Servlet容器线程池的大小是由限制的,当前端请求量大,而后台慢服务比较多的时候,很容易耗尽容器线程池内的线程,造成容器无法接收新的请求。
    • 2️⃣不支持任何长连接,如WebSockt。

    7 Zuul网关的替换方案

    • Zuul 2.x版本:SpringCloud目前还没有整合Zuul 2.x版本。
    • SpringCloud Gateway。
  • 相关阅读:
    datetime函数和random.seed()函数的应用
    TP5 display()
    _STORAGE_WRITE_ERROR_
    nginx下基于ThinkPHP框架的网站url重写
    Thinkphp3.2版本Controller和Action的访问方法
    Undefined class constant 'MYSQL_ATTR_INIT_COMMAND'
    如何将word公式粘贴到TinyMCE里面
    如何将word公式粘贴到eWebEditor里面
    如何将word公式粘贴到wangEditor里面
    如何将word公式粘贴到xhEditor里面
  • 原文地址:https://www.cnblogs.com/xuweiweiwoaini/p/13814405.html
Copyright © 2011-2022 走看看