服务网关-Zuul(二)
https://blog.csdn.net/weixin_45481406/article/details/110499263
什么是Zuul
Zuul是从设备和网站到应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul旨在实现动态路由、监视、弹性和安全性,Zuul包含了对请求的路由和过滤两个最主要的功能。
Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用。Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能:
- 身份安全与认证:识别每个资源的验证请求,并拒绝那些与要求不符合的请求
- 审查和监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生产试图
- 动态路由:动态地将请求路由到不同的后端集群
- 压力测试:逐渐增加集群的流量,以了解性能
- 负载分配:为每一种负载类型分配对应的容量,并弃用超出限定值的请求
- 静态响应处理:在边缘位置直接建立部分响应,从而避免其转发到内部集群中
- 多区域弹性:跨越AWS Region进行请求路由,旨在实现ELB(Elastic Load Balancing)使用的多样化,以及系统的边缘更贴近系统的使用者
关于网关的介绍可以参考文章:服务网关-基础介绍(一)
Zuul 入门案例
环境准备
服务注册中心使用Eureka,可以参考 服务注册与发现-Eureka(三)
Zuul实现API网关,搭建网关服务
1. 导入jar包
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
</dependencies>
<dependencyManagement><dependencies><!-- Spring Cloud 依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR1</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
注意:目前SpringCloud还没有升级Zuul的版本,依旧使用的是Zuul1.0的版本,所以在选择SpringBoot和SpringCloud别用那么新的版本
2. 启动类中开启Zuul代理
@SpringBootApplication
@EnableZuulProxy // 开启Zuul代理
public class ZuulServerDemoApplication {public static void main(String[] args) {SpringApplication.run(ZuulServerDemoApplication.class, args);}
}
3. 配置路由规则
# Zuul 路由规则
zuul:routes:eureka-feign-demo: # 路由ID,自定义,建议配置成对应服务的服务名path: /consumer/** # 请求url的映射路径url: http://localhost:8006/ # 映射路径对应的微服务地址
通配符的含义
通配符 | 含义 | 举例 | 示例 |
---|---|---|---|
? | 匹配任意单个字符 | /consumer/? | /consumer/a , /consumer/b … |
* | 匹配任意数量字符不包括子路径 | /consumer/* | /consumer/aaas , /consumer/bbbs … |
** | 匹配任意数量字符包括所有的下级路径 | /consumer/** | /consumer/a , /consumer/b/cc … |
4. 启动测试
请求地址示例:IP:zuul服务端口/请求映射路径/服务API地址,通过Zuul路由请求到指定的服务
路由规则
上面的案例使用的是URL地址路由,除此之外,还有服务名称路由。当我们的服务有很多的时候,这个时候在通过URL地址去路由就很难去管理,因此Zuul支持与Eureka整合开发,根据serviceId也就是服务名自动从服务注册中心获取服务地址并转发,这样做的好处不仅可以通过单个端点来访问应用的所有服务,而且在添加或移除服务实例时不用再修改Zuul的路由配置
1. Zuul服务中添加Eureka依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2. 配置Eureka注册中心
# Eureka 配置
eureka:instance:hostname: ${spring.cloud.client.ip-address}:${server.port} # 主机名,不配置会获取系统主机名prefer-ip-address: true # 是否使用IP注册instance-id: ${spring.cloud.client.ip-address}:${server.port} # 实例IDclient:register-with-eureka: true # 是否将自己注册到注册中心,默认truefetch-registry: true # 是否从注册中心获取注册信息,默认trueservice-url: # 指向注册中心defaultZone: http://root:123456@localhost:8002/eureka/,http://root:123456@localhost:8001/eureka/registry-fetch-interval-seconds: 10 # 拉取Eureka注册信息间隔时间
3. 修改Zuul路由配置
# Zuul 路由规则
zuul:routes:eureka-feign: # 路由ID,自定义,建议配置成对应服务的服务名path: /consumer/** # 请求url的映射路径serviceId: eureka-feign-demo # 根据服务名自动从服务注册中心获取服务地址并转发
路由排除
我们可以通过路由排除设置不允许被访问的资源,允许被访问的资源可以通过路由规则进行设置。可以通过URL地址或服务名称两种方式排除
方式一:URL地址排除
# Zuul 路由规则
zuul:ignored-patterns: /**/list # 排除所有请求路径中有 list 的请求,排除多个用逗号分割
方式二:服务名称排除
# Zuul 路由规则
zuul:ignored-services: eureka-feign-demo # 据服务名排除,多个服务逗号分割, '*' 排除所有
路由前缀
路由前缀就是在调用所有的资源之前加上一个前缀,它的好处了可以重写URL,屏蔽真实的参数和目标地址,还可以方便搜索引擎收录
# Zuul 路由规则
zuul:prefix: /api
网关过滤器
Zuul包含了对请求的路由和过滤两个核心功能,其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础;而过滤器功能负责对请求的处理过程进行干预,是实现请求校验、服务器聚合等功能的基础,而实际上,路由功能在真正运行时,它的路由映射和请求转发都是由几个不同的过滤器完成的。
路由映射主要通过 pre
类型的过滤器完成,它将请求路径与配置的路由规则进行匹配,以找到需要转发的目标地址;而请求转发的部分则由 routing
类型的过滤器完成,对 pre
类型的过滤器获得的路由地址进行转发。所以说,过滤器可以说是Zuul实现API网关功能最核心的部件,每一个进入Zuul的http请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端
过滤器主要有以下四种类型:
- pre:请求被路由到源服务器之前执行的过滤器,例如执行身份认证、日志处理
- routing:处理将请求发送到源服务器的过滤器
- post:响应从源服务器返回时执行的过滤器,例如增加响应头、收集统计和度量指标
- error:上述阶段中出现错误时执行的过滤器
过滤器的使用案例
Zuul中实现过滤器必须包含4个基本特征:过滤器类型、执行顺序、执行条件、具体操作,这些步骤都是 ZuulFilter
接口中定义的4个抽象方法
@Component
public class ConsumerFilter extends ZuulFilter {private static final Logger log = LoggerFactory.getLogger(ConsumerFilter.class);/*** 过滤器类型:pre routing post error*/@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}/*** 执行顺序,数值越小,优先级越高*/@Overridepublic int filterOrder() {return 0;}/*** 执行条件 true-开启 false-关闭*/@Overridepublic boolean shouldFilter() {return true;}/*** 具体操作*/@Overridepublic Object run() throws ZuulException {RequestContext context = RequestContext.getCurrentContext();HttpServletRequest request = context.getRequest();log.info("过滤器执行: 方法名:{} url地址:{}", request.getMethod(), request.getRequestURL().toString());return null;}
}
Zuul请求的生命周期
Zuul和Hystrix无缝整合
在SpringCloud中,Zuul启动器中包含了Hystrix的相关依赖,在Zuul网关工程中,默认是提供了Hystrix Dashboard服务监控数据的,但是不会提供监控面板的界面显示。在SpringCloud中,Zuul和Hystrix是无缝结合的,我们可以很方便的实现网关容错处理。
网关监控
1. 导入jar包
Zuul的依赖包中包含了Hystrix相关jar包,所以我们不需要在项目中在额外添加Hystrix的依赖,但是需要在开启数据监控的项目中添加 dashboard
依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
2. 配置中开启端点监控
# 度量指标监控和健康检查
management:endpoints:web:exposure:include: hystrix.stream
3. 启动类中开启数据监控
@SpringBootApplication
@EnableZuulProxy // 开启Zuul代理
@EnableHystrixDashboard // 开启数据监控
public class ZuulServerDemoApplication {public static void main(String[] args) {SpringApplication.run(ZuulServerDemoApplication.class, args);}
}
4. web页面监控
访问 http://localhost:80/hystrix hystrix 的web控制端,配置hystrix监控地址
网关熔断
在Edgware版本之前,Zuul提供了接口 ZuulFallbackProvider
用于实现fallback处理。从Edgware版本开始,Zuul提供了接口 FallbackProvider
来提供fallback处理。Zuul的fallback容错处理逻辑只针对timeout异常处理,当请求被zuul路由后,只要服务有返回(包括异常),都不会触发zuul的fallback处理逻辑。因为对于zuul网关来说,做请求路由分发的时候,结果由远程服务运算。远程服务反馈了异常信息,zuul网关不会处理异常,因为无法确定这个错误是否是应用程序真实想反馈给客户端的
示例:
@Component
public class ProviderFallBack implements FallbackProvider {/*** 需要处理的服务,返回需要处理的服务名称* 1. 为指定的服务定义特性化FallBack逻辑* 2. 提供一个处理所有服务的通用FallBack逻辑*/@Overridepublic String getRoute() {return "eureka-feign-demo";}/*** @param route 容错服务名称* @param cause 服务异常信息* @desc 容错处理逻辑*/@Overridepublic ClientHttpResponse fallbackResponse(String route, Throwable cause) {return new ClientHttpResponse() {/*** 返回状态码*/@Overridepublic HttpStatus getStatusCode() throws IOException {return HttpStatus.INTERNAL_SERVER_ERROR;}/*** 返回状态码*/@Overridepublic int getRawStatusCode() throws IOException {return this.getStatusCode().value();}/*** 返回状态码*/@Overridepublic String getStatusText() throws IOException {return this.getStatusCode().getReasonPhrase();}/*** 回收资源*/@Overridepublic void close() {}/*** 设置响应体* Zuul会将本方法返回的输入流数据读取并通过HttpServletResponse的输出流输出到客户端*/@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream("服务不可用".getBytes());}/*** 设置响应头信息*/@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));return headers;}};}
}
网关限流
限流就是限制流量,通过限流可以很好的控制系统QPS,从而达到保护系统的目的。Zuul网关也提供了限流保护。当请求并发达到阈值,自动触发限流保护,返回结果错误。只要提供error错误处理机制即可。 限流的实现主要依赖第三方框架 RateLimit 实现
1. 限流算法
Zuul实现限流是通过第三方组件实现的,常见的限流算法主要有:计数器算法、漏桶算法、令牌桶算法
1. 计数器算法
计数器算法是限流算法中最简单的也是最容易实现的一种算法。例如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个,那么在一开始的时就设置一个计数器counter,每接收到一个请求counter就加1,如果counter大于100并且与第一个请求的时间间隔还在1分钟内,触发限流;超过一分钟则重置counter重新计数
这个算法非常简单,但是会有一个致命问题:临界问题。如果恶意用户在59秒的时候发送100个请求,并且在1分钟的时候又发送100个请求,相当于在1秒内实际发送了200个请求。用户在时间窗口重置的节点突发请求,瞬间超过了我们的速率限制,瞬间可以压垮我们的应用。
除此之外,还存在资源浪费问题,例如在30s的时候我们的请求就达到上限了,剩余的30s就处于闲置状态
2. 漏桶算法
漏桶算法可以看做是注水漏水的过程。往桶中以任意速率注入水,以一定速率流出水,当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。漏桶算法是使用队列机制实现的
漏桶算法主要用途在于保护其他服务,假设入水量很大,而出水量较慢,则会造成网关资源堆积,可能导致网关瘫痪。而目标服务可能可以处理大量请求的,但是漏桶算法出水量缓慢反而造成服务的资源浪费
漏桶算法无法应对突发调用。不管上面流量多大,下面流出的速度始终不变。因为处理的速度是固定的,请求进来的速度是未知的,可能突然出来很多请求,没来得及处理的请求就先放在桶里。当桶满了就会将新进来的请求丢弃掉
3. 令牌桶算法
令牌桶算法是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放一定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择等待可用的令牌或直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌
令牌桶算法主要目的在于保护自己,将请求压力交给目标服务处理。假设突然来了很多请求,只要拿到令牌这些请求就会瞬间被处理调用目标服务
2. 限流案例
Zuul限流算法使用的是令牌桶算法,需要额外添加依赖,并且限流数据采用Redis进行储存
1. 导入依赖
<!-- 连接池 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>
<!-- redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- rate-limit -->
<dependency><groupId>com.marcosbarbero.cloud</groupId><artifactId>spring-cloud-zuul-ratelimit</artifactId><version>2.2.1.RELEASE</version>
</dependency>
2. 配置Redis
spring:redis:host: 127.0.0.1port: 6379timeout: 10000database: 0lettuce:pool:max-active: 1024max-idle: 200max-wait: 10000min-idle: 5
限流分为全局限流和局部限流
3. 配置全局限流
使用全局限流配置,Zuul会对代理的所有服务提供限流保护
zuul:# 服务限流ratelimit:enabled: true # 开启限流保护repository: redis # 限流数据储存方式default-policy-list: # 全局策略,默认- limit: 3 # 每个刷新间隔窗口的请求数量限制quota: 1000 # 单位时间内累计请求时间限制(秒)refresh-interval: 60 # 限流时间窗口,默认60type: # 限流方式- USER- ORIGIN- URL
4. 配置局部限流
使用局部限流配置,Zuul仅针对配置的服务提供限流保护
zuul:# 服务限流ratelimit:enabled: true # 开启限流保护repository: redis # 限流数据储存方式policy-list: # 局部限流配置,优先级高于全部eureka-feign-demo: # 需要限流的服务名- limit: 5 # 每个刷新间隔窗口的请求数量限制quota: 1000 # 单位时间内累计请求时间限制(秒)refresh-interval: 60 # 限流时间窗口,默认60type: # 限流方式- USER- ORIGIN- URL
5. 修改错误异常
@Component
public class ErrorFilter extends ZuulFilter {private static final Logger log = LoggerFactory.getLogger(ErrorFilter.class);@Overridepublic String filterType() {return FilterConstants.ERROR_TYPE;}@Overridepublic int filterOrder() {return 0;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() {RequestContext context = RequestContext.getCurrentContext();HttpServletResponse response = context.getResponse();ZuulException exception = this.zuulException(context.getThrowable());HttpStatus status = null;if (429 == exception.nStatusCode) {status = HttpStatus.TOO_MANY_REQUESTS;}if (500 == exception.nStatusCode) {status = HttpStatus.INTERNAL_SERVER_ERROR;}try {// 此处自定义响应体endresponse.setStatus(status.value());response.setContentType("application/json;charset=UTF-8");response.getOutputStream().write(status.getReasonPhrase().getBytes());} catch (IOException e) {e.printStackTrace();}return null;}/*** @param e 异常* @desc 异常类型判断*/private ZuulException zuulException(Throwable e) {if (e.getCause() instanceof ZuulRuntimeException) {return (ZuulException) e.getCause().getCause();}if (e.getCause() instanceof ZuulException) {return (ZuulException) e.getCause();}if (e instanceof ZuulException) {return (ZuulException) e;}return new ZuulException(e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null);}
}
注意:使用自定义异常李过滤器需要关闭默认的异常过滤器
zuul:SendErrorFilter:error:disable: true # 停用内置的默认异常处理器
3. 自定义限流策略
如果希望自己控制限流策略,可以通过自定义 RateLimitKeyGenerator
的实现来增加自己的策略逻辑,主要是保持限流的key不一致
@Component
public class MyRateLimitKeyGenerator extends DefaultRateLimitKeyGenerator {public MyRateLimitKeyGenerator(RateLimitProperties properties, RateLimitUtils rateLimitUtils) {super(properties, rateLimitUtils);}@Overridepublic String key(HttpServletRequest request, Route route, RateLimitProperties.Policy policy) {// 自定义限流器的原理是保持限流的key不一致return super.key(request, route, policy) + ":" + request.getParameter("id"); // 拼接上请求的参数为新的key}
}
网关调优
使用Zuul的SpringCloud微服务结构图如下:
从上图可以看出,整体请求逻辑还是比较复杂的,在没有Zuul网关的情况下,client请求service的时候,也有超时的可能。那么当增加了Zuul网关的时候,请求超时的可能就更明显了
当请求通过Zuul网关路由到服务,并等待服务返回响应,这个过程Zuul也有超时控制。Zuul的底层使用的是Hystrix + Ribbon来实现请求路由的。
Zuul中的Hystrix内部使用线程池隔离机制提供请求路由实现,其默认超时时间是1秒。Ribbon底层默认超时时长是5000毫秒。如果Hystrix超时,直接返回超时异常;如果Ribbon超时,同时Hystrix未超时,Ribbon会自动进行服务集群轮询重试,知道Hystrix超时为止;如果Hystrix超时时长小于Ribbon超时时长,Ribbon就不会进行服务集群轮询重试。
示例:
1. 添加依赖
Zuul网关重试机制需要依赖第三方组件
<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId>
</dependency>
2. 启动类中开启重试机制
@SpringBootApplication
@EnableZuulProxy // 开启Zuul代理
@EnableHystrixDashboard // 开启数据监控
@EnableRetry // 开启重试机制
public class ZuulServerDemoApplication {public static void main(String[] args) {SpringApplication.run(ZuulServerDemoApplication.class, args);}
}
3. 配置重试参数
Zuul需要配置Hystrix和Ribbon的重试参数
zuul:retryable: true # 开启重试
# Hystrix 超时时间设置
hystrix:command:default:execution:isolation:thread:timeoutInMilliseconds: 10000 # 线程池隔离,默认超时时间1000ms
# Ribbon 超时时间,建议小于Hystrix
ribbon:ConnectTimeout: 5000 # 请求连接的超时时间,默认1000msReadTimeout: 5000 # 请求处理的超时时间,默认1000ms# 重试次数MaxAutoRetries: 1 # 表示访问集群下原节点的重试次数(同路径访问)MaxAutoRetriesNextServer: 1 # 表示访问集群下其他节点重试次数(换了一台服务器)# Ribbon 开启重试OkToRetryOnAllOperations: true
源码地址:https://gitee.com/peachtec/hxz-study
服务网关-Zuul(二)相关推荐
- zuul网关_SpringCould之服务网关(zuul)介绍与配置
??记得点击上方蓝字"程序员小强"关注哦 一.前言介绍 1.什么是服务(API)网关? 服务网关也就是API网关,可以作为服务的统一入口. 可提供身份校验.动态路由.负载均衡.安全 ...
- SpringCloud 服务网关 Zuul 自定义路由和排除路由配置
前言 首先需要说明的是该文是 [带你入门SpringCloud 之 服务网关 Zuul ]的拓展篇,如果还未阅读 [带你入门SpringCloud 之 服务网关 Zuul ]请先阅读完毕后在阅读该文. ...
- [菜鸟SpringCloud实战入门]第九章:服务网关Zuul体验
前言 欢迎来到菜鸟SpringCloud实战入门系列(SpringCloudForNoob),该系列通过层层递进的实战视角,来一步步学习和理解SpringCloud. 本系列适合有一定Java以及Sp ...
- 服务网关 Zuul基本使用
API 网关是一个更为智能的应用服务器,它的定义类似面向对象设计模式中的Facade模式,它的存在就像是整个微服务架构系统的门面一样,所有的外部客户端访问都需要经过它来进行调度和过滤.它除了要实现请求 ...
- 服务容错保护Hystrix服务网关Zuul
1.服务容错保护Hystrix 1.1.背景 在微服务架构中consumer调用provider的时候,provider在响应的时候,有可能会慢,如果provider 10s响应,那么consumer ...
- 服务网关zuul之二:过滤器--请求过滤执行过程(源码分析)
Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能: 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求. 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生成 ...
- springcloud 网关_Spring Cloud 系列之 Netflix Zuul 服务网关(二)
本篇文章为系列文章,未读第一集的同学请猛戳这里: 哈喽沃德先生:Spring Cloud 系列之 Netflix Zuul 服务网关(一)zhuanlan.zhihu.com 本篇文章讲解 Zuul ...
- Spring Cloud(七)服务网关 Zuul Filter 使用
上一篇文章中,讲了Zuul 转发,动态路由,负载均衡,等等一些Zuul 的特性,这个一篇文章,讲Zuul Filter 使用,关于网关的作用,这里就不再次赘述了,重点是zuul的Filter ,我们可 ...
- Spring Cloud第五章:服务网关Zuul
在微服务架构中,需要几个关键的组件,服务注册与发现.服务消费.负载均衡.断路器.智能路由.配置管理等,由这几个组件可以组建一个简单的微服务架构,如下图: 客户端的请求首先经过负载均衡(zuul.Ngn ...
最新文章
- linux内核pwn,[内核pwn] 环境搭建
- python 列表拼接_【Python杂货铺】速学python基础
- 译Step-by-Step Guide on Configuring Django-Userena
- 岳翔南京大学计算机,基于组合IIS路径抽取的组合线性混成系统有界可达性分析-中国科学.PDF...
- 逻辑回归(logistic regression)的本质——极大似然估计
- docker安装nacos步骤
- 【Qt教程】1.3 - Qt5 工程文件的功能解读、快捷键
- python如何制作一个工程软件_如何利用python制作一个解压缩软件-Go语言中文社区...
- 关于matlab的问题,关于MATLAB的一些基础问题
- 洛谷2501 BZOJ1801中国象棋题解
- expect - linux远程执行命令
- 首届全国大学生工程训练综合能力竞赛圆满落幕
- java动画迷宫寻路_迷宫寻路算法
- 实测:华为鸿蒙系统比 Android 系统快 60%!
- 偶尔会有的一点感受(二)
- 编译可在Android上运行的qemu
- 阿里云大数据开发三面面经,已过,面试题已配答案
- 30个令人捧腹的关于码农和编程的笑话
- Visual Studio 2008 (vs 2008)简体中文专业版、团队版及SP1下载地址
- 历届图灵奖 (Turing award)得奖名单