一、概述

 API 网关是一个更为智能的应用服务器,它的定义类似于面向对象设计模式中的 Facade 模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤。它除了要实现请求路由、负载均衡、校验过滤等功能之外,还需要更多能力,比如与服务治理框架的结合、请求转发时的熔断机制、服务的聚合等一系列高级功能。

 在 Spring Cloud 中了提供了基于 Netflix Zuul 实现的 API 网关组件 Spring Cloud Zuul。

二、准备阶段

SpringBoot 版本号:2.1.6.RELEASE

SpringCloud 版本号:Greenwich.RELEASE

pom.xml

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

application.yml

server:
port: 5555 spring:
application:
name: cloud-zuul eureka:
client:
service-url:
defaultZone: http://user:password@localhost:1111/eureka/

ZuulApplication.java

// 开启 Zuul 的Api 网关服务功能
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulApplication { public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}

三、请求转发

Spring Cloud Zuul 通过与 Spring Cloud Eureka 进行整合,将自身注册为 Eureka 服务治理下的应用,同时从 Eureka 中获得了所有其他微服务的实例信息。这样的设计非常巧妙地将服务治理体系中维护的实例信息利用起来,使得将维护服务实例的工作交给了服务治理框架自动完成,不再需要人工介入。而对于路由规则的维护, Zuul 默认会将通过以服务名作为

ContextPath 的方式来创建路由映射。比如上面的配置,Spring Cloud Zuul 会为 Eureka 中的每个服务都自动创建一个默认路由规则,默认规则的 path 会使用 serviceId 配置的服务名作为请求前缀 —— 对于 /'serviceId'/** 的请求,会被转发到 serviceId 的服务处理。

可以设置不对每个服务自动创建路由规则吗?

zuul:
# Zuul 将对所有的服务都不自动创建路由规则
ignored-services: "*"

如果我们手动配置路由是怎样的呢?推荐下面的方式:

zuul:
routes:
client-2:
path: /client-2/**
serviceId: cloud-eureka-client
# zuul.routes.<serviceid> = <path>
cloud-eureka-client: /client-3/**
client-4:
path: /client-4/**
# 请求转发 —— 仅限转发到本地接口
url: forward:/local

其中, ?:匹配任意单个数量字符;*:匹配任意多个数量字符;**:匹配任意多个数量字符,支持多级目录。

不推荐使用 url 的方式来配置路由,该请求是直接通过 httpClient 包实现的, 而没有使用 Hystrix 命令进行包装, 所以这类请求并没有线程隔离和断路器的保护。

如果我们要过滤掉某些 url,让它不走路由规则呢?

zuul:
# 对某些 url 设置不经过路由选择
ignored-patterns: {"/**/world/**","/**/zuul/**"}

Spring Cloud Zuul 对 "/zuul" 的路径访问的会绕过 dispatcherServlet, 被 ZuulServlet 处理,主要用来应对处理大文件上传的情况。

zuul:
servlet-path: /zuul

四、请求过滤

Spring Cloud Zuul 提供了一套过滤器机制,开发者可以通过使用 Zuul 来创建各种校验过滤器,然后指定哪些规则的请求需要执行校验逻辑,只有通过校验的才会被路由到具体的微服务接口,不然就返回错误提示。

要在 Zuul 实现过滤器机制也很简单,只需要继承 ZuulFilter 类即可。接下来,我们来写一个过滤器 TokenFilter,校验接口参数中是否有 token 参数。

@Component
public class TokenFilter extends ZuulFilter { private Logger logger = LoggerFactory.getLogger(TokenFilter.class); /**
* 过滤器的类型,它决定过滤器在请求的哪个生命周期中执行。这里定义为 pre, 代表会在请求被路由之前执行。路由类型有下面几种:
* <p>
* - pre: 可以在请求被路由之前调用。
* - routing: 在路由请求时被调用。
* - post: 在 routing 和 error 过滤器之后被调用。
* - error: 处理请求时发生错误时被调用。
*
* @return
*/
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
} /**
* 过滤器的执行顺序。当请求在一个阶段中存在多个过滤器时,需要根据该方法返回的值来依次执行,数值越小,优先级越高。
*
* @return
*/
@Override
public int filterOrder() {
return 0;
} /**
* 判断该过滤器是否需要被执行。这里我们直接返回了true, 因此该过滤器对所有请求都会生效。实际运用中我们可以利用该函数来指定过滤器的有效范围。
*
* @return
*/
@Override
public boolean shouldFilter() {
return true;
} /**
* 过滤器的具体执行逻辑
*
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
logger.warn("token is empty");
// 令 zuul 过滤该请求,不对其进行路由
ctx.setSendZuulResponse(false);
// 设置返回的错误码
ctx.setResponseStatusCode(401);
// 设置返回的 body
ctx.setResponseBody("");
return null;
}
logger.info("access token is ok");
return null;
}
}

实际上,上面提到的 Zuul 路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。所以,过滤器可以说是 Zuul 实现 API 网关功能最为核心的部件,每一个进入 Zuul 的 HTTP 请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。下图源自 Zuul 的官方Wiki 中关于请求生命周期的图解, 它描述了一个 HTTP 请求到达 API 网关之后, 如何在各种不同类型的过滤器之间转的详细过程。

当外部 HTTP 请求到达 API 网关服务的时候,首先它会进入第一个阶段 pre, 在这里它会被 pre 类型的过滤器进行处理, 该类型过滤器的主要目的是在进行请求路由之前做一些前置加工,比如请求的校验、限流等。在完成了 pre 类型的过滤器处理之后,请求进入第二个阶段 routing, 也就是之前说的路由请求转发阶段,请求将会被 routing 类型过滤器处理。这里的具体处理内容就是将外部请求转发到具体服务实例上去的过程,当服务实例将请求结果都返回之后,routing 阶段完成, 请求进入第三个阶段 post。此时请求将会被 post 类型的过滤器处理,这些过滤器在处理的时候不仅可以获取到请求信息,还能获取到服务实例的返回信息,所以在 post 类型的过滤器中,我们可以对处理结果进行一些加工或转换等内容。另外,还有一个特殊的阶段 error, 该阶段只有在上述三个阶段中发生异常的时候才会触发,但是它的最后流向还是 post 类型的过滤器,因为它需要通过 post 过滤器将最终结果返回给请求客户端。

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 处理正常的请求响应

我们可以在配置文件中,选择是否禁用某个过滤器。

zuul:
# 禁用某个过滤器 zuul.<SimpleClassName>.<filterTye>.disable=true
TokenFilter:
pre:
disable: true

常常 request 中有些 header 信息我们不希望渗透到服务中去,比如 accessToken、sign、Cookie 等。或者我们要保持 request 的 host 信息一致,该怎么配置呢?

zuul:
routes:
client-2:
path: /client-2/**
serviceId: cloud-eureka-client
# 敏感头信息设置为空,表示不过滤敏感头信息,允许敏感头信息渗透到下游服务器(针对单个服务的敏感头部信息配置,下面两个配置项选其一即可)
sensitiveHeaders: ""
customSensitiveHeaders: true # Spring Cloud Zuul在请求路由时,会过滤掉 HTTP 请求头(Cookie、Set-Cookie、Authorization)信息中的一些敏感信息,
sensitive-headers: {"Cookie", "Set-Cookie", "Authorization"}
# 网关在进行路由转发时为请求设置 Host 头信息(保持在路由转发过程中 host 头信息不变)
add-host-header: true
# 请求转发时加上 X-Forwarded-*头域
add-proxy-headers: true

五、Hystrix 和 Ribbon 支持

# 该参数可以用来设置 API 网关中路由转发请求的 HystrixCommand 执行超时时间,单位为毫秒。
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutinMilliseconds: 5000 ribbon:
# 该参数用来设置路由转发请求的时候,创建请求连接的超时时间。
ConnectTimeout: 500
# 该参数用来设置路由转发请求的超时时间。
ReadTimeout: 2000
# 最大自动重试次数
MaxAutoRetries: 1
# 最大自动重试下一个服务的次数
MaxAutoRetriesNextServer: 1

其中,Hystrix 的配置参数可以在 HystrixCommandProperties.java 中找到。

其中,Ribbon 的配置参数可以在 CommonClientConfigKey.java 中找到。

另外需要注意的是,请求重试还需要将 zuul.retryable 设置为 true。

演示源代码 :https://github.com/JMCuixy/spring-cloud-demo

内容参考:《Spring Cloud 微服务实战》

Spring Cloud 之 Zuul基础.的更多相关文章

  1. Spring Cloud Netflix Zuul 重试会自动跳过经常超时的服务实例的简单说明和分析

    在使用E版本的Spring Cloud Netflix Zuul内置的Ribbon重试功能时,发现Ribbon有一个非常有用的特性: 如果某个服务的某个实例经常需要重试,Ribbon则会在自己维护的一 ...

  2. spring cloud(二) zuul

    spring cloud 网关 zuul 搭建过程 1. 新建boot工程 pom引入依赖 <dependency> <groupId>org.springframework. ...

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

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

  4. Spring Cloud (十三) Zuul:静态路由、静态过滤器与动态路由的实现

    前言 本文起笔于2018-06-26周二,接了一个这周要完成的开发任务,需要先等其他人的接口,可能更新的会慢一些,还望大家见谅.这篇博客我们主要讲Spring Cloud Zuul.项目地址:我的gi ...

  5. Spring Cloud之Zuul

    API网关是一个更为智能的应用服务器,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤.它除了要实现请求路由.负载均衡.校验过滤等功能之外,还需要更多能力, ...

  6. spring cloud 配置zuul实用

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 技术背景 前面我们通过Ribbon或Feign实现了微服务之间的调用和负载均衡 ...

  7. Spring Cloud(五) --- zuul

    微服务网关 在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务.当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用 ...

  8. spring cloud使用zuul实现反向代理和负载均衡

    首先,这篇文章参考的是http://blog.didispace.com/springcloud5/这位大牛的博客.本人是通过这篇博客来学习zuul的,现在写的博客只是个人在学习时个人的一些感受和理解 ...

  9. Spring Cloud之Zuul网关集群

    Nginx+Zuul 一主一备 或者 轮训多个 在微服务中,所有服务请求都会统一到Zuul网关上. Nginx 配置: #user nobody; worker_processes 1; #error ...

随机推荐

  1. Android之Log封装

    blog原文地址:http://yuxingxin.com/2015/10/26/AndroidLog/ Github:https://github.com/fallblank/CodeEssay

  2. UWP中的消息提示框(一)

    不管什么平台,应用内难免会出现一些消息提示框,下面就来聊聊我在UWP里用到的消息提示框. 弹窗也可按是否需要用户操作促发一些逻辑进行分为两大类. 不需要用户干涉的一类: MessageDialog:操 ...

  3. C# 遍历窗体控件顺序问题

    今天在做C# winform 窗体控件遍历时遇到控件顺序的问题,也就是控件被遍历的先后问题.实际情况如下所述. 窗体界面如下: 界面构成是:主界面有一个 Panel (Panel_14),Panel_ ...

  4. QT5.1编译后的安装目录问题(硬路径问题)

    这个是我的编译参数:configure -ltcg -confirm-license -opensource -platform win32-msvc2010 -debug-and-release - ...

  5. Qt 5.3更新无数,更改C++控制台输出最为赞(这样就和普通C++ IDE没区别了)

    转载请注明文章:Qt 5.3更新无数,更改C++控制台输出最为赞 出处:多客博图 本人觉得有了这个更新,Qt Creator可谓几乎没有缺点了,起码仅仅开发C/C++,是不用再去安装VS了. Qt 5 ...

  6. Qt技术优势

    1. Qt这个C++的图形库由Trolltech在1994年左右开发.它可以运行在Windows,Mac OS X, Unix,还有像Sharp Zaurus这类嵌入式系统中.Qt是完全面向对象的. ...

  7. asp.net mvc+jquery easyui开发实战教程之网站后台管理系统开发3-登录模块开发

    进行本文之前需要在数据库用户表里面增加一条用户数据,直接手动添加即可,未安全考虑密码一定要使用Md5加密后的,这里提供666666的Md5密文为(c831b04de153469d),本文完成登录模块的 ...

  8. 使用Nodejs实现实时推送MySQL数据库最新信息到客户端

    下面我们要做的就是把MySQL这边一张表数据的更新实时的推送到客户端,比如MySQL这边表的数据abc变成123了,那使用程序就会把最新的123推送到每一个连接到服务器的客户端.如果服务器的连接的客户 ...

  9. linux 十五个原理知识点

    DNS系统架构与解析原理http协议通信原理TCP/IP的3次握手和四次断开原理MySQL主从同步原理Nginx配合php的fastcgi工作原理Lvs的4种模式工作原理Memcached工作原理(内 ...

  10. 血的教训--如何正确使用线程池submit和execute方法

    血的教训之背景:使用线程池对存量数据进行迁移,但是总有一批数据迁移失败,无异常日志打印 凶案起因 ​ 听说parallelStream并行流是个好东西,由于日常开发stream串行流的场景比较多,这次 ...