目录
1 前言
在分布式系统中,由于网络原因或者自身的原因,服务一般无法保证100%可用。如果一个服务出现了问题,那么调用这个服务就会出现线程阻塞的情况,此时若有大量的请求进来,就会出现多条线程阻塞等待、进而导致服务瘫痪。所以我们一般在服务中进行服务容错,来对出现这种问题的服务进行处理。
常见的容错组件:
- Hystrix
Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性和容错性。
- Resilience4j
Resilience4j一款非常轻量简单,并且文档非常清晰,丰富的熔断工具,这也是Hystrix官方推荐的替代产品。不仅如此,Resilience4j还原生支持Spring Boot1.x/2.x,而且监控也支持和prometheus等多款主流产品进行整合
- Sentinel
Sentinel是阿里巴巴开源的一款断路器的实现。
2 Sentinel
Sentinel是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点、从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
Sentinel的特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel分为两个部分
核心库(java客户端)不依赖任何框架/库。都够运行所有的java运行时环境,同时对Dubbo/Spring Cloud等框架也有较好的支持。
控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。
2.1 微服务集成 Sentinel
注意:此篇文章是基于之前文章来写的,直接看到这里会摸不着头脑
加入Sentinel相关依赖。
注意:如果是上游微服务调用下游微服务(比如:订单微服务>>>商品微服务),那么应该在上游微服务(订单微服务)中引入依赖。
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
2.2 安装Sentinel控制台
Sentinel提供一个轻量级的控制台、它提供机器发现、单机资源实时监控以及规则管理等功能。
1)下载地址:Releases · alibaba/Sentinel · GitHub
2)找到下载路径,cmd进入后执行jar命令启动
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar
3)访问http://localhost:8080/#/login(默认用户名和密码都是sentinel)
4)编写配置文件
sentinel: transport: port: 9999 #和控制台交流的端口,随意指定一个未使用的端口即可 dashboard: localhost:8080 #指定控制台服务的地址 eager: true #取消懒加载(否则服务启动之后,必须访问一次才能注册到sentinel中)
5)启动微服务并且重新访问sentinel客户端
发现我们的订单服务已经注册上去了。
2.3 Sentinel简单使用
sentinel会记录我们服务的请求,我们可以简单的对一个接口进行流控测试。如上图所示点击“流控”后。
流控:
1)QPS(先测试该接口一秒钟访问请求不超过2次)
然后连续访问接口(1s内请求多次)就会发现被流控了
2.4 @SentinelResource的使用
在定义了资源之后,我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能通过@SentinelResource来指定出现异常时的处理策略。@SentinelResource用于自定义资源,并提供可选的异常处理和fallback配置项。主要参数如下:
1、value:资源名称,必需项(不能为空),尽可能设置唯一值。
2、entryType:entry 类型,可选项(默认为 EntryType.OUT)。
3、blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
4、fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
(1)返回值类型必须与原函数返回值类型一致;
(2)方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
(3)fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
5、defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
(1)返回值类型必须与原函数返回值类型一致;
(2)方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
(3)defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
6、exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
自定义异常:
@Service
@Slf4j
public class OrderServiceImpl2 {
/**
* 定义一个资源 并且定义当资源内部发生异常时的处理逻辑
* blockHandler 定义当资源内部发生了BlockException应该进入的方法(捕获的是sentinel定义的异常)
* fallback 定义当资源内部发生了Throwable应该进入的方法
* @return
*/
@SentinelResource(value = "message",
blockHandler = "blockHandler",
fallback = "fallback")
public String message(String name) {
return "SentinelResource";
}
/**
* 当前方法的返回值必须和原方法一致
* 允许在参数列表后面最后加入一个参数BlockException,用来接收原方法中发生的异常
* @return
*/
public String blockHandler(String name, BlockException e){
//自定义异常处理逻辑
log.info("触发了BlockException异常");
return "BlockException";
}
/**
* 当前方法的返回值必须和原方法一致
* 允许在参数列表后面最后加入一个参数BlockException,用来接收原方法中发生的异常
* @return
*/
public String fallback(String name, Throwable e){
//自定义异常处理逻辑
log.info("触发了Throwable异常");
return "Throwable";
}
}
如上:定义了两个异常处理。一个是BlockException(针对Sentinel的异常会被捕捉),一个是
Throwable(针对业务异常)。以此来测试。我们可以先进行一个流控,让其触发Sentinel异常。
一秒钟之内点击多次,就会触发 Sentinel异常,随后进行访问:http://localhost:8091/order/message。
@RequestMapping("/order/message")
public String message3(){
return orderServiceImpl2.message("suibianla");
}
可见,已经捕获了我们的sentinel中的异常,那如果是业务逻辑异常呢?
可以将业务处理代码改为:
其余代码不变。然后继续访问。
可见fallback已经捕获到了业务逻辑异常。
这个时候可能有人发现了问题,一个方法就要写一个blockHandler异常和一个fallback异常。这样肯定是不可取的。所以Sentinel给我们提供了异常处理类,这样就不用每一个方法都去这样写了。就是将之前定义的两个异常方法抽取出来(方法必须变成静态的)。
@Slf4j
public class OrderBlockException {
/**
* 当前方法的返回值必须和原方法一致
* 允许在参数列表后面最后加入一个参数BlockException,用来接收原方法中发生的异常
* @return
*/
public static String blockHandler(String name, BlockException e){
//自定义异常处理逻辑
log.info("触发了BlockException异常");
return "触发了BlockException异常";
}
}
@Slf4j
public class OrderFallbackException {
/**
* 当前方法的返回值必须和原方法一致
* 允许在参数列表后面最后加入一个参数BlockException,用来接收原方法中发生的异常
* @return
*/
public static String fallback(String name, Throwable e){
//自定义异常处理逻辑
log.info("触发了Throwable异常");
return "触发了Throwable异常";
}
}
然后业务方法变为一下就可完成之前的功能:
@SentinelResource(value = "message",
blockHandlerClass = OrderBlockException.class,
blockHandler = "blockHandler",
fallbackClass = OrderFallbackException.class,
fallback = "fallback")
public String message(String name) {
return "SentinelResource";
}
2.5 feign整合sentinel实现容错
1)在配置文件中开启feign对Sentinel的支持
#开启feign对sentinel的支持
feign:
sentinel:
enabled: true
2)编写容错类
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Author: liubujun
* @Date: 2022/5/8 19:47
*/
//一旦feign远程调用出现问题,就会进入当前类中同名方法,执行容错逻辑
@Slf4j
@Service
public class ProductServiceFallbackFacyory implements FallbackFactory<ProductService> {
@Override
public ProductService create(Throwable throwable) {
//throwable就是在feign调用过程中产生异常
return new ProductService() {
@Override
public Product findById(Integer pid) {
log.error("{}",throwable);
Product product = new Product();
product.setPid(-100);
product.setPname("商品微服务出现异常,进入微服务当中");
return product;
}
};
}
}
3)在feign调用过程中声明
4)调用(若想模拟feign调用异常,只需关闭商品微服务即可)
错误异常控制台已经打印出来: