Zuul作为微服务系统的网关组件,用于构建边界服务,致力于动态路由、过滤、监控、弹性伸缩和安全。

为什么需要Zuul

Zuul、Ribbon以及Eureka结合可以实现智能路由和负载均衡的功能;网关将所有服务的API接口统一聚合,统一对外暴露。外界调用API接口时,不需要知道微服务系统中各服务相互调用的复杂性,保护了内部微服务单元的API接口;网关可以做用户身份认证和权限认证,防止非法请求操作API接口;网关可以实现监控功能,实时日志输出,对请求进行记录;网关可以实现流量监控,在高流量的情况下,对服务降级;API接口从内部服务分离出来,方便做测试。

Zuul通过Servlet来实现,通过自定义的ZuulServlet来对请求进行控制。核心是一系列过滤器,可以在Http请求的发起和响应返回期间执行一系列过滤器。Zuul采取了动态读取、编译和运行这些过滤器。过滤器之间不能直接通信,而是通过RequestContext对象来共享数据,每个请求都会创建一个RequestContext对象。

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

搭建Zuul服务

需要配置好eureka,ribbon,可参考:

spring boot 2.0.3+spring cloud (Finchley)1、搭建Eureka 以及构建高可用Eureka Server集群

spring boot 2.0.3+spring cloud (Finchley)2、搭建负载均衡Ribbon (Eureka+Ribbon+RestTemplate)

spring boot 2.0.3+spring cloud (Finchley)3、声明式调用Feign

新建工程eureka-zuul-client,pom文件引入相关依赖,包括主maven工程的pom文件,eureka client起步依赖,Zuul的起步依赖,web的起步依赖(已在主maven工程pom中配置)。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5.  
  6. <groupId>com.cralor</groupId>
  7. <artifactId>eureka-zuul-client</artifactId>
  8. <version>0.0.1-SNAPSHOT</version>
  9. <packaging>jar</packaging>
  10.  
  11. <name>eureka-zuul-client</name>
  12. <description>Demo project for Spring Boot</description>
  13.  
  14. <parent>
  15. <groupId>com.cralor</groupId>
  16. <artifactId>chap9-zuul</artifactId>
  17. <version>0.0.1-SNAPSHOT</version>
  18. <relativePath/> <!-- lookup parent from repository -->
  19. </parent>
  20.  
  21. <properties>
  22. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  23. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  24. <java.version>1.8</java.version>
  25. </properties>
  26. <dependencies>
  27. <dependency>
  28. <groupId>org.springframework.cloud</groupId>
  29. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework.cloud</groupId>
  33. <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.springframework.boot</groupId>
  37. <artifactId>spring-boot-starter-test</artifactId>
  38. <scope>test</scope>
  39. </dependency>
  40. </dependencies>
  41. <build>
  42. <plugins>
  43. <plugin>
  44. <groupId>org.springframework.boot</groupId>
  45. <artifactId>spring-boot-maven-plugin</artifactId>
  46. </plugin>
  47. </plugins>
  48. </build>
  49.  
  50. </project>

在启动类加上注解@EnableZuulProxy

  1. @EnableZuulProxy
  2. @SpringBootApplication
  3. public class EurekaZuulClientApplication {
  4.  
  5. public static void main(String[] args) {
  6. SpringApplication.run(EurekaZuulClientApplication.class, args);
  7. }
  8. }

在配置文件做相关配置,包括端口号5000,服务注册中心地址http://localhost:8761/eureka/,程序名service-zuul,其中zuul路由配置:zuul.routes.hiapi为”/hiapi/**“,zuul.routes.serviceId为”eureka-client“,这两个配置就可以将以”/hiapi“开头的Url路由到eureka-client服务,zuul.routes.hiapi中的”hiapi“是自己定义的需要指定它的path和serviceId,两者配合使用,就可以将指定类型的请求Url路由到指定的serviceId。同理,满足”/ribbonapi“开头的请求Url都会被分发到eureka-ribbon-client,满足”/feignapi“开头的请求Url都会被分发到eureka-feign-client服务。如果服务存在多个实例,zuul会结合ribbon做负载均衡。

  1. server:
  2. port: 5000
  3. spring:
  4. application:
  5. name: service-zuul
  6.  
  7. eureka:
  8. client:
  9. serviceUrl:
  10. defaultZone: http://localhost:8761/eureka/
  11.  
  12. zuul:
  13. routes:
  14. hiapi:
  15. path: /hiapi/**
  16. serviceId: eureka-client
  17. ribbonapi:
  18. path: /ribbonapi/**
  19. serviceId: eureka-ribbon-client
  20. feignapi:
  21. path: /feignapi/**
  22. serviceId: eureka-feign-client

依次启动工程eureka-server、eureka-client(启动两个实例端口为8762、8763)、eureka-ribbon-client、eureka-feign-client和eureka-zuul-client。浏览器多次访问http://localhost:5000/hiapi/hi?name=cralor,会交替显示

   

可见zuul在路由转发做了负载均衡。同理多次访问http://localhost:5000/ribbonapi/hi?name=cralor和http://localhost:5000/feignapi/hi?name=cralor也可以看到类似内容。

 在Zuul上配置API接口的版本号

如果想给每个服务的API接口加前缀,可使用zuul.prefix配置,例如http://localhost:5000/v1/hiapi/hi?name=cralor,即在所有的API接口上加一个v1作为版本号。

  1. zuul:
  2. prefix: /v1
  3. routes:
  4. hiapi:
  5. path: /hiapi/**
  6. serviceId: eureka-client
  7. ribbonapi:
  8. path: /ribbonapi/**
  9. serviceId: eureka-ribbon-client
  10. feignapi:
  11. path: /feignapi/**
  12. serviceId: eureka-feign-client

重启eureka-zuul-client,访问http://localhost:5000/v1/hiapi/hi?name=cralor,结果如上。

在Zuul上配置熔断器

Zuul作为Netflix组件,可以与Ribbon、Eureka和Hystrix等组件相结合,实现负载均衡、熔断器的功能。默认情况下Zuul和Ribbon相结合,实现了负载均衡。实现熔断器功能需要实现FallbackProvider接口,实现该接口的两个方法,一个是getRoute(),用于指定熔断器功能应用于哪些路由的服务;另一个方法fallbackResponse()为进入熔断器功能时执行的逻辑。

  1. @Component
  2. public class MyFallbackProvider implements FallbackProvider {
  3. @Override
  4. public String getRoute() {
  5. return "eureka-client";
  6. }
  7.  
  8. @Override
  9. public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
  10. System.out.println("route:"+route);
  11. System.out.println("exception:"+cause.getMessage());
  12. return new ClientHttpResponse() {
  13. @Override
  14. public HttpStatus getStatusCode() throws IOException {
  15. return HttpStatus.OK;
  16. }
  17.  
  18. @Override
  19. public int getRawStatusCode() throws IOException {
  20. return 200;
  21. }
  22.  
  23. @Override
  24. public String getStatusText() throws IOException {
  25. return "ok";
  26. }
  27.  
  28. @Override
  29. public void close() {
  30.  
  31. }
  32.  
  33. @Override
  34. public InputStream getBody() throws IOException {
  35. return new ByteArrayInputStream("oooops!error,i'm the fallback.".getBytes());
  36. }
  37.  
  38. @Override
  39. public HttpHeaders getHeaders() {
  40. HttpHeaders headers = new HttpHeaders();
  41. headers.setContentType(MediaType.APPLICATION_JSON);
  42. return headers;
  43. }
  44. };
  45. }
  46. }

重启eureka-zuul-client,关闭eureka-client的所有实例,访问http://localhost:5000/v1/hiapi/hi?name=cralor,浏览器显示

如果需要所有的路由服务都加熔断功能,需要在getRoute()方法上返回”*“的匹配符

  1. @Override
  2. public String getRoute() {
  3. return "*";
  4. }

在Zuul使用过滤器

Zuul包括以下4中过滤器

PRE过滤器:是在请求路由到具体服务之前执行的,可以做安全验证,如身份验证,参数验证。

ROUTING过滤器:它用于将请求 路由到具体的微服务实例。默认使用Http Client进行网络请求。

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

ERROR过滤器:在其他过滤器发生错误时执行。

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.<SimpleClassName>.<filterType>.disable=true
比如要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter就设置

  1. zuul:
  2. SendResponseFilter:
  3. post:
  4. disable: true

实现自定义滤器需要继承ZuulFilter,实现ZuulFilter中的抽象方法,包括filterType(),filterOrder()以及IZuulFilter的shouldFilter()和run()方法。filterType()为过滤器类型,有4中类型:pre、post、routing和error。filterOrder()是过滤顺序,它为一个int类型的值,值越小,越早执行该过滤器。shouldFilter()表示是否需要执行该过滤器逻辑,true表示执行,false表示不执行,如果true则执行run()方法。run()方法是具体的过滤的逻辑。本例中检查请求的参数中是否传了token这个参数,如果没有传,则请求不被路由到具体的服务实例,直接返回 响应,状态码为401。

  1. @Component
  2. public class MyFilter extends ZuulFilter {
  3.  
  4. private static Logger log=LoggerFactory.getLogger(MyFilter.class);
  5.  
  6. @Override
  7. public String filterType() {
  8. return "pre"; //定义filter的类型,有pre、route、post、error四种
  9. }
  10.  
  11. @Override
  12. public int filterOrder() {
  13. return 0;//定义filter的顺序,数字越小表示顺序越高,越先执行
  14. }
  15.  
  16. @Override
  17. public boolean shouldFilter() {
  18. return true;//表示是否需要执行该filter,true表示执行,false表示不执行
  19. }
  20.  
  21. @Override
  22. public Object run() throws ZuulException {
  23. //filter需要执行的具体操作
  24. RequestContext ctx = RequestContext.getCurrentContext();
  25. HttpServletRequest request = ctx.getRequest();
  26. String token = request.getParameter("token");
  27. System.out.println(token);
  28. if(token==null){
  29. log.warn("token is empty");
  30. ctx.setSendZuulResponse(false);
  31. ctx.setResponseStatusCode(401);
  32. try {
  33. ctx.getResponse().getWriter().write("token is empty");
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. return null;
  38. }
  39. log.info("ok");
  40. return null;
  41. }
  42. }

重启服务,访问http://localhost:5000/v1/hiapi/hi?name=cralor,显示

地址栏输入http://localhost:5000/v1/hiapi/hi?name=cralor&token=ccc,显示

可见,MyFilter这个Bean注入IOC容器后,对请求进行了过滤,并在请求路由转发之前进行了逻辑判断。

案例代码地址:https://github.com/cralor7/springcloud

spring boot 2.0.3+spring cloud (Finchley)5、路由网关Spring Cloud Zuul的更多相关文章

  1. Quick Guide to Microservices with Spring Boot 2.0, Eureka and Spring Cloud

    https://piotrminkowski.wordpress.com/2018/04/26/quick-guide-to-microservices-with-spring-boot-2-0-eu ...

  2. Spring Boot 2.0正式发布,新特性解读

    作者|翟永超 Spring Boot 2.0 来啦,有哪些新特性?升级吗? 写在前面 北京时间 3 月 1 日,经过漫长的等待之后,Spring Boot 2.0 正式发布.作为 Spring 生态中 ...

  3. 重磅:Spring Boot 2.0 正式发布!

    Spring Boot 2.0 正式发布! 2018/03/01最新消息,传得沸沸扬扬的Spring Boot 2.0 正式发布了. 小编去看了下Spring Boot的官网,正式版本已经释放出来了! ...

  4. Spring Boot 2.0 返回JSP页面实战

    1. 模板引擎JSP的限制 在开始之前呢,我觉得我们有必要先去了解下 Spring Boot 2.0 官方文档中提到的如下内容: 模板引擎 除了REST Web服务之外,还可以使用Spring MVC ...

  5. Spring Boot 2.0系列文章(五):Spring Boot 2.0 项目源码结构预览

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/15/springboot2_code/ 项目结构 结构分析: Spring-boot-pr ...

  6. Spring Boot 2.0系列文章(七):SpringApplication 深入探索

    关注我 转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/04/30/springboot_SpringApplication/ 前言 在 Spring B ...

  7. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  8. Spring Boot 2.0 迁移指南

    ![img](https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6Rs7yEJ6ItV43JZMS7AJWoMSZtxicnG0iaE0AvpUHI8oM7lxz1rRs ...

  9. Spring 官宣发布 Spring Boot 3.0 第一个里程碑 M1,从 Java 8 提升到 Java 17!

    Spring官方于2022年1月20日发布Spring Boot 3.0.0-M1版本,预示开启了Spring Boot 3.0的里程碑,相信这是通往下一代Spring框架的激动人心的旅程. 接下来一 ...

  10. spring boot 2.0.3+spring cloud (Finchley)3、声明式调用Feign

    Feign受Retrofix.JAXRS-2.0和WebSocket影响,采用了声明式API接口的风格,将Java Http客户端绑定到他的内部.Feign的首要目标是将Java Http客户端调用过 ...

随机推荐

  1. Scrum立会报告+燃尽图(十月二十一日总第十二次)

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2246 项目地址:https://git.coding.net/zhang ...

  2. 软件工程第九周psp

    1.PSP表格 2.进度条 3.饼状图 4.折线图

  3. Codeforces Round #312 (Div. 2) E. A Simple Task 线段树+计数排序

    题目链接: http://codeforces.com/problemset/problem/558/E E. A Simple Task time limit per test5 secondsme ...

  4. alpha冲6

    队名:日不落战队 安琪(队长) 今天完成的任务 回收站前端界面. 明天的计划 查看个人信息界面. 还剩下的任务 信息修改前端界面. 设置界面. 遇到的困难 模拟机莫名其妙就崩了,调试了很久,后在队友的 ...

  5. C#高级编程 (第六版) 学习 第三章:对象和类型

    第三章 对象和类型 1,类和结构 类存储在托管堆上 结构存储在堆栈上   2,类成员 类中的数据和函数称为类成员 数据成员 数据成员包括了字段.常量和事件   函数成员 方法:与某个类相关的函数,可以 ...

  6. Node.js系列——(1)安装配置与基本使用

    1.安装 进入下载地址 小编下载的是msi文件,下一步下一步傻瓜式安装. 打印个hello看看: 2.REPL 全称Read Eval Print Loop,即交互式解释器,可以执行读取.执行.打印. ...

  7. 第83天:jQuery中操作form表单

    操作form表单 1. 属性操作 设置属性: // 第一个参数表示:要设置的属性名称 // 第二个参数表示:该属性名称对应的值 $(selector).attr(“title”, “传智播客”); 获 ...

  8. BZOJ 1565 植物大战僵尸(拓扑排序+最大权闭合子图)

    图中的保护关系就类似于最大权闭合子图.即你想杀x,你就一定要杀掉保护x的点,那么把x向保护它的点连边.那么题目就转化成了最大权闭合子图的问题. 但是这个图有点特殊啊... 考虑有环的情况,显然这个环以 ...

  9. git commad

    repo forall -c 'echo $REPO_PATH;ssh trd.git.htc.com -p29419 -lowen_wen gerrit create-project --submi ...

  10. 【HLSDK系列】怎么增加一种新实体

    你平常肯定接触到很多比如 info_player_start hostage info_target 之类的实体,这里就解释一下怎么创建一种新的实体. 首先建立一个新的 .h 文件(当然你写在现有的文 ...