zoukankan      html  css  js  c++  java
  • 八、springcloud之服务网关zuul(一)

    一、Zuul简介

    •   zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用。
    •   Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。
    •   Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。
    •   zuul的例子可以参考 netflix 在github上的 simple webapp,可以按照netflix 在github wiki 上文档说明来进行使用。
    •   Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。
    •   Spring Cloud Zuu默认和Ribbon结合实现了负载均衡的功能。

    二、zuul的工作原理

    zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。

    zuul把Request route到 用户处理逻辑 的过程中,这些filter参与一些过滤处理,比如Authentication,Load Shedding等。  

      com.netflix.zuul.http.ZuulServlet是ZuulFilter链执行的入口,通过service方法,提取请求到RequestContext,然后通过调用ZuulRunner,依次按顺序执行每种类型的Filter,完成整个Filter的生命周期,架构图如下所示。

      在zuul中, 整个请求的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的zuulfilter共享。zuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的zuulfilter的管理器FilterProcessor从filterloader 中获取zuulfilter,而zuulfilter是被filterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,zuulservelet首先执行的Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。 

      执行流程图

      

      Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期

        (1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

        (2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。

        (3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

        (4) ERROR:在其他阶段发生错误时执行该过滤器。

      功能:

    • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
    • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
    • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
    • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
    • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
    • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
    • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

      ZuulServlet - 处理请求(调度不同阶段的filters,处理异常等) 

    • ZuulServlet类似SpringMvc的DispatcherServlet,所有的Request都要经过ZuulServlet的处理
    • 三个核心的方法preRoute(),route()postRoute(),zuul对request处理逻辑都在这三个方法里
    • ZuulServlet交给ZuulRunner去执行。
    • 由于ZuulServlet是单例,因此ZuulRunner也仅有一个实例。
    • ZuulRunner直接将执行逻辑交由FilterProcessor处理,FilterProcessor也是单例,其功能就是依据filterType执行filter的处理逻辑
    • FilterProcessor对filter的处理逻辑。
    1. 首先根据Type获取所有输入该Type的filter,List<ZuulFilter> list
    2. 遍历该list,执行每个filter的处理逻辑,processZuulFilter(ZuulFilter filter)
    3. RequestContext对每个filter的执行状况进行记录,应该留意,此处的执行状态主要包括其执行时间、以及执行成功或者失败,如果执行失败则对异常封装后抛出。 
    4. 到目前为止,zuul框架对每个filter的执行结果都没有太多的处理,它没有把上一filter的执行结果交由下一个将要执行的filter,仅仅是记录执行状态,如果执行失败抛出异常并终止执行。

      zuul的原理及生命周期深入解析:https://www.cnblogs.com/lexiaofei/p/7080257.html

      源码解析:

        https://blog.csdn.net/forezp/article/details/76211680(可以增加做日志处理)

        http://xujin.org/sc/sc-zuul-s2/

    三、zuul入门使用

      1、添加依赖

      引入spring-cloud-starter-zuul

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>

      2、配置文件

      应用名、服务端口等

    spring.application.name=gateway-service-zuul
    server.port=8888
    
    #路由
    #这里的配置表示,访问/it/** 直接重定向到http://www.baidu.com/**
    zuul.routes.api-a-url.path=/it/**
    zuul.routes.api-a-url.url=http://www.baidu.com/

        服务路由:

          在Zuul中提供了两种映射方式:

            方式一:通过url直接映射,上面配置就是

              api-a-url部分为路由的名字,可以任意定义,但是一组映射关系的path和url要相同

            方式二:服务化的方式(使用serviceId的映射方式)(推荐)

              通过url映射的方式对于Zuul来说,并不是特别友好,Zuul需要知道我们所有为服务的地址,才能完成所有的映射配置。而实际上,我们在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,我们就可以实现对serviceId的映射

            需要添加spring-cloud-starter-eureka依赖

            修改配置:

    zuul.routes.api-a.path=/api-a/**
    zuul.routes.api-a.serviceId=service-A
    
    zuul.routes.api-b.path=/api-b/**
    zuul.routes.api-b.serviceId=service-B
    
    eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

            针对我们在准备工作中实现的两个微服务service-A和service-B,定义了两个路由api-a和api-b来分别映射。另外为了让Zuul能发现service-A和service-B,也加入了eureka的配置。

        这两种方式推荐使用serviceId的映射方式,除了对Zuul维护上更加友好之外,serviceId映射方式还支持了断路器,对于服务故障的情况下,可以有效的防止故障蔓延到服务网关上而影响整个系统的对外服务

      3、启动类

      启动类添加@EnableZuulProxy,支持网关路由。

    @SpringCloudApplication
    @EnableZuulProxy
    public class GatewayServiceZuulApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(GatewayServiceZuulApplication.class, args);
        }
    }

      注:1、这里用了@SpringCloudApplication注解,之前没有提过,通过源码我们看到,它整合了@SpringBootApplication@EnableDiscoveryClient@EnableCircuitBreaker,主要目的还是简化配置。

        @EnableCircuitBreaker:启动断路器

        2、@EnableZuulProxy简单理解为@EnableZuulServer的增强版,当Zuul与Eureka、Ribbon等组件配合使用时,我们使用@EnableZuulProxy。 

      4、服务过滤

      一、在服务网关中定义过滤器只需要继承ZuulFilter抽象类实现其定义的四个抽象函数就可对请求进行拦截与过滤。

    public class AccessFilter extends ZuulFilter  {
    
        private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
    
        @Override
        public String filterType() {
            return "pre";
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() {
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
    
            log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
    
            Object accessToken = request.getParameter("accessToken");
            if(accessToken == null) {
                log.warn("access token is empty");
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(401);
                return null;
            }
            log.info("access token ok");
            return null;
        }
    
    }
    View Code
    1. filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下:
      • pre:可以在请求被路由之前调用
      • routing:在路由请求时候被调用
      • post:在routing和error过滤器之后被调用
      • error:处理请求时发生错误时被调用
    2. filterOrder:通过int值来定义过滤器的执行顺序
    3. shouldFilter:返回一个boolean类型来判断该过滤器是否要执行,所以通过此函数可实现过滤器的开关。在上例中,我们直接返回true,所以该过滤器总是生效。
    4. run:过滤器的具体逻辑。需要注意,这里我们通过ctx.setSendZuulResponse(false)令zuul过滤该请求,不对其进行路由,然后通过ctx.setResponseStatusCode(401)设置了其返回的错误码,当然我们也可以进一步优化我们的返回,比如,通过ctx.setResponseBody(body)对返回body内容进行编辑等。

      Zuul中默认实现的Filter

        

    类型顺序过滤器功能
    pre -3 ServletDetectionFilter 标记处理Servlet的类型
    pre -2 Servlet30WrapperFilter 包装HttpServletRequest请求
    pre -1 FormBodyWrapperFilter 包装请求体
    route 1 DebugFilter 标记调试标志
    route 5 PreDecorationFilter 处理请求上下文供后续使用
    route 10 RibbonRoutingFilter serviceId请求转发
    route 100 SimpleHostRoutingFilter url请求转发
    route 500 SendForwardFilter forward请求转发
    post 0 SendErrorFilter 处理有错误的请求响应
    post 1000 SendResponseFilter 处理正常的请求响应

      禁用指定的Filter

      可以在application.yml中配置需要禁用的filter,格式:

    zuul:
        FormBodyWrapperFilter:
            pre:
                disable: true

      二、在实现了自定义过滤器之后,还需要实例化该过滤器才能生效,我们只需要在应用主类中或者在配置类中配置:

    @Configuration
    public class MyZuulFilterConfig {
        @Bean
        public AccessFilter accessFilter() {
            return new AccessFilter();
        }
    }

      启动该服务网关后,访问:

    • http://localhost:5555/api-a/add?a=1&b=2:返回401错误
    • http://localhost:5555/api-a/add?a=1&b=2&accessToken=token:正确路由到server-A,并返回计算内容

    四、服务网关的重要性

    • 不仅仅实现了路由功能来屏蔽诸多服务细节,更实现了服务级别、均衡负载的路由。
    • 实现了接口权限校验与微服务业务逻辑的解耦。通过服务网关中的过滤器,在各生命周期中去校验请求的内容,将原本在对外服务层做的校验前移,保证了微服务的无状态性,同时降低了微服务的测试难度,让服务本身更集中关注业务逻辑的处理。
    • 实现了断路器,不会因为具体微服务的故障而导致服务网关的阻塞,依然可以对外服务。

    参考:http://blog.didispace.com/springcloud5/

       http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html

  • 相关阅读:
    幂等性
    接口开发需要考虑内容
    接口安全保护策略
    高并发之API接口限流
    Windows 下通过DOS命令获取指定文件夹下所有文件的全路径
    spring cloud 版本号与 boot版本之间的对应关系(版本不对,会导致pom无法引入)
    springCloud全家桶
    map的循环删除操作
    Java hashCode() 和 equals()的若干问题解答
    SpringMVC工作流程
  • 原文地址:https://www.cnblogs.com/soul-wonder/p/9242746.html
Copyright © 2011-2022 走看看