SpringCloud微服务服务容错机制Sentinel熔断器

1、服务雪崩

1.1、原理

一个分布式系统中各个服务互相依赖,如果其中一个服务异常,则会导致依赖它的服务进入等待,其相关上游服务也会因此而形成任务堆积,从而导致大量服务瘫痪,这就是服务雪崩

1.2、常见的解决方案

要防止雪崩的扩散,我们就要做好服务的容错,也就是让出现异常的服务不再进行扩散,同时避免异常导致服务崩溃等。

常见的容错思路有以下几种:
隔离

将一个系统分成多个模块,各个模块之间相互独立。当发生故障时,故障模块不会影响到其它模块。常见的有线程池隔离和信号量隔离。

1、线程池隔离就是给各个模块各自创建线程池,从而避免出现故障不释放线程影响其他模块等问题。

2、计数器(信号量)来限制对共享资源的并发访问数量。当多个线程或进程需要访问共享资源时,它们需要先获取信号量,如果信号量的值大于0,则线程或进程可以获取信号量并访问共享资源;如果信号量的值为0,则线程或进程需要等待其他线程或进程释放信号量。(异常时,可在finally中释放信号,也就是给信号量+1)
超时

给服务的调用设置一个超时时间,如果响应时间超过这个时间,则直接断开请求,释放线程,让上游服务正常运行,避免阻塞其他任务的执行。
限流

限制对指定服务的请求量,如果超过设置的流量阈值,则对超出请求只作拒绝策略。
熔断

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。

服务熔断一般有三种状态:

1、熔断关闭状态(Closed)

服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制

2、熔断开启状态(Open)

后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法

3、半熔断状态(Half-Open)

尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状

态。
降级

降级其实就是为服务提供一个托底方案,一旦服务无法正常调用,就使用托底方案

2、Sentinel入门

2.1、什么是Sentinel

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 具有以下特征:

1、丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

2、完备的实时监控:Sentinel 提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒

级数据, 甚至 500 台以下规模的集群的汇总运行情况。

3、广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块, 例如与 SpringCloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。

4、完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 分为两个部分:

1、核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。

2、控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

2.2、安装Sentinel控制台

Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。

1、下载jar包,解压到文件夹

https://github.com/alibaba/Sentinel/release

2、启动控制台

java 复制代码
# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -
Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar

3、修改 shop-order ,在里面加入有关控制台的配置

java 复制代码
spring:
	cloud:
		sentinel:
			transport:
				port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
				dashboard: localhost:8080 # 指定控制台服务的地址

4、通过浏览器访问localhost:8080 进入控制台 ( 默认用户名密码是 sentinel/sentinel )

5、补充:了解控制台的使用原理

Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上,

即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口

调用微服务中的监控程序获取微服务的各种信息。

2.3、微服务集成Sentinel

1、在pom.xml中加入下面依赖

java 复制代码
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2、编写一个Controller测试使用

java 复制代码
@RestController
@Slf4j
public class OrderController3 {

	@RequestMapping("/order/message1")
	public String message1() {
		return "message1";
	}
	@RequestMapping("/order/message2")
	public String message2() {
		return "message2";
	}
}

2.4、实现一个接口的限流

1、通过控制台为message1添加一个流控规则

2、通过控制台快速频繁访问, 观察效果

3、Sentinel的概念和功能

3.1、基本概念
资源
资源就是Sentinel要保护的东西

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,可以是一个服务,也可以是

一个方法,甚至可以是一段代码。

java 复制代码
我们入门案例中的message1方法就可以认为是一个资源

规则
规则就是用来定义如何进行保护资源的

作用在资源之上, 定义以什么样的方式保护资源,主要包括流量控制规则、熔断降级规则以及系统

保护规则。

java 复制代码
我们入门案例中就是为message1资源设置了一种流控规则, 限制了进入message1的流量

3.2、重要功能

Sentinel的主要功能就是容错,主要体现为下面这三个:
流量控制

流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是

随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。

Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
熔断降级

当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则

对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。

Sentinel 对这个问题采取了两种手段:

1、通过并发线程数进行限制

Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。当某个资源

出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆

积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的

线程完成任务后才开始继续接收请求。

2、通过响应时间对资源进行降级

除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。

当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的

时间窗口之后才重新恢复。

java 复制代码
Sentinel 和 Hystrix 的区别
两者的原则是一致的, 都是当一个资源出现问题时, 让其快速失败, 不要波及到其它服务
但是在限制的手段上, 确采取了完全不一样的方法:
	Hystrix 采用的是线程池隔离的方式, 优点是做到了资源之间的隔离, 缺点是增加了线程
切换的成本。
	Sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制。

系统负载保护

Sentinel 同时提供系统维度的自适应保护能力。当系统负载较高的时候,如果还持续让

请求进入可能会导致系统崩溃,无法响应。在集群环境下,会把本应这台机器承载的流量转发到其

它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,Sentinel 提供了对应的保

护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请

求。
总之一句话: 我们需要做的事情,就是在Sentinel的资源上配置各种各样的规则,来实现各种容错的功
能。

4、Sentinel规则

4.1、流控规则

流量控制,其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标,当达到指定的阈值时

对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。

java 复制代码
资源名:唯一名称,默认是请求路径,可自定义
针对来源:指定对哪个微服务进行限流,默认指default,意思是不区分来源,全部限制
阈值类型/单机阈值:
   QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流
   线程数:当调用该接口的线程数达到阈值的时候,进行限流
是否集群:暂不需要集群

高级选项

1、流控模式
直接(默认) :接口达到限流条件时,开启限流
关联 :当指定接口关联的接口达到限流条件时,开启对指定接口开启限流
链路 :当从某个接口过来的资源达到限流条件时,开启限流。针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细

2、流控效果
快速失败(默认) : 直接失败,抛出异常,不做任何额外的处理,是最简单的效果
Warm Up :它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值,适用于将突然增大的流量转换为缓步增长的场景。
排队等待 :让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设

置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。

4.2、降级规则

降级规则就是设置当满足什么条件的时候,对服务进行降级。

Sentinel提供了三个衡量条件:

1、平均响应时间 :当资源的平均响应时间超过阈值(以 ms 为单位)之后,资源进入准降级状态。

如果接下来 1s 内持续进入 5 个请求,它们的 RT都持续超过这个阈值,那么在接下的时间窗口

(以 s 为单位)之内,就会对这个方法进行服务降级。

2、异常比例:当资源的每秒异常总数占通过量的比值超过阈值之后,资源进入降级状态,即在接下的

时间窗口(以 s 为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是 [0.0,

1.0]。

3、异常数 :当资源近 1 分钟的异常数目超过阈值之后会进行服务降级。注意由于统计时间窗口是分

钟级别的,若时间窗口小于 60s,则结束熔断状态后仍可能再进入熔断状态。
4.3、热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上
热点规则简单使用

1、编写代码

java 复制代码
@RequestMapping("/order/message3")
@SentinelResource("message3")//注意这里必须使用这个注解标识,热点规则不生效
public String message3(String name, Integer age) {
	return name + age;
}

2、配置热点规则

3、分别用两个参数访问,会发现只对第一个参数限流了

热点规则增强使用

参数例外项允许对一个参数的具体值进行流控

编辑刚才定义的规则,增加参数例外项

4.4、授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

若配置白名单,则只有请求来源位于白名单内时才可通过;

若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

流控应用这个位置要填写的是来源标识,Sentinel提供了 RequestOriginParser 接口来处理来源。只要Sentinel保护的接口资源被访问,Sentinel就会调用 RequestOriginParser 的实现类去解析访问来源

1、自定义来源处理规则

java 复制代码
@Component
public class RequestOriginParserDefinition implements RequestOriginParser{
	@Override
	public String parseOrigin(HttpServletRequest request) {
		String serviceName = request.getParameter("serviceName");
		return serviceName;
	}
}

2、授权规则配置

这个配置的意思是只有serviceName=pc不能访问(黑名单)

4.5、系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、CPU使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。

java 复制代码
Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过
系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般
是 CPU cores * 2.5。
RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。

4.6、自定义规则异常返回

1、创建异常处理类

java 复制代码
package com.godfrey.execption;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author godfrey
 * @since 2020-12-26
 */
public class ExceptionHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        httpServletResponse.setContentType("text/html;charset=utf-8");
        String msg = null;
        if (e instanceof FlowException) {
            msg = "限流";
        } else if (e instanceof DegradeException) {
            msg = "降级";
        }
        httpServletResponse.getWriter().write(msg);
    }
}

2、配置

java 复制代码
@Configuration
public class SentinelConfiguration {

	@PostConstruct
    public void init2() {
        WebCallbackManager.setUrlBlockHandler(new ExceptionHandler());
    }
}

5、@SentinelResource的使用

在定义了资源点之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能通过@SentinelResource来指定出现异常时的处理策略。

@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。其主要参数如下:


定义限流和降级后的处理方法
方式一:直接将限流和降级方法定义在方法中

java 复制代码
@Service
@Slf4j
public class OrderServiceImpl3 {
	int i = 0;
	@SentinelResource(
		value = "message",
		blockHandler = "blockHandler",//指定发生BlockException时进入的方法
		fallback = "fallback"//指定发生Throwable时进入的方法
	)
	public String message() {
		i++;
		if (i % 3 == 0) {
			throw new RuntimeException();
		}
		return "message";
	}
	//BlockException时进入的方法
	public String blockHandler(BlockException ex) {
		log.error("{}", ex);
		return "接口被限流或者降级了...";
	}
	//Throwable时进入的方法
	public String fallback(Throwable throwable) {
		log.error("{}", throwable);
		return "接口发生异常了...";
	}
}

方式二: 将限流和降级方法外置到单独的类中

java 复制代码
@Service
@Slf4j
public class OrderServiceImpl3 {
	int i = 0;
	@SentinelResource(
	value = "message",
	blockHandlerClass = OrderServiceImpl3BlockHandlerClass.class,
	blockHandler = "blockHandler",
	fallbackClass = OrderServiceImpl3FallbackClass.class,
	fallback = "fallback"
	)
	public String message() {
		i++;
		if (i % 3 == 0) {
			throw new RuntimeException();
		}
			return "message4";
		}
	}
	@Slf4j
	public class OrderServiceImpl3BlockHandlerClass {
	//注意这里必须使用static修饰方法
		public static String blockHandler(BlockException ex) {
		log.error("{}", ex);
		return "接口被限流或者降级了...";
		}
	}
	@Slf4j
	public class OrderServiceImpl3FallbackClass {
		//注意这里必须使用static修饰方法
		public static String fallback(Throwable throwable) {
		log.error("{}", throwable);
		return "接口发生异常了...";
	}
}

6、Sentinel规则持久化

通过前面的讲解,我们已经知道,可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例,推送过程如下图所示:

首先 Sentinel 控制台通过 API 将规则推送至客户端并更新到内存中,接着注册的写数据源会将新的

规则保存到本地的文件中。

1、编写处理类(这里是将各项规则持久化到各自的规则文件)

java 复制代码
//规则持久化
public class FilePersistence implements InitFunc {
	@Value("spring.application:name")
	private String appcationName;
	@Override
	public void init() throws Exception {
		String ruleDir = System.getProperty("user.home") + "/sentinelrules/"+appcationName;
		String flowRulePath = ruleDir + "/flow-rule.json";
		String degradeRulePath = ruleDir + "/degrade-rule.json";
		String systemRulePath = ruleDir + "/system-rule.json";
		String authorityRulePath = ruleDir + "/authority-rule.json";
		String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
		this.mkdirIfNotExits(ruleDir);
		this.createFileIfNotExits(flowRulePath);
		this.createFileIfNotExits(degradeRulePath);
		this.createFileIfNotExits(systemRulePath);
		this.createFileIfNotExits(authorityRulePath);
		this.createFileIfNotExits(paramFlowRulePath);
		// 流控规则
		ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
			flowRulePath,
			flowRuleListParser
		);
		FlowRuleManager.register2Property(flowRuleRDS.getProperty());
		WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
			flowRulePath,
			this::encodeJson
		);
		WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
		
		// 降级规则
		ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
			degradeRulePath,
			degradeRuleListParser
		);
		DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
		WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
			degradeRulePath,
			this::encodeJson
		);
		WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
		
		// 系统规则
		ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
			systemRulePath,
			systemRuleListParser
		);
		SystemRuleManager.register2Property(systemRuleRDS.getProperty());
		WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
			systemRulePath,
			this::encodeJson
		);
		WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
		
		// 授权规则
		ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
			authorityRulePath,
			authorityRuleListParser
		);
		AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
		WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
			authorityRulePath,
			this::encodeJson
		);
		WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
		
		// 热点参数规则
		ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new FileRefreshableDataSource<>(
			paramFlowRulePath,
			paramFlowRuleListParser
		);
		ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
		WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
			paramFlowRulePath,
			this::encodeJson
		);
		
		ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
	}
	private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
		source,
		new TypeReference<List<FlowRule>>() {}
	);
	
	private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
		source,
		new TypeReference<List<DegradeRule>>() {}
	);
	
	private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
		source,
		new TypeReference<List<SystemRule>>() {}
	);
	private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
		source,
		new TypeReference<List<AuthorityRule>>() {}
	);
	private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser = source -> JSON.parseObject(
		source,
		new TypeReference<List<ParamFlowRule>>() {}
	);
	private void mkdirIfNotExits(String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			file.mkdirs();
		}
	}
	private void createFileIfNotExits(String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			file.createNewFile();
		}
	}
	private <T> String encodeJson(T t) {
		return JSON.toJSONString(t);
	}
}

2、添加配置

在resources下创建配置目录 META-INF/services ,然后添加文件

com.alibaba.csp.sentinel.init.InitFunc

在文件中添加配置类的全路径

java 复制代码
com.itheima.config.FilePersistence

原理:

java 复制代码
a、扫描配置文件
	Sentinel 启动时,ServiceLoader 会扫描所有 JAR 包中 META-INF/services/com.alibaba.csp.sentinel.init.InitFunc 文件。
b、加载实现类
	读取文件中声明的全限定类名(如 com.itheima.config.FilePersistence),通过反射实例化该类。
c、执行初始化
	调用实现类的 init() 方法,执行自定义逻辑(如加载持久化规则)。

7、Feign整合Sentinel

实际上就是开启Feign对Sentinel的支持,如果不开启,即使在Sentinel控制台对接口配置了相应的规则,但当通过feign来调用这个接口时,也不会触发配置的规则。若是开启,则会触发规则,下面所配置的容错类只是在触发Sentinel各项规则时所做的处理。

1、引入sentinel的依赖

java 复制代码
<!--sentinel客户端-->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2、在配置文件中开启Feign对Sentinel的支持

java 复制代码
feign:
	sentinel:
		enabled: true

3、创建容错类

java 复制代码
//容错类要求必须实现被容错的接口,并为每个方法实现容错方案
@Component
@Slf4j
public class ProductServiceFallBack implements ProductService {
	@Override
	public Product findByPid(Integer pid) {
		Product product = new Product();
		product.setPid(-1);
		return product;
	}
}

4、为被容器的接口指定容错类

java 复制代码
//value用于指定调用nacos下哪个微服务
//fallback用于指定容错类
@FeignClient(value = "service-product", fallback = ProductServiceFallBack.class)
public interface ProductService {
	@RequestMapping("/product/{pid}")//指定请求的URI部分
	Product findByPid(@PathVariable Integer pid);
}

5、修改controller

java 复制代码
@RestController
@Slf4j
public class OrderController {
	@Autowired
	private OrderService orderService;
	@Autowired
	private ProductService productService;
	//下单--fegin
	@RequestMapping("/order/prod/{pid}")
	public Order order(@PathVariable("pid") Integer pid) {
		log.info("接收到{}号商品的下单请求,接下来调用商品微服务查询此商品信息", pid);
		//调用商品微服务,查询商品信息
		Product product = productService.findByPid(pid);
		if (product.getPid() == -1) {
			Order order = new Order();
			order.setPname("下单失败");
			return order;
		}
		log.info("查询到{}号商品的信息,内容是:{}", pid, JSON.toJSONString(product));
		//下单(创建订单)
		Order order = new Order();
		order.setUid(1);
		order.setUsername("测试用户");
		order.setPid(pid);
		order.setPname(product.getPname());
		order.setPprice(product.getPprice());
		order.setNumber(1);
		orderService.createOrder(order);
		log.info("创建订单成功,订单信息为{}", JSON.toJSONString(order));
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return order;
	}
}

6、停止所有 shop-product 服务,重启 shop-order 服务,访问请求,观察容错效果

扩展: 如果想在容错类中拿到具体的错误,可以使用下面的方式

java 复制代码
@FeignClient(
value = "service-product",
//fallback = ProductServiceFallBack.class,
fallbackFactory = ProductServiceFallBackFactory.class)
public interface ProductService {
	//@FeignClient的value + @RequestMapping的value值 其实就是完成的请求地址
	"http://service-product/product/" + pid
	@RequestMapping("/product/{pid}")//指定请求的URI部分
	Product findByPid(@PathVariable Integer pid);
}
java 复制代码
@Component
public class ProductServiceFallBackFactory implements
FallbackFactory<ProductService> {
	@Override
	public ProductService create(Throwable throwable) {
		return new ProductService() {
			@Override
			public Product findByPid(Integer pid) {
				throwable.printStackTrace();
				Product product = new Product();
				product.setPid(-1);
				return product;
			}
	};
	}
}

注意: fallback和fallbackFactory只能使用其中一种方式

相关推荐
喂完待续8 小时前
【序列晋升】28 云原生时代的消息驱动架构 Spring Cloud Stream的未来可能性
spring cloud·微服务·云原生·重构·架构·big data·序列晋升
jzzy_hony8 小时前
云原生:微服务与Serverless指南
微服务·云原生·serverless
欧阳的棉花糖9 小时前
微前端俯瞰
微服务·前端工程化
掘金-我是哪吒10 小时前
分布式微服务系统架构第170集:Kafka消费者并发-多节点消费-可扩展性
分布式·微服务·架构·kafka·系统架构
惜.己1 天前
Docker启动失败 Failed to start Docker Application Container Engine.
spring cloud·docker·eureka
LQ深蹲不写BUG1 天前
微服务的保护方式以及Sentinel详解
微服务·云原生·架构
你是人间五月天1 天前
sentinel实现控制台与nacos数据双向绑定
windows·sentinel
无名客01 天前
sentinel限流常见的几种算法以及优缺点
算法·sentinel·限流
鼠鼠我捏,要死了捏1 天前
基于Apache Flink Stateful Functions的事件驱动微服务架构设计与实践指南
微服务·apache flink·实时处理