一、为什么需要Zuul?

  Zuul 作为微服务系统的网关组件,用于构建边界服务( Edge Service ),致力于动态路由、过滤、监控、弹性伸缩和安全。Zuul 作为路由网关组件,在微服务架构中有着非常重要的作用,主要体现在以下6个方面。

  1)Zuul Ribbon 以及 Eureka 相结合,可以实现智能路由和负载均衡的功能, Zuul 能够将请求流量按某种策略分发到集群状态的多个服务实例。

  2)网关将所有服务的 API 接口统一聚合,并统一对外暴露。外界系统调用 API 接口时,都是由网关对外暴露的 API 接口,外界系统不需要知道微服务系统中各服务相互调用的复杂性。微服务系统 保护了其内部微服务单元的 API 接口 防止其被外界直接调用,导致服务的敏感信息对外暴露。

  3)网关服务可以做用户身份认证和权限认证,防止非法请求操作 API 接口,对服务器起到保护作用。

  4)网关可以实现监控功能,实时日志输出,对请求进行记录。

  5)网关可以用来实现流量监控。在高流量的情况下,对服务进行降级。

  6)API 接口从内部服务分离出来 方便做测试。

  二、Zuul的工作原理

  Zuul 是通过 Servlet 来实现的, Zuul 通过自定义的 Zuul Servlet (类似于 Spring MVC的DispatcServlet 〕来对请求进行控制。 Zuul 的核心是一系列过滤器,可以在 Http 请求的发起和响应返回期间执行一系列的过滤器。 Zuul 包括以下4种过滤器。  

  1)PRE 过滤器:它是在请求路由到具体的服务之前执行的,这种类型的过滤器可以做安全验证,例如身份验证、 参数验证等。

  2)ROUTING 过滤器:它用于将请求路由到具体的微服务 。在默认情况下,它使用Http Client 进行网络请求。

  3)POST 过滤器:它是在请求已被路由到微服务后执行的一般情况下,用作收集统计信息、指标,以及将响应传输到客户端。

  4)ERROR 过滤器:它是在其他过滤器发生错误时执行的。

  Zuul 采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通RequestContext 对象来共享数据,每个请求都会创建一个RequestContext 对象。Zuul 过滤器具有以下关键特性。

  1)Type (类型):Zuul 过滤器的类型,这个类型决定了过滤器在请求的哪个阶段起作用,例如 Pre Post 阶段等。

  2)Execution Order (执行顺序) :规定了过滤器的执行顺序, Order 的值越小,越先执行

  3)Criteria (标准):Filter 行所需的条件。

  4)Action (行动):如果符合执行条件,则执行 Action (即逻辑代码)。

  Zuul的请求生命周期:
  

  当一个客户端 Request 请求进入 Zuul网关服务时,网关先进入“pre filter ,进行一系列的验证、操作或者判断。然后交给“routing filter ”进行路由转发,转发到具体的服务实例进行逻辑处理、返回数据。当具体的服务处理完后,最后由“post filter进行处理,该类型的处理器处理完之后,将 Response 信息返回给客户端。

  ZuulServlet是Zuul的核心Servlet,ZuulServlet的作用是初始化ZuulFilter。并编排这些顺序,具体的逻辑在service()方法中

  1.    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  2. try {
  3. ...
  4.  
  5.        try {
  6. this.preRoute();
  7. } catch (ZuulException var12) {
  8. this.error(var12);
  9. this.postRoute();
  10. return;
  11. }
  12.  
  13. try {
  14. this.route();
  15. } catch (ZuulException var13) {
  16. this.error(var13);
  17. this.postRoute();
  18. return;
  19. }
  20.  
  21. try {
  22. this.postRoute();
  23. } catch (ZuulException var11) {
  24. this.error(var11);
  25. }
  26. } catch (Throwable var14) {
  27. this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
  28. } finally {
  29. RequestContext.getCurrentContext().unset();
  30. }
  31. }

  三、搭建具体的Zuul应用服务。

  1)加入依赖:

  1.      <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.cloud</groupId>
  7. <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-web</artifactId>
  12. </dependency>

  2)编写启动程序加上@EnableZuulProxy注解。

  1. package com.cetc;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
  6. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
  7.  
  8. @SpringBootApplication
  9. @EnableEurekaClient
  10. @EnableZuulProxy
  11. public class ZuulApplication {
  12. public static void main(String[] args) {
  13. SpringApplication.run(ZuulApplication.class, args);
  14. }
  15. }

  3)加入配置(服务端口为8682):

  1. server:
  2. port: 8682
  3. spring:
  4. application:
  5. name: zuul
  6. eureka:
  7. client:
  8. service-url:
  9. defaultZone: http://127.0.0.1:8670/eureka/ # 实际开发中建议使用域名的方式
  10. zuul:
  11. routes:
  12. test:
  13. path: /test/**
  14. url: http://127.0.0.1:8673/
  15. ribbon:
  16. path: /ribbon/**
  17. serviceId: rest
  18. feign:
  19. path: /feign/**
  20. serviceId: feign

  说明:上面的为基本配置:

  zuul.routes.test/ribbon/feign:中的test/ribbon/feign为自定义配置。

  path:为Zuul中代理的路径。

  serviceId:为服务的名称。

  url:为具体的地址,不存在负载均衡的问题。(主要用于外部服务加入)

  4)测试:启动项目包含Eureka-Server, 2个Eureka-Client,Ribbon中的Rest,Feign和Zuul。端口分别为:8670、8673/8674、8675、8676、8682。

  

  效果如下:

  

  测试接口为:

  http://127.0.0.1:8682/test/api/test/getPort

  http://127.0.0.1:8682/ribbon/api/ribbon/getPort

  http://127.0.0.1:8682/feign/api/feign/getPort

  可见这里的访问:使用url的方式默认没有负载均衡的,所以不建议使用,使用ribbon和feign两种方式都是可以进行负载均衡的。

  5)如果想使用url的方式来做负载均衡那么就要自己维护访问列表。配置如下:

  1. zuul:
  2. routes:
  3. test-r:
  4. path: /test-r/**
  5. serviceId: test-r
  6. test-r:
  7. ribbon:
  8. listOfServers: http://127.0.0.1:8673/, http://127.0.0.1:8674/
  9. ribbon:
  10. eureka:
  11. enabled: false

  说明:这里禁用ribbon不影响前面配置好的服务。但是会影响Zuul调用ribbon的rest或者feign服务,如果这两个服务存在负载均衡,那么调用的时候就存在负载均衡问题。所以一般建议不要这样使用

  测试:

  

  6)如果想加入具体的版本号,可以加入如下配置:

  1. zuul:
  2. prefix: /v1

  访问方式为:http://127.0.0.1:8682/v1/test/api/test/getPort。在链接中加入版本号。

  四、Zuul加入熔断器:

  默认实现FallbackProvider接口,加入容器即可

  1. package org.springframework.cloud.netflix.zuul.filters.route;
  2.  
  3. import org.springframework.http.client.ClientHttpResponse;
  4.  
  5. public interface FallbackProvider {
  6. String getRoute();
  7.  
  8. ClientHttpResponse fallbackResponse(String var1, Throwable var2);
  9. }

  实现为:

  1. package com.cetc.config;
  2.  
  3. import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
  4. import org.springframework.http.HttpHeaders;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.MediaType;
  7. import org.springframework.http.client.ClientHttpResponse;
  8. import org.springframework.stereotype.Component;
  9.  
  10. import java.io.ByteArrayInputStream;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13.  
  14. @Component
  15. public class ZuulFallbackProvider implements FallbackProvider {
  16.  
  17. @Override
  18. public String getRoute() {
  19. return "*";
  20. }
  21.  
  22. @Override
  23. public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
  24. return new ClientHttpResponse() {
  25. @Override
  26. public HttpStatus getStatusCode() throws IOException {
  27. return HttpStatus.OK;
  28. }
  29.  
  30. @Override
  31. public int getRawStatusCode() throws IOException {
  32. return 200;
  33. }
  34.  
  35. @Override
  36. public String getStatusText() throws IOException {
  37. return "OK";
  38. }
  39.  
  40. @Override
  41. public void close() {
  42.  
  43. }
  44.  
  45. @Override
  46. public InputStream getBody() throws IOException {
  47. return new ByteArrayInputStream("error".getBytes());
  48. }
  49.  
  50. @Override
  51. public HttpHeaders getHeaders() {
  52. HttpHeaders httpHeaders = new HttpHeaders();
  53. httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
  54. return httpHeaders;
  55. }
  56. };
  57. }
  58. }

  测试:关闭feign测试接口为:

  

  五、在Zuul中自定义filter:

  1. package com.cetc.filter;
  2.  
  3. import com.netflix.zuul.ZuulFilter;
  4. import com.netflix.zuul.context.RequestContext;
  5. import com.netflix.zuul.exception.ZuulException;
  6. import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.http.HttpStatus;
  7. import org.springframework.stereotype.Component;
  8. import org.springframework.util.StringUtils;
  9.  
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.io.IOException;
  13.  
  14. @Component
  15. public class CustomZuulFilter extends ZuulFilter {
  16. @Override
  17. public String filterType() {
  18. return FilterConstants.PRE_TYPE;
  19. }
  20.  
  21. @Override
  22. public int filterOrder() {
  23. return 0;
  24. }
  25.  
  26. @Override
  27. public boolean shouldFilter() {
  28. //是否开启过滤逻辑,开启后运行run()方法
  29. return true;
  30. }
  31.  
  32. @Override
  33. public Object run() throws ZuulException {
  34. RequestContext requestContext = RequestContext.getCurrentContext();
  35. HttpServletRequest request = requestContext.getRequest();
  36. String token = request.getParameter("token");
  37. if (StringUtils.isEmpty(token)) {
  38. HttpServletResponse response = requestContext.getResponse();
  39. requestContext.setSendZuulResponse(false);
  40. requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
  41. try {
  42. response.getWriter().write("token is empty");
  43. } catch (IOException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. return null;
  48. }
  49. }

  测试:

  

  是不是感觉这类有点验证的味道了。通过自定义的过滤就能达到请求过滤的目的。实现过程如下:

  

   六、Zuul的常见使用方式。

  Zuul 是采用了类似于 Spring MVC DispatchServlet 来实现的,采用的是异步阻塞模型,所以性能比 Ngnix 差。由于 Zuul和其他 Netflix 组件可以相互配合、无缝集成 Zuul 很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数情况下 Zuul 都是以集群的形式在的。由于Zuul的横向扩展能力非常好,所以当负载过高时,可以通过添加实例来解决性能瓶颈

  1)一种常见的使用方式是对不同的渠道使用不同的 Zuul 来进行路由,例如移动端共用Zuul一个网关实例。Web 端用另一个Zuul 网关实例,其他的客户端用另外一个Zuul 实例进行路由。

  

  2)另外一种常见的集群是通过 Ngnix和Zuul 相互结合来做负载均衡。暴露在最外面的是Ngnix 主从双热备进行 Keepalive, Ngnix 经过某种路由策略,将请求路由转发到 Zuul 集群上,Zuul 最终将请求分发到具体的服务上。

  

  七、源码地址:https://github.com/lilin409546297/spring-cloud/tree/master/zuul

Spring-Cloud之Zuul路由网关-6的更多相关文章

  1. spring cloud 学习之路由网关(zuul)

    学习自方志朋的博客 http://blog.csdn.net/forezp/article/details/69939114 在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现.服务消费. ...

  2. zuul路由网关集成ssl,实现http到https的转变

    1 前言 最近几天刚开始接触微信小程序的开发,才接触到了https的概念(微信小程序中的请求必须为https请求,不然请求无法成功).https算是对http的安全封装,在http的基础上加了ssl证 ...

  3. spring cloud 2.x版本 Zuul路由网关教程

    前言 本文采用Spring cloud本文为2.1.8RELEASE,version=Greenwich.SR3 本文基于前两篇文章eureka-server.eureka-client.eureka ...

  4. Spring Cloud Alibaba | Nacos动态网关路由

    Spring Cloud Alibaba | Gateway基于Nacos动态网关路由 本篇实战所使用Spring有关版本: SpringBoot:2.1.7.RELEASE Spring Cloud ...

  5. spring cloud 通过zuul网关去请求的时候报404的几个原因。

    spring cloud 中 zuul 网关的那些坑: 1.检查你的服务是否正常启动. 2.检查你的服务是否正常注册到注册中心. 3.zuul网关的路由规则是会把你注册在注册中心的serviceId ...

  6. 【七】zuul路由网关

    一.zuul是什么?zuul 包含以下两个最主要的功能:1.路由功能: 负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础.2.过滤器功能: 则负责对请求的处理过程进行干预,是实现请 ...

  7. SpringCloud 进阶之Zuul(路由网关)

    1. Zuul(路由网关) Zuul 包含了对请求的路由和过滤两个最主要的功能; 路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础; 过滤功能:负责对请求的处理过程进行干 ...

  8. SpringCloud学习系列之七 ----- Zuul路由网关的过滤器和异常处理

    前言 在上篇中介绍了SpringCloud Zuul路由网关的基本使用版本,本篇则介绍基于SpringCloud(基于SpringBoot2.x,.SpringCloud Finchley版)中的路由 ...

  9. SpringCloud的入门学习之概念理解、Zuul路由网关

    1.Zuul路由网关是什么? 答:Zuul包含了对请求的路由和过滤两个最主要的功能,其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进 ...

  10. SpringCloud学习笔记(八):Zuul路由网关

    概述 是什么? Zuul包含了对请求的路由和过滤两个最主要的功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤器功能则负责对请求的处理过程进行干预,是实现请 ...

随机推荐

  1. [技术博客]Android 开发 Bug Log

    [技术博客] Android 开发 Bug Log 大大小小的bug,聪明的愚蠢的都有, 持续记录中...... java.lang.IllegalArgumentException: The sty ...

  2. Dubbo自定义Filter统一处理异常

    Dubbo版本:2.7 使用自定义Filter注意事项 1.自定义名称不能和默认Filter相同,否则可能不生效 2.只用定义Filter类和META-INF下的文本文件,不用添加配置,@Activa ...

  3. Matlab Script to pre-process UAV123 tracking dataset

    Matlab Script to pre-process UAV123 tracking dataset 2019-11-08 09:43:11 Official project page: http ...

  4. SQLite R*Tree 模块测试

    目录 SQLite R*Tree 模块测试 1.SQLite R*Tree 模块特性简介 2.SQLite R*Tree 模块简单测试代码 SQLite R*Tree 模块测试 相关参考: MySQL ...

  5. 【Python】解析Python中类的使用

    目录结构: contents structure [-] 类的基本使用 专有方法 继承 单重继承 多重继承 砖石继承 1.类的基本使用 下面是类使用的一个简单案例, class person: &qu ...

  6. Java地址:

    GitHub:https://github.com/nanchen2251 个人博客:https://nanchen2251.github.io/ 简书地址:http://www.jianshu.co ...

  7. 如何在nginx下实现访问web网站密码认证保护的功能

    在某些特定的环境下,我们希望nginx下的web站点在访问时需要用户输入账户密码才能访问.以便拒绝那些不速之客. 其实,配置起来也很简单,按照下面的步骤即可实现. 一.编辑虚拟主机配置文件. serv ...

  8. promise 和 async await比较

    async搭配await是ES7提出的,它的实现是基于Promise.这里使用它对比Promise的用法,这里只是简单的适合日常业务的使用场景.   async.await是ES7中的提案,通过同步方 ...

  9. 解决pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your path问题

    解决方案: 找到python的安装路径下的pytesseract:   例如我的是  C:\develop\Python\Lib\site-packages\pytesseract .用文本编辑器打开 ...

  10. 梳理数据库(MySQL)的主要知识点

    一.数据库类型 常用的关系型数据库 Oracle:功能强大,主要缺点就是贵 MySQL:互联网行业中最流行的数据库,免费.关系数据库场景中的功能 MySQL 都能很好的满足 MariaDB:MySQL ...