100:sentinel服务保护框架
1 sentinel实现动态服务限流演示
课程内容:
- 服务保护、服务限流、服务降级的概念
- 服务雪崩效应产生背景及解决方案
- sentinel配置及多种使用方式
- 实例演示sentinel限流效果
2 服务保护、服务限流、服务降级的概念
服务接口保护有哪些方案?
黑名单和白名单、对IP实现限流/熔断机制、服务降级、服务隔离机制。
服务限流:目的是为了保护服务。在高并发的情况下,如果客户端请求服务器端达到一定的极限,请求的数量超出了设置的阈值,开启自我保护机制。直接执行服务降级的方法,不会执行业务逻辑,走本地fallback方法。
服务降级:在高并发的情况下,为了防止用户一直等待,采用限流或者熔断机制保护服务,不会执行业务逻辑,走本地fallback方法,返回一个友好的提示给客户端。
比如:返回提示“当前排队人数过多,请稍后重试”。
3 服务雪崩效应产生的背景与解决方案
服务雪崩效应:默认的情况下,tomcat/jetty服务器只会有一个线程池处理所有接口的请求。这样的话在高并发的情况下,如果客户端所有的请求都堆积到同一个接口上,那么会出现该服务器所有的线程都在处理该接口,可能会导致其他的接口无法访问,短暂没有线程处理。
jmeter模拟雪崩效应场景
订单服务
@RestController
@Slf4j
public class OrderService {
@RequestMapping("/orderToMember")
public String orderToMember() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("orderToMember >>>" + Thread.currentThread().getName() + ">>>");
return "this is order";
}
@RequestMapping("/smsOrder")
public String smsOrder() {
log.info("smsOrder >>>" + Thread.currentThread().getName() + ">>>");
return "订单发送短信消息";
}
}
如何去证明tomcat服务器只有一个线程池处理所有接口的请求?
打印线程名称。线程名称组合:线程池名称+线程id名称。
服务雪崩解决方案
服务隔离机制:线程池隔离或者信号量隔离机制。
线程池隔离:每个接口都有自己独立的线程池去维护请求,每个线程池互不影响。
缺点:占用服务器内存非常大。
信号量隔离:设置最多允许某个接口有一阈值的线程数量处理接口,如果超出该线程数量,则拒绝访问。
4 sentinel中文基本的介绍
Sentinel: 分布式系统的流量防卫兵
Sentinel是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel具有以下特征:
- 丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dubbo、gRPC的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
- 完善的SPI扩展点:Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel中文文档介绍:
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
sentinel与hystrix的区别
5 sentinel手动实现限流规则
限流配置有两种方案:
1 手动使用代码配置 纯代码/注解的形式
2 Sentinel 控制台形式配置
默认情况下Sentinel不对数据持久化,需要自己独立持久化。
实现的步骤:
创建流控规则(限流规则),然后再被映射地址去引用。
maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<version>0.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
手动配置管理Api限流接口
@RestController
@Slf4j
public class OrderService {
/**
* 限流规则名称
*/
private static final String GETORDER_KEY = "orderToMember";
/**
* 限流规则
* @return
*/
@RequestMapping("/initFlowQpsRule")
public String initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(GETORDER_KEY);
// QPS控制在1,每秒钟运行1个请求
rule1.setCount(1);
// QPS限流
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
return "....限流配置初始化成功..";
}
@RequestMapping("/orderToMember")
public String orderToMember() {
Entry entry = null;
try {
entry = SphU.entry(GETORDER_KEY);
return "orderToMember接口";
} catch (Exception e) {
// 限流的情况就会进入到Exception
return "当前访问人数过多,请稍后重试!";
} finally {
// SphU.entry(xxx) 需要与 entry.exit() 成对出现,否则会导致调用链记录异常
if (entry != null) {
entry.exit();
}
}
}
}
测试效果:
项目启动自动加载
当SpringBoot项目启动成功之后,加载限流规则。
@Slf4j
@Component
public class SentinelApplicationRunner implements ApplicationRunner {
/**
* 限流规则名称
*/
private static final String GETORDER_KEY = "orderToMember";
@Override
public void run(ApplicationArguments args) throws Exception {
List<FlowRule> rules = new ArrayList<FlowRule>();
FlowRule rule1 = new FlowRule();
rule1.setResource(GETORDER_KEY);
// QPS控制在1,每秒钟运行1个请求
rule1.setCount(1);
// QPS限流
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
log.info(">>>限流配置加载成功<<<");
}
}
6 使用注解形式实现对服务接口限流
@SentinelResource参数
Value:流量规则资源名称、
BlockHandler 限流/熔断出现异常执行的方法
Fallback 服务降级执行的方法
服务降级由很多原因造成:限流、熔断、接口超时、接口出现异常等,hystrix没有把异常分开,sentinel把限流/熔断独立出来。
注解形式配置管理Api限流
@RestController
@Slf4j
public class OrderService {
/**
* 限流规则名称
*/
private static final String GETORDER_KEY = "orderToMember";
/**
* fallback 服务降级本地方法
* blockHandler 限流/熔断出现异常执行的方法
* value 指定资源的名称
* @return
*/
@SentinelResource(value = GETORDER_KEY, blockHandler = "getOrderQpsException")
@RequestMapping("/orderToMemberSentinelResource")
public String orderToMemberSentinelResource() {
return "orderToMemberSentinelResource";
}
/**
* 被限流后返回的提示
* @param e
* @return
*/
public String getOrderQpsException(BlockException e) {
e.printStackTrace();
return "该接口已经被限流啦!";
}
}
测试效果:
7 SpringBoot整合sentinel控制台
Sentinel 环境快速搭建
下载对应Sentinel-Dashboard jar包
下载地址:https://github.com/alibaba/Sentinel/releases/tag/1.7.1
运行执行命令
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar (jar包)
访问:http://127.0.0.1:8718 默认账号密码:sentinel sentinel
SpringBoot整合Sentinel仪表盘 配置
spring:
application:
## 服务名称
name: mayikt-order
cloud:
nacos:
discovery:
## nacos注册地址
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: 127.0.0.1:8718
eager: true
运行效果:
8 sentinel控制台动态实现接口限流
@RestController
@Slf4j
public class OrderService {
/**
* 被限流后返回的提示
* @param e
* @return
*/
public String getOrderQpsException(BlockException e) {
e.printStackTrace();
return "该接口已经被限流啦!";
}
/**
* 基于控制台创建规则实现限流
* 注意:如果没有使用@SentinelResource注解的情况下,默认的资源名称为接口路径地址
* @return
*/
@RequestMapping("/getOrderConsole")
public String getOrderConsole(){
return "getOrderConsole";
}
@RequestMapping("/getOrderConsole2")
@SentinelResource(value = "getOrderConsole2", blockHandler = "getOrderQpsException")
public String getOrderConsole2(){
return "getOrderConsole2";
}
}
测试效果:
9 sentinel控制台实现信号隔离
@RestController
@Slf4j
public class OrderService {
/**
* 被限流后返回的提示
* @param e
* @return
*/
public String getOrderQpsException(BlockException e) {
e.printStackTrace();
return "该接口已经被限流啦!";
}
@RequestMapping("/getOrderSemaphore")
@SentinelResource(value = "getOrderSemaphore", blockHandler = "getOrderQpsException")
public String getOrderSemaphore() {
try{
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info(">>>" + Thread.currentThread().getName());
return "getOrderSemaphore";
}
}
测试效果: