Sentinel是面向分布式服务架构的轻量级流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
一、主要概念
资源:它可以是Java应用程序中的任何内容,例如,由应用程序提供的服务,或者由应用程序调用的其他应用提供的服务,甚至可以是一段代码。只要通过Sentinel API定义的代码,就是资源,能够被Sentinel保护起来。在大部分情况下,可以使用方法签名、URL甚至服务名作为资源名来标识资源。
规则:围绕资源的实时状态设定的规则,包括流量控制规则、熔断降级规则和系统保护规则。所有规则可以动态实时调整。
Sentinel的主要功能包括流量控制、熔断降级、系统负载保护。
熔断降级:Sentinel的熔断降级功能目的就是在分布式系统中某个依赖服务出现问题的时候,不要让问题蔓延到整个系统。但是Sentinel采取两种手段来实现这个目标--限制并发线程数;通过响应时间对资源访问进行降级。
流量控制:流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。Sentinel作为一个调配器,可以根据需要把随机的请求调整成合适的形状。
系统负载保护:当系统负载较高的时候,如果还持续让请求进入,则可能导致系统崩溃、无法响应。Sentinel提供了对应的保护机制,让系统的入口流量和系统的负载达到平衡,保证系统在能力范围之内处理最多的请求。
二、用法示例
Sentinel有详尽的官方文档,这里就简单的介绍一个场景,1.需要将流量控制在QPS=5;2.在服务超时的情况下,返回默认的值。
2.1 定义资源
public class SentinelKey {
public static final String breakKey = "sentinel-break-flow-qps";
public static final String degradeKey = "sentinel-degrade";
}
2.2 定义规则类
@Component
public class SentinelConfig {
@PostConstruct
public void init() {
//qps
initFlowQpsRule();
//degrade
initDegradeRule();
}
public void initFlowQpsRule() {
List rules = new ArrayList();
FlowRule rule1 = new FlowRule();
rule1.setResource(SentinelKey.breakKey);
// set limit qps to 5
rule1.setCount(5);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
public void initDegradeRule() {
List rules = new ArrayList();
DegradeRule rule = new DegradeRule();
rule.setResource(SentinelKey.degradeKey);
// set threshold rt, 10 ms
rule.setCount(10);
rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
//时间窗口,接下来的10s都会自动熔断
rule.setTimeWindow(10);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
}
这里面加载了两个规则,一个是限流的规则,一个是降级规则,限流比较容易理解,看一下熔断降级。
降级分为三个策略:平均响应时间 (DEGRADE_GRADE_RT
) | 异常比例 (DEGRADE_GRADE_EXCEPTION_RATIO
) | 异常数 (DEGRADE_GRADE_EXCEPTION_COUNT
)。
本文用的平均响应时间,当资源的平均响应时间超过阈值(DegradeRule
中的 count
,以 ms 为单位)之后,资源进入准降级状态。如果接下来 1s 内持续进入 5 个请求(即 QPS >= 5),它们的 RT 都持续超过这个阈值,那么在接下的时间窗口(DegradeRule
中的 timeWindow
,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException
)。
2.3 接口类
package com.csdn.net.flow.limit.controller;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.csdn.net.flow.limit.sentinel.SentinelKey;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/flow/limit")
public class FlowLimitController {
@RequestMapping(value = "/sentinel", method = {RequestMethod.GET})
public String testSentinelFlowLimit() {
Entry entry = null;
try {
entry = SphU.entry(SentinelKey.breakKey);
} catch (BlockException e1) {
System.out.println("BlockException e1 = " + e1.getMessage());
return "blocked thread name = " + Thread.currentThread().getName();
} catch (Exception e2) {
System.out.println("Exception e2 = " + e2.getMessage());
Tracer.trace(e2);
} finally {
if (entry != null) {
entry.exit();
}
}
return "ok thread name = " + Thread.currentThread().getName();
}
@RequestMapping(value = "/sentinel/degrade", method = {RequestMethod.GET})
public String testSentinelDegrade() {
Entry entry = null;
try {
entry = SphU.entry(SentinelKey.degradeKey);
//超时模拟
Thread.sleep(20);
} catch (Throwable t) {
if (!BlockException.isBlockException(t)) {
Tracer.trace(t);
}
System.out.println("Throwable t = " + t.getMessage());
return "degrade thread name = " + Thread.currentThread().getName();
} finally {
if (entry != null) {
entry.exit();
}
}
return "ok thread name = " + Thread.currentThread().getName();
}
}
在降级中超时模拟20ms,在降级的规则中定义的是10ms。
2.4 测试类
import com.alibaba.fastjson.JSONObject;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SentinelDegradeTest {
@Test
public void testFlowLimitParallel() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(100);
String url = "http://localhost:8080/flow/limit/sentinel";
for(int i = 0; i < 10; i++) {
executorService.submit(new ReqTask(url));
}
executorService.awaitTermination(100000, TimeUnit.SECONDS);
}
@Test
public void testDegradeParallel() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(100);
String url = "http://localhost:8080/flow/limit/sentinel/degrade";
for(int i = 0; i < 10; i++) {
executorService.submit(new ReqTask(url));
}
Thread.sleep(900);
System.out.println("============================================");
for(int i = 0; i < 20; i++) {
executorService.submit(new ReqTask(url));
}
executorService.awaitTermination(100000, TimeUnit.SECONDS);
}
}
class ReqTask implements Runnable {
private String url;
public ReqTask(String url) {
this.url = url;
}
@Override
public void run() {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity resp = restTemplate.getForEntity(url, String.class);
System.out.println(resp.getBody());
}
}
测试结果:
限流:
ok thread name = http-nio-8080-exec-10
ok thread name = http-nio-8080-exec-9
ok thread name = http-nio-8080-exec-1
ok thread name = http-nio-8080-exec-2
ok thread name = http-nio-8080-exec-3
ok thread name = http-nio-8080-exec-11
blocked thread name = http-nio-8080-exec-6
blocked thread name = http-nio-8080-exec-4
blocked thread name = http-nio-8080-exec-5
blocked thread name = http-nio-8080-exec-7
降级:
ok thread name = http-nio-8080-exec-11
ok thread name = http-nio-8080-exec-5
ok thread name = http-nio-8080-exec-4
ok thread name = http-nio-8080-exec-6
ok thread name = http-nio-8080-exec-8
ok thread name = http-nio-8080-exec-12
ok thread name = http-nio-8080-exec-2
ok thread name = http-nio-8080-exec-9
ok thread name = http-nio-8080-exec-13
ok thread name = http-nio-8080-exec-7
============================================
degrade thread name = http-nio-8080-exec-4
ok thread name = http-nio-8080-exec-6
ok thread name = http-nio-8080-exec-8
ok thread name = http-nio-8080-exec-2
ok thread name = http-nio-8080-exec-12
degrade thread name = http-nio-8080-exec-4
degrade thread name = http-nio-8080-exec-1
degrade thread name = http-nio-8080-exec-8
degrade thread name = http-nio-8080-exec-9
degrade thread name = http-nio-8080-exec-8
degrade thread name = http-nio-8080-exec-2
degrade thread name = http-nio-8080-exec-11
degrade thread name = http-nio-8080-exec-8
degrade thread name = http-nio-8080-exec-1
degrade thread name = http-nio-8080-exec-9
degrade thread name = http-nio-8080-exec-4
degrade thread name = http-nio-8080-exec-6
degrade thread name = http-nio-8080-exec-7
degrade thread name = http-nio-8080-exec-12
degrade thread name = http-nio-8080-exec-2
Author:忆之独秀
Email:[email protected]
注明出处:https://blog.csdn.net/lavorange/article/details/95781752