书接上篇 微服务守护神-Sentinel-热点-授权-系统规则 ,上面介绍了Sentinel热点、授权、系统规则,本篇继续来Sentinel 剩下其他操作
当触发sentinel流控规则之后, sentinel就干巴巴返回异常信息,单纯的文字输出
而当前大部分项目都是前后端分离,接口设计基本采用restful的设计风格,返回都是json格式数据,此时怎办?sentinel其实也考虑过这种情况,允许顽疾自定义异常返回。
FlowException 限流异常
DegradeException 降级异常
ParamFlowException 参数限流异常
AuthorityException 授权异常
SystemBlockException 系统负载异常
sentinel默认给出以上5种异常,并提供BlockExceptionHandler 接口允许用户自定义
需求:自定义流控异常返回
package cn.wolfcode.error;
@Component
public class ExceptionHandlerPage implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
response.setContentType("application/json;charset=utf-8");
ResultData data = null;
if (e instanceof FlowException) {
data = new ResultData(-1, "接口被限流了");
} else if (e instanceof DegradeException) {
data = new ResultData(-2, "接口被降级了");
}else if (e instanceof ParamFlowException) {
data = new ResultData(-3, "参数限流异常");
}else if (e instanceof AuthorityException) {
data = new ResultData(-4, "授权异常");
}else if (e instanceof SystemBlockException) {
data = new ResultData(-5, "系统负载异常了...");
}
response.getWriter().write(JSON.toJSONString(data));
}
}
@Data
@AllArgsConstructor//全参构造
@NoArgsConstructor//无参构造
class ResultData {
private int code;
private String message;
}
简单明了,直接实现BlockExceptionHandler接口,重写handle 方法即可。后续测试,触发不同类型流程跳转到不同异常,输出指定格式数据。
前面项目中所有触发流控规则之后,我们的解决方案都是抛出异常,提示信息。但是开发中没有那么简单的逻辑,很多时候多需要围绕这个异常做一些后续操作。最简单例子:当触发熔断时,要走降级方法,而不是简单抛出异常。如果想实现这种操作该怎么玩?此时就得用上sentinel提供@SentinelResource的功能了。
@SentinelResource 注解作用有2个
1>定义资源,标记资源
能将普通的方法标记成一个资源,进而可以给这个资源加上流控规则。
@Service
@Slf4j
public class TraceServiceImpl {
@SentinelResource(value = "tranceService")
public void tranceService(){
log.info("调用tranceService方法");
}
}
2>定制规则
如果说前面使用可视化控制台配置流控规则,那么使用@SentinelResource注解就可以编码方式配置规则。
@SentinelResource 注解常用属性
属性 | 作用 |
---|---|
value | 资源名称,必需项(不能为空) |
entryType | entry 类型,可选项(默认为 EntryType.OUT ) |
blockHandler/ blockHandlerClass |
blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public ,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException 。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
fallback/ fallbackClass |
fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求: 1. 返回值类型必须与原函数返回值类型一致; 2.方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 3.fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
defaultFallback |
默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求: 1. 返回值类型必须与原函数返回值类型一致; 2. 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。 3. defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。 |
exceptionsToIgnore |
用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 |
需求:当接口调用异常时,触发降级方法
@RestController
@Slf4j
public class AnnoController {
@RequestMapping("/anno1")
@SentinelResource(value = "anno1",
blockHandler="anno1BlockHandler",
fallback = "anno1Fallback"
)
public String anno1(String name){
if("dafei".equals(name)){
throw new RuntimeException();
}
return "anno1";
}
public String anno1BlockHandler(String name,BlockException ex){
log.error("{}", ex);
return "接口被限流或者降级了";
}
//Throwable时进入的方法
public String anno1Fallback(String name,Throwable throwable) {
log.error("{}", throwable);
return "接口发生异常了";
}
}
方法/anno1 带上name=dafei,触发异常,异常出现进入anno1Fallback 逻辑, 配置流控规则,正常方法触发流控后,跳转到anno1BlockHandler 逻辑。
使用@SentinelResource 确实达到熔断降级逻辑,但是编写方式有点麻烦,你可以理解为写一个接口需要配置2个方法,这种方式有点不优雅,这是引入Feign的编写方式啦。也就是说怎么使用Feign来优雅解决掉这种熔断降级的麻烦。
需求:订单调用商品接口,加上熔断降级逻辑
延用上几篇的案例
步骤1:在shop-order-server项目的配置文件中开启feign对Sentinel的支持
feign:
sentinel:
enabled: true
步骤2:创建容错类
@Component
public class ProductFeignService implements IProductFeginService {
@Override
public Product findByPid(Long pid) {
Product product = new Product();
product.setPid(-1L);
product.setPname("兜底数据");
product.setPprice(0.0);
return product;
}
}
步骤3:在feign接口中定义容错类
@FeignClient(name = "product-service",fallback = ProductFeignFallBack.class)
public interface IProductFeginService {
@RequestMapping("/product/{pid}")
public Product findByPid(@PathVariable("pid") Long pid);
}
步骤4:停止所有 商品服务,重启 shop-order 服务,访问请求,观察容错效果
可能上面的案例并不是特别恰当,我们只是通过案例来演示Feign集成Sentinel实现降级的效果. 接下来我们具体更贴切的案例来讲解Feign降级的作用.
比如我们在购物的时候,查看商品详情页面的时候,里面包含库存信息,商品详情信息,评论信息,这个需求包含的微服务如下:
没做降级熔断前,假设评论服务宕机了,那意味用户发出查看商品请求也无法正常显示了,原因查看商品接口需要调用评论微服务,而评论微服调不通,请求失败。那页面商品就无法显示,用户自然也无法进行下单。但是对于用户来说,评论看不到并不影响购物,所以这时候我们应该对评论服务进行及时降级处理,返回一个兜底数据(空数据),这样用户的查看商品请求能正常显示,只是评论数据看不到而已,但完全不影响用户。
到这,微服务守护神-Sentinel所有逻辑就讲完了,下一篇到哪个组件了呢?且听下回讲解。
看文字不过瘾可以切换视频版:SpringCloud Alibaba 极简入门