Hystrix断路器在微服务网关中的应用(Spring Cloud Gateway)

2023-10-07 20:40

本文主要是介绍Hystrix断路器在微服务网关中的应用(Spring Cloud Gateway),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前文回顾

在之前的一篇文章:微服务网关Zuul迁移到Spring Cloud Gateway,我们讲解了如何从Zuul迁移到新的组件:Spring Cloud Gateway,以及扩展了微服务网关的功能,包括限流过滤器、断路器过滤器等。然而很多读者在使用的时候反馈,使用POSTMAN发送GET请求测试断路器是正常的,然而POST请求会出现:

{"timestamp": "2018-10-11T13:07:07.790+0000","path": "/user/body","status": 500,"error": "Internal Server Error","message": "fallbackcmd failed and fallback failed."
}

看一下网关服务的控制台,HystrixGatewayFilterFactory也报错。

com.netflix.hystrix.exception.HystrixRuntimeException: fallbackcmd failed and fallback failed.at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:825) ~[hystrix-core-1.5.12.jar:1.5.12]at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804) ~[hystrix-core-1.5.12.jar:1.5.12]at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(OperatorOnErrorResumeNextViaFunction.java:140) ~[rxjava-1.3.8.jar:1.3.8]at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) ~[rxjava-1.3.8.jar:1.3.8]at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) ~[rxjava-1.3.8.jar:1.3.8]at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1472) ~[hystrix-core-1.5.12.jar:1.5.12]at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1397) ~[hystrix-core-1.5.12.jar:1.5.12]at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.onError(OnSubscribeDoOnEach.java:87) ~[rxjava-1.3.8.jar:1.3.8]at rx.internal.reactivestreams.SubscriberAdapter.onError(SubscriberAdapter.java:59) ~[rxjava-reactive-streams-1.2.1.jar:1.2.1]

本文主要是解决Hystrix过滤器应用过程中的报错问题,并提供正确的使用方式。

问题分析

熔断机制和日常生活中见到电路保险丝是非常相似的,当出现了问题之后,保险丝会自动烧断,以保护我们的电器。在我们的对外提供服务时,当现在服务的提供方出现了问题之后整个的程序将出现错误的信息显示,而这个时候如果不想出现这样的错误信息,而希望替换为一个错误时的内容。

一个服务挂了后续的服务跟着不能用了,这就是雪崩效应。

我们在网关配置了Hystrix断路器的过滤器:

      routes:- id: hytstrix_routeuri: lb://userorder: 6000predicates:- Path=/user/**filters:- StripPrefix=1- name: Hystrixargs:name: fallbackcmdfallbackUri: forward:/fallbackcontroller?a=123

出现错误之后可以 fallback 错误的处理信息。此外,Hystrix断路器经常结合 Feign一起使用,还需要在Feign(客户端)进行熔断的配置。

依赖版本

spring-boot-starter-parent的版本为2.0.3.RELEASE
Spring Cloud的版本为Finchley.RELEASE,对应的spring-cloud-gateway版本为2.0.0.RELEASE

报错分析

使用POSTMAN发送GET请求,不会出现第一小节的异常。当改为POST请求之后,HystrixGatewayFilterFactory抛出异常。使得刚开始的猜想往为什么不支持POST请求上考虑。打开debug日志,我们得到如下更为详细的输出:

AbstractCommand$22.call[821] : HystrixCommand execution COMMAND_EXCEPTION and fallback failed.java.lang.IllegalArgumentException: Actual request host must not be null
at org.springframework.util.Assert.notNull(Assert.java:193)
at org.springframework.web.cors.reactive.CorsUtils.isSameOrigin(CorsUtils.java:74)
at org.springframework.web.cors.reactive.DefaultCorsProcessor.process(DefaultCorsProcessor.java:70)
at org.springframework.web.reactive.handler.AbstractHandlerMapping.lambda$getHandler$1(AbstractHandlerMapping.java:152)
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:107)
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1640)
at

从上面的日志可以看出,最后的错误定位到了默认的CORS处理器DefaultCorsProcessor的实现。

public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,HttpServletResponse response) throws IOException {//是否为CORS请求(包含Origin头部)if (!CorsUtils.isCorsRequest(request)) {return true; //不是则直接返回}ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response);//根据serverResponse响应判断Access-Control-Allow-Originif (responseHasCors(serverResponse)) {logger.debug("Skip CORS processing: response already contains \"Access-Control-Allow-Origin\" header");return true;}ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request);if (WebUtils.isSameOrigin(serverRequest)) {logger.debug("Skip CORS processing: request is from same origin");return true;}boolean preFlightRequest = CorsUtils.isPreFlightRequest(request);if (config == null) {if (preFlightRequest) {rejectRequest(serverResponse);return false;}else {return true;}}return handleInternal(serverRequest, serverResponse, config, preFlightRequest);}

如上是报错部分的代码,这段代码的功能是基于cors的配置,处理给定的请求。首先判断是否为CORS的请求,是则直接返回true;否则判断响应中的头部Access-Control-Allow-Origin是否为空(Access-Control-Allow-Origin是HTML5中定义的一种解决资源跨域的策略。他是通过服务器端返回带有Access-Control-Allow-Origin标识的Response header,用来解决资源的跨域权限问题,表示接受哪些域名的请求);否则
基于Origin、Host、Forwarded、X-Forwarded-Proto、X-Forwarded-Host、X-Forwarded-Port等头部,校验请求是否同源。

对于非简单请求,CORS机制跨域会首先进行 preflight(一个 OPTIONS 请求), 该请求成功后才会发送真正的请求。 这一设计旨在确保服务器对 CORS 标准知情,以保护不支持CORS的旧服务器。

到这一步,会判断CORS的配置是否为空,如果为空,且不是一个preflight请求,则返回true,否则返回false;再下一步进入CORS的配置不为空的处理逻辑,此处略过。

这里我们拓展一下,浏览器将CORS请求分为两类:简单请求(simple request)和非简单请求(not-simple-request),简单请求浏览器不会预检,而非简单请求会预检。

这两种方式怎么区分?同时满足下列三大条件,就属于简单请求,否则属于非简单请求

  1. 请求方式只能是:GET、POST、HEAD
  2. HTTP请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID
  3. Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain。

对于简单请求,浏览器直接请求,会在请求头信息中,增加一个origin字段,来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值,来决定是否同意该请求,服务器返回的响应会多几个头信息字段,如下所示:

  1. Access-Control-Allow-Origin:该字段是必须的,* 表示接受任意域名的请求,还可以指定域名
  2. Access-Control-Allow-Credentials:该字段可选,是个布尔值,表示是否可以携带cookie,(注意:如果Access-Control-Allow-Origin字段设置*,此字段设为true无效)
  3. Access-Control-Allow-Headers:该字段可选,里面可以获取Cache-Control、Content-Type、Expires等,如果想要拿到其他字段,就可以在这个字段中指定。

上面的头信息中,三个与CORS请求相关,都是以Access-Control-开头。

非简单请求是对那种对服务器有特殊要求的请求,比如请求方式是PUT或者DELETE,或者Content-Type字段类型是application/json。都会在正式通信之前,增加一次HTTP请求,称之为预检。浏览器会先询问服务器,当前网页所在域名是否在服务器的许可名单之中,服务器允许之后,浏览器会发出正式的XMLHttpRequest请求,否则会报错。

回顾我们的业务场景,来自客户端的请求,到达网关后将会转发到具体服务,此时对应的服务是down的状态,返回的响应结果为空。我们在网关没有任何的CORS配置,因此按照上述的CORS处理逻辑,返回的结果为false。

当目标服务的状态是正常的,请求得到相应,CORS处理是正常的;因此,出错的根源在于,当我们的请求头中携带Origin时,目标服务的不可用将会导致如上的错误,这显然不是我们想要的结果。

解决思路

对于上述问题,围绕CORS的处理,我们有如下几种解决思路。

移除请求的头部Origin

移除请求的头部Origin,从CORS处理的逻辑得知,当该请求不是一个CORS请求(即不包含头部Origin),处理的过程就结束,这样可以避免后续的检查。

修改配置如下:

      routes:- id: hytstrix_routeuri: lb://userorder: 6000predicates:- Path=/user/**filters:- StripPrefix=1- RemoveRequestHeader=Origin- name: Hystrixargs:name: fallbackcmdfallbackUri: forward:/fallbackcontroller?a=123

再次发送请求,无论是GET还是POST,携带头部Origin都可以正常fallback。

CORS配置

我们还可以增加CORS的过滤器,手动增加响应的头部信息。

@Beanpublic WebFilter corsFilter() {return (ServerWebExchange ctx, WebFilterChain chain) -> {ServerHttpRequest request = ctx.getRequest();if (CorsUtils.isCorsRequest(request)) {ServerHttpResponse response = ctx.getResponse();HttpHeaders headers = response.getHeaders();headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);headers.add("Access-Control-Max-Age", MAX_AGE);headers.add("Access-Control-Allow-Headers",ALLOWED_HEADERS);if (request.getMethod() == HttpMethod.OPTIONS) {response.setStatusCode(HttpStatus.OK);return Mono.empty();}}return chain.filter(ctx);};}

Spring Cloud Gateway版本升级

2.0.1.RELEASE版本开始,Spring Cloud Gateway提供了全局cors的配置:

spring:cloud:gateway:globalcors:corsConfigurations:'[/**]':allowedOrigins: "*"allowedMethods: "*"

通过如上配置,可以实现与上一小节相同的功能。

小结

本文主要讲了Hystrix过滤器在网关中的应用时遇到的问题,通过错误信息,debug源码寻找问题的根源。之后我们分析了问题,并根据问题的根源提出了几种可行的解决方案。解决问题的方法有多种,本文主要是提供一个排查问题和解决问题的思路。

订阅最新文章,欢迎关注我的公众号

微信公众号

参考
  1. Hystrix filter throw exception with post request
  2. http跨域时的options请求

这篇关于Hystrix断路器在微服务网关中的应用(Spring Cloud Gateway)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/160296

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#