
Spring Cloud Kubernetes微服务实战与源码分析 专栏收录该内容,点击查看专栏更多内容原创 吴就业 351 0 2020-07-10
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/dc14a4b1b14d421f965cb92aabc307d0
作者:吴就业
链接:https://wujiuye.com/article/dc14a4b1b14d421f965cb92aabc307d0
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
本篇文章写于2020年07月10日,从公众号|掘金|CSDN手工同步过来(博客搬家),本篇为原创文章。

常用于实现熔断降级的框架有Hystrix、Sentinel,我们常说的Spring Cloud项目说的其实是Spring Cloud Netflix,Hystrix以及前面学习过的Ribbon都是Netflix系的家族成员,所以使用Hystrix可以非常简单的与当前项目中使用到的OpenFeign、Ribbon整合,但笔者并没有选择Hystrix,而是选择阿里系的Sentinel。
为什么选择Sentinel而不是Hystrix?从接入简单考虑可能选择Hystrix是不错的选择,但笔者对Hystrix比较陌生,陌生到只听过名字。笔记在去年做的Dubbo项目中使用过Sentinel,所以对Sentinel很熟悉,并且看过它的一点底层源码,例如,如何统计当前时间窗口的QPS、计算并发使用的线程数等。所以笔者选择的是自己熟悉的。使用Sentinel有一个好处,文档是中文的,使用文档介绍的也比较齐全,对入门较友好。
按照惯性,本篇先介绍如何将Sentinel与OpenFeign整合使用,并且熔断降级策略使用动态配置,将配置存储在配置中心。
sck-demo项目源码地址:sck-demo (这是一个使用Spring Cloud Kubernetes搭建的微服务demo工程,可翻阅往期文章学习Spring Cloud Kubernetes的使用以及源码分析)。
Sentinel官方使用文档:Sentinel wiki
借助spring-cloud-starter-alibaba-sentinel实现与OpenFeign整合,下一篇分析完源码之后,我们也可以自己实现一个spring-cloud-starter-kubernetes-sentinel(滑稽)。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
引入该依赖也会将sentinel-core以及sentinel-datasource-extension引入,实际上实现熔断降级我们只需要这两个jar包就够了,当然,前提是自己实现与OpenFeign的整合。而spring-cloud-starter-alibaba-sentinel提供了Sentinel与OpenFeign整合使用的实现。
熔断是在consumer端实现的,所以在consumer端的application.yaml配置文件中添加如下配置。
feign:
sentinel:
enabled: true
通过继承AbstractDataSource实现自定义数据源,降级规则以什么格式存储由你来定,由于sck-demo项目使用Kubernetes的ConfigMap资源存储动态配置,所以笔者选择使用yaml格式配置降级规则。
创建用于装载降级规则配置的Properties类DegradeRuleProps,代码如下。
@Data
@ToString
@Component
@RefreshScope
@ConditionalOnClass(DegradeRule.class)
@ConfigurationProperties(prefix = "feign.sentinel.degrade")
public class DegradeRuleProps {
/**
* 是否启用熔断降级功能
*/
private boolean enable = true;
/**
* 熔断规则
*/
private List<DegradeRule> rules;
@Data
@ToString
public static class DegradeRule {
private String resource;
private Integer count;
private Integer timeWindowSecond;
// 1、DEGRADE_GRADE_EXCEPTION_COUNT = 2
// 2、DEGRADE_GRADE_EXCEPTION_RATIO = 1
// 3、DEGRADE_GRADE_RT = 0
private Integer degradeGrad = RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT;
/**
* 可触发RT率断路的最小连续请求数(使用DEGRADE_GRADE_RT时必须,不然一个请求就百分百)
*/
private int rtSlowRequestAmount = 5;
/**
* 可触发断路的最小请求数(在活动统计时间timeWindowSecond范围内)。
*/
private Integer minRequestAmount = 5;
public static boolean checkNotNull(DegradeRule rule) {
return rule != null && rule.count > 0
&& rule.timeWindowSecond > 0
&& !StringUtils.isEmpty(rule.resource);
}
}
}
自定义降级规则数据源DegradeRuleDataSource实现代码如下。

动态数据源只是接口,实现了动态数据源并不意味着Sentinel会帮你实现初始化加载或者更新降级规则。所以我们在Bean的初始化方法中初始化加载一次降级规则,并实现ApplicationListener接口监听RefreshScopeRefreshedEvent动态配置刷新事件,当配置改变时,重新加载降级规则。
让降级规则生效只需要调用DegradeRuleManager的loadRules方法,让Sentinel更新降级规则DegradeRule。如果不配置任何降级规则,则不会对任何资源降级。
编写自动配置类SentinelAutoConfiguration,当feign.sentinel.enabled开启时,也就是启动OpenFeign整合Sentinel的自动配置时,再将数据源注册到Spring容器中。最好将数据源写在一个组件模块中,实现代码共用。
@Configuration
@ConditionalOnClass(DegradeRuleManager.class)
@ConditionalOnProperty(value = "feign.sentinel.enabled", havingValue = "true")
@Import({DegradeRuleDataSource.class})
@EnableConfigurationProperties({DegradeRuleProps.class})
public class SentinelAutoConfiguration {
}
在resources目录下创建一个META-INF目录,添加spring.factories文件,在文件中添加如下内容。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
com.wujiuye.sck.common.sentinel.SentinelAutoConfiguration
本地测试不使用配置中心,直接在application-[activeProfile].yaml文件中添加降级规则,例如。
feign:
sentinel:
degrade:
rules:
- resource: POST:http://localhost:8080/v1/demo1
count: 10
degradeGrad: 2
timeWindowSecond: 1
minRequestAmount: 5
- resource: POST:http://localhost:8080/v1/demo2
count: 10
degradeGrad: 2
timeWindowSecond: 1
minRequestAmount: 5
测试例子配置两个熔断降级规则,resource资源名称配置格式为:(请求方式)+“:”+资源url。以资源POST:http://localhost:8080/v1/demo1为例,该资源配置使用的降级规则为DEGRADE_GRADE_EXCEPTION_COUNT(对应值为2),最小失败总数为10,时间窗口为1秒,最小请求数为5。意思是,如果当前时间窗口内(1秒内)调用接口的总数大于5,并且出现异常总数大于10时,接下来的请求将触发熔断,直到下个时间周期到来重新计算。
笔者去年抄过Sentinel的时间窗口请求数据统计的源码,通过修改实现通用QPS统计工具包qps-helper,Github下载地址:qps-helper。感兴趣可以看下。
最后给接口上的@FeignClient注解配置fallback属性,配置降级处理。fallback属性要求配置一个类,该类必须实现相同的接口。
@FeignClient(name = ProviderConstant.SERVICE_NAME,
path = "/v1",
url = "${fegin-client.sck-demo-provider-url}",
primary = false,
configuration = {DefaultFeignConfig.class,
DefaultFeignRetryConfig.class,
RequestInterceptorConfig.class},
// 这里配置(1)
fallback = ServiceDegradeFallback.class)
public interface DemoService {
@PostMapping("/services")
ListGenericResponse<DemoDto> getServices();
}
ServiceDegradeFallback类中处理降级逻辑,例如,响应一个状态码告知服务降级接口调用失败。
public class ServiceDegradeFallback implements DemoService {
@Override
public ListGenericResponse<DemoDto> getServices() {
ListGenericResponse response = new ListGenericResponse<DemoDto>();
response.setCode(ResultCode.SERVICE_DEGRAD.getCode())
.setMessage("服务降级");
return response;
}
}
我们还需要将该ServiceDegradeFallback注册到Feign的Clinet环境隔离的容器中。编写配置类SentinelFeignConfig,将SentinelFeignConfig添加到@FeignClient注解的configuration属性。
@FeignClient(name = ProviderConstant.SERVICE_NAME,
path = "/v1",
url = "${fegin-client.sck-demo-provider-url}",
primary = false,
configuration = {DefaultFeignConfig.class,
DefaultFeignRetryConfig.class,
RequestInterceptorConfig.class,
// 这里配置(2)
SentinelFeignConfig.class
},
// 这里配置(1)
fallback = ServiceDegradeFallback.class)
public interface DemoService {
@PostMapping("/services")
ListGenericResponse<DemoDto> getServices();
}
在SentinelFeignConfig中注册ServiceDegradeFallback。
public class SentinelFeignConfig {
@Bean
public ServiceDegradeFallback degradeMockYcpayService() {
return new ServiceDegradeFallback();
}
}
当满足熔断条件时,Sentinel会抛出一个DegradeException异常,如果配置了fallback,那么Sentinel会从Bean工厂中根据取fallback属性配置的类型取一个Bean调用相同的方法。
但有一点不足的地方,我们在ServiceDegradeFallback中无法获取异常的类型,无法判断是否是熔断降级,只能把所有情况都当初熔断降级处理。
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

我们基于Spring Cloud Gateway开发内部微服务网关,并结合注册中心实现自动服务发现路由。就在最近将项目部署测试环境的Kubernetes集群上时,发现路由失败。
本篇我们继续通过了解Spring Cloud Kubernetes实现动态加载配置接口来理解Spring Cloud动态配置实现的整个流程。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。