一、为什么需要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()方法中

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
...        try {
this.preRoute();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
} try {
this.route();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
} try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}

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

  1)加入依赖:

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

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

package com.cetc;

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
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}

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

server:
port: 8682
spring:
application:
name: zuul
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8670/eureka/ # 实际开发中建议使用域名的方式
zuul:
routes:

test:
path: /test/**
url: http://127.0.0.1:8673/
ribbon:
path: /ribbon/**
serviceId: rest
feign:
path: /feign/**
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的方式来做负载均衡那么就要自己维护访问列表。配置如下:

zuul:
routes:
test-r:
path: /test-r/**
serviceId: test-r
test-r:
ribbon:
listOfServers:
http://127.0.0.1:8673/, http://127.0.0.1:8674/
ribbon:
eureka:
enabled:
false

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

  测试:

  

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

zuul:
prefix:
/v1

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

  四、Zuul加入熔断器:

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

package org.springframework.cloud.netflix.zuul.filters.route;

import org.springframework.http.client.ClientHttpResponse;

public interface FallbackProvider {
String getRoute(); ClientHttpResponse fallbackResponse(String var1, Throwable var2);
}

  实现为:

package com.cetc.config;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream; @Component
public class ZuulFallbackProvider implements FallbackProvider { @Override
public String getRoute() {
return "*";
} @Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
} @Override
public int getRawStatusCode() throws IOException {
return 200;
} @Override
public String getStatusText() throws IOException {
return "OK";
} @Override
public void close() { } @Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("error".getBytes());
} @Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
return httpHeaders;
}
};
}
}

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

  

  五、在Zuul中自定义filter:

package com.cetc.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; @Component
public class CustomZuulFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
} @Override
public int filterOrder() {
return 0;
} @Override
public boolean shouldFilter() {
//是否开启过滤逻辑,开启后运行run()方法
return true
;
} @Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
HttpServletResponse response = requestContext.getResponse();
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
try {
response.getWriter().write("token is empty");
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}

  测试:

  

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

  

   六、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. 安装和启动ElasticSearch服务遇到的几个问题

    首先安装和启动服务的教程是参考文章:ES入门之一 安装ElasticSearch 然后在最后的启动es服务时遇到了几个小问题,因此在这里记录一下. 因为我对linux并不是很熟悉,因此文中如果有说错的 ...

  2. 【转】Spring线程及线程池的使用

    spring @Async 线程池使用 最近公司项目正逐渐从dubbo向springCloud转型,在本次新开发的需求中,全部使用springcloud进行,在使用时线程池,考虑使用spring封装的 ...

  3. 模拟25A 题解

    A. Lighthouse m的范围极小,显然的容斥. 总的方案数,减去受任意一个限制的方案数,加回受两个限制的方案数. 就能得到受所有限制的的方案数. 将选择的一些边所指向的点放在同一个联通块里. ...

  4. quartz 1.6.2之前的版本,定时任务自动停掉问题

    https://searchcode.com/codesearch/view/28831622/ Quartz 1.6.2 Release Notes This release contains a ...

  5. activeMQ 的启动 停止 查看状态

    1 启动 : 进入到activeMQ 的 bin 目录,执行   ./activemq start  开启 ,如下: 2  查看activeMQ 是不是启动的状态, ./activemq  statu ...

  6. 【转发】jquery实现自动打开新的页签

    通常我们想要浏览器在回调方法中打开一个页签,用这个方法 //window.open("http://www.baidu.com") ;       //自动打开新窗口,会被浏览器拦 ...

  7. HTML5的服务器EventSource(server-sent event)发送事件

    参考资料: HTML5的服务器(server-sent event)发送事件有什么应用场景? W3school HTML 5 服务器发送事件 『后台消息推送功能』,前端除了轮询.scoket.第三方服 ...

  8. Egret自定义计时器(TimerManager和Laya.timer)

    一 自定义计时器 因为游戏中经常用到计时器,比如每1秒发射一枚子弹啊,每2秒怪物AI自动转向啊 每次去new Timer 然后addEventListener(egret.TimerEvent...  ...

  9. win7安装 truffle

    1. 最近有个项目需要用到区块链,第一次玩不太熟悉.现在电脑上安装个  truffle,作为一个区块链节点 2. 安装 truffle ,之前需要安装其他几个软件 truffle的安装需要首先装有:n ...

  10. 1-4dockerfile基本使用

    1.创建一个文件夹 mkdir mynginx cd mynginx touch Dockerfile [root@VM_0_10_centos mynginx]# cat Dockerfile FR ...