Version: Next

Sentinel 服务熔断

Sentinel 整合 RibbonOpenFeign

环境准备

准备——新建一些模块

  • 两个服务提供者 provider-payment90039004
  • 一个服务消费者 consumer-order84
  • 84 通过 Nacos 找到 90039004 的真实地址,通过 Ribbon 负载均衡的进行访问

Cloudalibaba-provider-payment9003

<dependencies>
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Cloudalibaba-provider-payment9004

  • 照着 9003 弄一个,改一下端口号

Cloudalibaba-consumer-nacos-order84

<dependencies>
<!-- SpringCloud ailibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud ailibaba sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

服务熔断无配置

  • 重启 90039004
  • 启动 84
  • 访问 http://localhost:84/consumer/fallback/1,得到正常响应,且可以看到 90039004 两个节点在轮询负载均衡
  • 再看 order84 Controller 中的业务代码
    • id123 时,能到 90039004 查到数据正常返回
    • 当为 4 抛出一个 非法参数异常
    • 如果是 1、2、3、4 以外的数字,会跑去 90039004 查,然后啥也查不到,得到的响应中 data 为空,于是 84 自己抛出一个 空指针异常
    • 上述两个异常都是 Java 层面的,后续就是演示用 fallback 属性来处理它们
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback",fallback ="handlerFallback")
// 异常忽略 exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
// @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}

服务熔断只配置 fallback

@SentinelResource 加上 fallback 属性

  • fallback 属性指定一个兜底方法
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
@SentinelResource(value = "fallback", fallback = "handlerFallback")
// 异常忽略 exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
// @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
// 兜底方法
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "异常handlerFallback,exception内容: " + e.getMessage(), payment);
}
  • 访问 http://localhost:84/consumer/fallback/4,触发了兜底方法,打印了自定义异常信息,屏蔽了 500 错误及异常信息栈
  • 访问 http://localhost:84/consumer/fallback/5,触发了兜底方法,打印了自定义异常信息,屏蔽了 500 错误及异常信息栈
{
"code": 444,
"message": "异常handlerFallback,exception内容: IllegalArgument ,非法参数异常...",
"data": {
"id": 4,
"serial": "null"
}
}

服务熔断只配置 blockHandler

@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback", fallback = "handlerFallback")
// 异常忽略 exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
@SentinelResource(value = "fallback", blockHandler = "blockHandler")
// @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult blockHandler(@PathVariable Long id, BlockException e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "blockHandler-sentinel 限流,BlockException: " + e.getMessage(), payment);
}

添加 Sentinel 配置

  • 给资源名 fallback 添加降级规则

测试

  • 访问 http://localhost:84/consumer/fallback/4
  • Java 异常会直接以 500 报文响应
  • 疯狂访问,触发降级,得到 blockHandler 方法执行后的响应结果

服务熔断两个都配

@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback", fallback = "handlerFallback")
// 异常忽略 exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
// @SentinelResource(value = "fallback", blockHandler = "blockHandler")
@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "异常handlerFallback,exception内容: " + e.getMessage(), payment);
}
public CommonResult blockHandler(@PathVariable Long id, BlockException e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "blockHandler-sentinel 限流,BlockException: " + e.getMessage(), payment);
}
  • Java 的错误会触发 fallback
  • 违反 Sentinel 设置会触发 blockHandler

如果一个请求把两个都触发了会发生什么?

  • 执行的是 blockHandler,因为 Sentinel 的限流直接阻止了方法的执行,根本执行不到 fallback

异常忽略

使用:exceptionsToTrace 属性指定要忽略的异常类型

  • exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback")
// @SentinelResource(value = "fallback", fallback = "handlerFallback")
// 异常忽略 exceptionsToTrace = IllegalArgumentException.class 忽略指定的异常
// @SentinelResource(value = "fallback", blockHandler = "blockHandler")
// @SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler")
@SentinelResource(value = "fallback", fallback = "handlerFallback", blockHandler = "blockHandler",exceptionsToIgnore = IllegalArgumentException.class)
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("IllegalArgument ,非法参数异常...");
} else if (result.getData() == null) {
throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "异常handlerFallback,exception内容: " + e.getMessage(), payment);
}
public CommonResult blockHandler(@PathVariable Long id, BlockException e) {
Payment payment = new Payment(id, "null");
return new CommonResult(444, "blockHandler-sentinel 限流,BlockException: " + e.getMessage(), payment);
}

整合 OpenFeign

  • 修改 order84
  • 引入 OpenFeign ,之前引入过了
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

测试

  • 只启动 9003
  • 重启 84
  • 访问 http://localhost:84/consumer/paymentSQL/1,可以得到正常响应,来自 9003

测试服务降级

  • 9003 关了,继续访问,当调用到 9003 时,因为 9003 挂了,OpenFeign 的调用就会出问题
  • 应当触发定义的自定义兜底类
{
"code": 444,
"message": "服务降级返回,---PaymentFallbackService",
"data": {
"id": 1,
"serial": "ErrorSerial"
}
}