主要介绍sentinel的限流熔断如何使用,如何与api网关进行整合,以及其dashboard的使用等;
sentinel 地址 https://github.com/alibaba/Sentinel
sentinel dashboard地址 https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard
sentinel wiki地址 https://github.com/alibaba/Sentinel/wikijava
<properties>
<sentinel.version>1.6.1</sentinel.version>
</properties>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>${sentinel.version}</version>
</dependency>
复制代码
限流详细文档地址:github.com/alibaba/Sen…git
下面举个简单的使用示例:
github
/** * 基于QPS的资源限流 * @param name -- 资源名称 * @param count -- 数量 */
public static void flowRule(String name, int count) {
//限流规则,能够多个flow rule,该规则支持QPS和并发线程数的限流
//FlowRuleManager.getRules()能够获取到已经设置的限流规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置资源名称,sentinel限流都是以资源为单位进行
rule.setResource(name);
//使用QPS限流
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//QPS达到的数量阈值
rule.setCount(count);
rules.add(rule);
//从新加载限流规则,此处将覆盖原有的限流,因此若是想要不覆盖
//请使用FlowRuleManager.getRules()获取到的加入到rules中
FlowRuleManager.loadRules(rules);
}
复制代码
public static void main(String[] args) {
//执行限流
flow("sayHello", 3);
}
public static void flow(String resourceName, int count) {
// 配置规则,每秒能够过3个hello world的请求
flowRule(resourceName, count);
while (true) {
for (int i = 0; i <= count; i++) {
//entry这里是关键逻辑,它会基于滑动时间窗口来进行限流统计;
//因此此处才是限流的关键
try (Entry entry = SphU.entry(resourceName)) {
sayHello("" + (int)entry.getCurNode().successQps());
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
sleepSeconds(1);
}
}
//须要限流的方法
public static void sayHello(String info) {
System.out.println("hello world! before success count = " + info);
}
//按秒睡眠
private static void sleepSeconds(long time) {
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
复制代码
运行上面的代码后,输出结果以下(从结果能够发现,程序以恒定每秒4次来调用被限流的方法,而配置中方法能支持的QPS为3次,因此每次调用都会有一次被保护,从而走限流分支,在限流分支中能够写本身的业务逻辑):spring
hello world! before success count = 0
hello world! before success count = 1
hello world! before success count = 2
blocked!
hello world! before success count = 0
hello world! before success count = 1
hello world! before success count = 2
blocked!
hello world! before success count = 0
hello world! before success count = 1
hello world! before success count = 2api
/** * 熔断降级规则配置 * @param name -- 资源名称 * @param count -- 数量 */
public static void degradeRule(String name, int count) {
//降级规则,能够多个degradeRule rule
//DegradeRuleManager.getRules()能够获取到已经设置的降级规则
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
//设置资源名称,sentinel降级都是以资源为单位进行
rule.setResource(name);
//使用异常统计降级,分钟统计,滑动时间窗口
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//异常数达到的数量阈值
rule.setCount(count);
//秒级时间窗口,该值必须有且必须大于零,不然降级将没法生效
rule.setTimeWindow(10);
rules.add(rule);
//从新加载限流规则,此处将覆盖原有的限流,因此若是想要不覆盖
//请使用DegradeRuleManager.getRules()获取到的加入到rules中
DegradeRuleManager.loadRules(rules);
}
复制代码
限流代码中的main调用degrade方法便可springboot
public static void degrade(String resourceName, int count) {
// 配置熔断规则,发生count个异常即将进行熔断
degradeRule(resourceName, count * 2);
int sum = 0;
while (true) {
for (int i = 0; i <= count; i++) {
//因此此处才是限流的关键
Entry entry = null;
try {
entry = SphU.entry(resourceName);
sayHello(++sum + "");
} catch (BlockException e) {
System.out.println("被降级了:" + e);
} catch (Exception ex) {
// 处理业务异常,调用tracer才能统计异常数
Tracer.trace(ex);
} finally {
if (entry != null) {
entry.exit();
}
}
}
System.out.println("===============分割线================");
sleepSeconds(1);
}
}
public static void sayHello(String info) {
System.out.println("hello world! before success count = " + info);
throw new RuntimeException("test degrade");
}
复制代码
运行代码后得到结果以下(在一分钟的滑动时间窗口下,异常超过6次则进行降级处理,直到时间窗口滑到小于6次异常才会继续被调用):并发
hello world! before success count = 1
hello world! before success count = 2
hello world! before success count = 3
hello world! before success count = 4
===============分割线================
hello world! before success count = 5
hello world! before success count = 6
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
===============分割线================
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
被降级了:com.alibaba.csp.sentinel.slots.block.degrade.DegradeException
maven
注解文档地址:github.com/alibaba/Sen…
须要依赖springaop,使用springboot项目集成便可,前面的maven依赖不变,新增maven依赖以下:ide
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>${sentinel.version}</version>
</dependency>
复制代码
简单展现使用注解方式来进行限流降级(能够动态调整限流方案,也能够全局设置降级和限流处理类等)函数
待代用的测试服务:
其中fallback函数必须和原函数参数一致,handler函数能够增长一个异常的参数,其余参数保持不变。
@Service
public class SentinelAnnotationService {
@SentinelResource(value = "helloService", blockHandler = "myHandler", fallback = "myFallback")
public String sayHello(String name) {
if (System.currentTimeMillis() % 2 == 0) {
throw new RuntimeException("test");
}
return "return hello " + name;
}
public String myHandler(String name, BlockException ex) {
return "return handler " + name;
}
public String myFallback(String name) {
return "return fallback " + name;
}
}
复制代码
@SpringBootApplication
public class Run {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Run.class, args);
test(context.getBean(SentinelAnnotationService.class));
}
/** * 每10秒访问5次以上次 */
private static void test(SentinelAnnotationService testService) {
//限流规则生成中
AddRule.flowRule("helloService", 3);
int sum = 10, total = sum;
while (sum-- > 0) {
for (int i = 1; i <= 5; i++) {
String result = testService.sayHello("littlehow" + i);
System.out.println("the " + (total - sum) + " result---->" + result);
}
sleepSeconds(10);
}
}
//按秒睡眠
private static void sleepSeconds(long time) {
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** * 这里很重要,这里是sentinel实现注解限流降级最关键的地方 * @return */
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
复制代码
执行结果的前三行输出是不肯定的,由于异常是随机抛出的,几率为50%,抛出异常后,将执行fallback,后两行的输出证实每秒限流3次效果达到了;
the 1 result---->return hello littlehow1
the 1 result---->return hello littlehow2
the 1 result---->return fallback littlehow3
the 1 result---->return handler littlehow4
the 1 result---->return handler littlehow5
与api网关集成文档地址:github.com/alibaba/Sen…
由于当前springboot与springcloud版本为1.x,因此暂时集成不了spring cloud gateway;之后版本升级后能够考虑进行集成;
待续...
目前sentinel官方还没实现与zuul2.x集成
dashboard的使用文档地址:github.com/alibaba/Sen…
dashboard的生产实践地址:github.com/alibaba/Sen…
sentinel提供了基于zk、apollo等配置中心来管理规则,基于apollo的规则须要项目内部写入本身的namespace,因此基于sentinel的扩展,自定义规则管理;
tips:也能够直接基于RuleManage进行改变
自定义规则改变不须要添加任何其余依赖,核心是sentinel提供的datasource接口,实现以下:
@Slf4j
public abstract class CommonDataSource<T> extends AbstractDataSource<String, List<T>> {
private String typeName;
public CommonDataSource(RuleConverter<T> ruleConverter, String typeName) {
super(ruleConverter);
this.typeName = typeName;
}
@Override
public void close() throws Exception {
log.info("data source close");
}
/** * 修改规则 * @throws Exception */
public void updateRules() {
String rules = null;
try {
//加载改变后的规则值
rules = this.readSource();
log.info("update rules {}, typeName={}", rules, typeName);
//将改变写入RuleManage中
this.getProperty().updateValue(this.parser.convert(rules));
} catch (Exception e) {
log.error("update rules {} fail, typeName={}", rules, typeName, e);
}
}
}
复制代码
abstract class RuleConverter<T> implements Converter<String, List<T>> {
private static final List EMPTY_LIST = new ArrayList<>(0);
/** * 判断是否为空 * @param value */
protected boolean isEmpty(String value) {
return null == value || "".equals(value) || "0".equals(value);
}
/** * 建立flow规则 * @param info * @return */
protected List<FlowRule> createFlowRule(String info) {
if (isEmpty(info)) {
return EMPTY_LIST;
}
List<FlowRule> flowRules = new ArrayList<>();
//解析出错将异常抛往上层,只简单配置resourceName,grade,count
for (String s : getRulesInfo(info)) {
String[] ruleInfo = getRuleInfo(s);
FlowRule rule = new FlowRule(ruleInfo[0].trim())
.setGrade(Integer.parseInt(ruleInfo[1]))
.setCount(Double.parseDouble(ruleInfo[2]));
flowRules.add(rule);
}
return flowRules;
}
protected List<DegradeRule> createDegradeRule(String info) {
if (isEmpty(info)) {
return EMPTY_LIST;
}
List<DegradeRule> degradeRules = new ArrayList<>();
//解析出错将异常抛往上层,只简单配置resourceName,grade,count,timeWindow
for (String s : getRulesInfo(info)) {
String[] ruleInfo = getRuleInfo(s);
DegradeRule rule = new DegradeRule(ruleInfo[0].trim())
.setGrade(Integer.parseInt(ruleInfo[1]))
.setCount(Double.parseDouble(ruleInfo[2]))
.setTimeWindow(Integer.parseInt(ruleInfo[3]));
degradeRules.add(rule);
}
return degradeRules;
}
private String[] getRulesInfo(String info) {
return info.trim().split("#");
}
private String[] getRuleInfo(String info) {
return info.trim().split(",");
}
}
复制代码
@Slf4j
public class FlowRuleConverter extends RuleConverter<FlowRule> {
@Override
public List<FlowRule> convert(String s) {
log.info("update flow rule to {}", s);
return createFlowRule(s);
}
public static FlowRuleConverter create() {
return new FlowRuleConverter();
}
}
复制代码
@Configuration
public class SentinelRuleConfig {
private static final String RULE_FLOW_KEY = "flow";
private static final String RULE_DEGRADE_KEY = "degrade";
private Map<String, String> rulesConfig = new HashMap<>();
private CommonDataSource<FlowRule> flowDataSource;
private CommonDataSource<DegradeRule> degradeDataSource;
public SentinelRuleConfig() {
super();
init();
}
/** * 多个规则用;隔开,一个规则内部用,隔开 * 顺序为resourceName,grade,count * 如helloService,1,3;sayBye,0,5 * grade参考以下 * @see com.alibaba.csp.sentinel.slots.block.RuleConstant * @param rules */
@Value("${rule.flow:helloService,1,3}")
private void setFlowRules(String rules) {
rulesConfig.put(RULE_FLOW_KEY, rules);
flowDataSource.updateRules();
}
/** * 多个规则用;隔开,一个规则内部用,隔开 * 顺序为resourceName,grade,count,timeWindow * 如helloService,1,3,10;sayBye,0,5,10 * grade参考以下 * @see com.alibaba.csp.sentinel.slots.block.RuleConstant * @param rules */
@Value("${rule.degrade:}")
private void setDegradeRules(String rules) {
rulesConfig.put(RULE_DEGRADE_KEY, rules);
degradeDataSource.updateRules();
}
public void init() {
flowDataSource = new CommonDataSource<FlowRule>(FlowRuleConverter.create(), RULE_FLOW_KEY) {
@Override
public String readSource() {
return rulesConfig.get(RULE_FLOW_KEY);
}
};
//将新的数据源配置注入到ruleManager
FlowRuleManager.register2Property(flowDataSource.getProperty());
degradeDataSource = new CommonDataSource<DegradeRule>(DegradeRuleConverter.create(), RULE_DEGRADE_KEY) {
@Override
public String readSource() {
return rulesConfig.get(RULE_DEGRADE_KEY);
}
};
DegradeRuleManager.register2Property(degradeDataSource.getProperty());
}
}
复制代码
apollo只需配置相应的rule.flow和rule.degrade的值便可