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

    服务网关的概念:

      API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
      作用和应用场景:

        网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。

    微服务网关Zuul:

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

        动态路由:动态将请求路由到不同后端集群
        压力测试:逐渐增加指向集群的流量,以了解性能
        负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
        静态响应处理:边缘位置进行响应,避免转发到内部集群
        身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。

        Spring Cloud对Zuul进行了整合和增强。

      Zuul加入后的架构:

        

      搭建Zuul网关服务器:

        1.创建工程导入依赖

          在IDEA中创建ZUUL网关工程 zuul_server ,并引入依赖

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>

        2.编写启动类

          @EnableZuulProxy : 通过 @EnableZuulProxy 注解开启Zuul网管功能

    @SpringBootApplication
    // 开启zuul网关功能
    @EnableZuulProxy
    public class ZuulServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulServerApplication.class, args);
        }
    }

        3.编写配置

          创建配置文件 application.yml ,并添加相应配置

    server:
      port: 8080
    spring:
      application:
        name: zuul-server #服务名称

      Zuul 中的路由转发:

        最直观的理解:“路由”是指根据请求URL,将请求分配到对应的处理程序。在微服务体系中,Zuul负责接收所有的请求。根据不同的URL匹配规则,将不同的请求转发到不同的微服务处理。
        只需要在 application.yml文件中配置路由规则即可:

    server:
      port: 8080
    spring:
      application:
        name: zuul-server #服务名称
    ##路由配置
    zull:
      routes:
        #以商品微服务为例
        product-server: #路由id,随便写
        path: /service-product/** #映射路径
        url: http://localhost:9011 #映射路径对应的实际url地址
        sensitiveHeaders: #默认zuul会屏蔽cookie,cookie不会传到下游服务,这里设置为空则取
                          #消默认的黑名单,如果设置了具体的头信息则不会传到下游服务

        启动服务,在浏览器中输入 http://localhost:8080/service-product/product/1 ,即可访问到商品微服务。
          

         面向服务的路由:

          微服务一般是由几十、上百个服务组成,对于一个URL请求,最终会确认一个服务实例进行处理。如果对每个服务实例手动指定一个唯一访问地址,然后根据URL去手动实现请求匹配,这样做显然就不合理。

          Zuul支持与Eureka整合开发,根据ServiceID自动的从注册中心中获取服务地址并转发请求,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例的时候不用修改Zuul的路由配置。

          1.添加Eureka客户端依赖

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

          2.开启Eureka客户端发现功能(从Spring Cloud Edgware版本开始, @EnableDiscoveryClient 或 @EnableEurekaClient 可省略。)

    @SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
    // 开启zuul网关功能
    @EnableZuulProxy
    @EnableDiscoveryClient
    public class ZuulServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulServerApplication.class, args);
        }
    }

          3.添加Eureka配置,获取服务信息

    ##配置eureka
    eureka:
      client:
        serviceUrl:
          defaultZone: http://127.0.0.1:9000/eureka/
      instance:
        preferIpAddress: true

          4.修改映射配置,通过服务名称获取

            因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而是通过服务名称来访问,而且Zuul已经集成了Ribbon的负载均衡功能。

    ##路由配置
    zuul:
      routes:
        #以商品微服务为例
        product-server: #路由id,随便写
          path: /service-product/** #映射路径
          #url: http://127.0.0.1:9011 #映射路径对应的实际url地址
          serviceId: service-product #配置转发的微服务名称

          5.重启服务,在浏览器上通过访问 http://localhost:8080/service-product/product/1 查看效果(访问路径中必须包含路由配置中的 path 部分

            

         简化的路由配置:

          在刚才的配置中,我们的规则是这样的

            zuul.routes.<route>.path=/xxx/** : 指定映射路径。 <route> 是自定义的路由名
            zuul.routes.<route>.serviceId=/service - product:指定服务名。

          而大多数情况下,我们的 <route> 路由名称(product-server)往往和服务名(service-product)会写成一样的。因此Zuul就提供了一种简化的配置语法:
            zuul.routes.<serviceId>=<path>

          上面的配置可以简化为一条:

    ##路由配置
    zuul:
      routes:
        service-product: /service-product/**

        默认的路由规则:

          在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁琐的。
          因此Zuul就指定了默认的路由规则:

            默认情况下,一切服务的映射路径就是服务名本身。
              例如服务名为: service-product ,则默认的映射路径就是: /service-product/**

        添加固定路由前缀:

          zuul.prefix: /api

          访问的路径:http://localhost:8080/api/order-product/order/buy/1

      Zuul 中的过滤器:

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

        ZuulFilter简介:

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

            1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
            2. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
            3. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTPHeader、收集统计信息和指标、将响应从微服务发送给客户端等。
            4. ERROR:在其他阶段发生错误时执行该过滤器。

        ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:

    public abstract ZuulFilter implements IZuulFilter{
    
        abstract public String filterType();
    
        abstract public int filterOrder();
        
        boolean shouldFilter();// 来自IZuulFilter
    
        Object run() throws ZuulException;// IZuulFilter
    }
    View Code

          shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false不执行。
          run :过滤器的具体业务逻辑。
          filterType :返回字符串,代表过滤器的类型。包含以下4种:
            pre :请求在被路由之前执行
            routing :在路由请求时调用
            post :在routing和errror过滤器之后调用
            error :处理请求时发生错误调用
          filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

        过滤器执行生命周期:

          

          正常流程:

        • 请求到达首先会经过pre类型过滤器,而后到达route类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

          异常流程:

        • 整个过程中,pre或者route过滤器出现异常,都会直接进入error过滤器,在error处理完毕后,会将请求交给POST过滤器,最后返回给用户。

        • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,将最终结果返回给请求客户端。

        • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和route不同的是,请求不会再到达POST过滤器了。

        自定义过滤器:

          模拟一个登录的校验。基本逻辑:如果请求中有access-token参数,则认为请求有效,放行。

    /**
     * 自定义的zuul过滤器
     *  继承抽象父类
     */
    @Component
    public class LoginFilter extends ZuulFilter {
    
        /**
         * 定义过滤器类型
         *  pre
         *  routing
         *  post
         *  error
         */
        public String filterType() {
            return "pre";
        }
    
    
        /**
         *  指定过滤器的执行顺序
         *      返回值越小,执行顺序越高
         */
        public int filterOrder() {
            return 1;
        }
    
    
        /**
         * 当前过滤器是否生效
         *  true : 使用此过滤器
         *  flase : 不使用此过滤器
         */
        public boolean shouldFilter() {
            return true;
        }
    
    
        /**
         * 指定过滤器中的业务逻辑
         *  身份认证:
         *      1.所有的请求需要携带一个参数 : access-token
         *      2.获取request请求
         *      3.通过request获取参数access-token
         *      4.判断token是否为空
         *      4.1 token==null : 身份验证失败
         *      4.2 token!=null : 执行后续操作
         *  在zuul网关中,通过RequestContext的上下问对象,可以获取对象request对象
         */
        public Object run() throws ZuulException {
            //System.out.println("执行了过滤器");
            //1.获取zuul提供的上下文对象RequestContext
            RequestContext ctx = RequestContext.getCurrentContext();
            //2.从RequestContext中获取request
            HttpServletRequest request = ctx.getRequest();
            //3.获取请求参数access-token
            String token = request.getParameter("access-token");
            //4.判断
            if (token == null) {
                //4.1 如果token==null ,拦截请求,返回认证失败
                ctx.setSendZuulResponse(false); // 拦截请求
                // 响应状态码,401-身份未认证
                ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
            }
            //4.2 如果token!=null ,继续后续操作
            return null;
        }
    }
    View Code

            RequestContext :用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储在上下文中
          在自定义的过滤器类中需要加上@Component注解,启动测试 ........
            访问成功

             访问出错

  • 相关阅读:
    Codeforces 1005D:Polycarp and Div 3
    HURST 1116:选美大赛(LIS+路径输出)
    洛谷 P1164:小A点菜(DP/DFS)
    HDU 1159:Common Subsequence(LCS模板)
    51Nod 1007:正整数分组(01背包)
    bzoj3993 [SDOI2015]星际战争
    cogs1341 永无乡
    cogs1533 [HNOI2002]营业额统计
    cogs62 [HNOI2004] 宠物收养所
    cogs1439 货车运输
  • 原文地址:https://www.cnblogs.com/roadlandscape/p/12504598.html
Copyright © 2011-2022 走看看