一、简介

1、概述

Sentinel是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来保障微服务的稳定性。

2、基本概念

  • 资源

资源是Sentinel的关键概念,只要通过Sentinel API定义的代码就是资源;它可以是Java应用程序中的任何内容,例如:由应用程序提供的服务或一段代码。

  • 规则

围绕资源的实时状态设定的规则,包括流量控制规则、熔断降级规则以及系统保护规则;所有规则都可以动态实时调整。

3、主要功能

  • 流量控制

任意时间到达服务器的请求往往是随机不可控的,而服务器的处理能力是有限的,因此需要根据系统的处理能力对流量进行控制。

流量控制有以下几个角度:资源的调用关系、运行指标(QPS、线程池、系统负载等)、控制的效果(直接限流、冷启动、排队等)。

  • 熔断降级

由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定状态,最终会导致请求发生堆积。例如:出现超时或异常比例升高的时候,需要对这个资源的调用进行限制,让请求快速失败,避免影响到其它资源导致最终产生雪崩的效果。

  • 系统负载保护

当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。

在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去;如果这个时候其它的机器也处在一个边缘的状态,增加的流量就会导致这台机器也崩溃,从而导致整个集群不可用。

针对这种情况,Sentinel提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。

二、Sentinel控制台

1、简介

Sentinel提供了一个轻量级的开源控制台,提供机器发现以及健康状况管理、监控(单机和集群)、规则管理和推送等功能。

2、安装启动

  • 下载

Release页面下载控制台jar包

  • 启动
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.6.jar

访问:http://localhost:8888/,需要先登录,默认用户名和密码都是sentinel,可以在启动时指定。

#指定控制台的登录用户名
-Dsentinel.dashboard.auth.username=sentinel

#指定控制台的登录密码
-Dsentinel.dashboard.auth.password=123456

#指定Spring Boot服务端session的过期时间,默认30分钟
-Dserver.servlet.session.timeout=7200

登录之后即可进入控制台首页:

三、样例

1、Hello World

  • pom.xml
<dependencies>
	<dependency>
		<groupId>com.alibaba.csp</groupId>
		<artifactId>sentinel-core</artifactId>
		<version>1.8.6</version>
	</dependency>

	<dependency>
		<groupId>com.alibaba.csp</groupId>
		<artifactId>sentinel-transport-simple-http</artifactId>
		<version>1.8.6</version>
	</dependency>

	<dependency>
		<groupId>ch.qos.logback</groupId>
		<artifactId>logback-classic</artifactId>
		<version>1.2.13</version>
	</dependency>
</dependencies>
  • 主程序
public class App {

	public static void main(String[] args) {
		
		Logger log = LoggerFactory.getLogger(App.class);
		
		initFlowRules();
		
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.SECOND, 30);
		long time = cal.getTimeInMillis();
		while(true) {
			
			try(Entry entry = SphU.entry("HelloWorld")){
				log.info("Hello World");
			} catch (BlockException e) {
				//log.error("blocked!");
			}
			
			if(System.currentTimeMillis() > time) {
				break;
			}
		}
		
	}


	private static void initFlowRules() {
		List<FlowRule> rules = new ArrayList<>();
		FlowRule rule = new FlowRule();
		rule.setResource("HelloWorld");
		rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
		rule.setCount(2);
		rules.add(rule);
		FlowRuleManager.loadRules(rules);
	}
}

其中log.info("Hello World");为被保护的逻辑,设置的规则为每秒最多通过2个请求。

  • 运行

运行后可以在控制台看到每秒输出2次Hello World:

17:44:02.558 [main] INFO  demo.App - Hello World
17:44:02.564 [main] INFO  demo.App - Hello World
17:44:03.500 [main] INFO  demo.App - Hello World
17:44:03.500 [main] INFO  demo.App - Hello World
17:44:04.500 [main] INFO  demo.App - Hello World
17:44:04.500 [main] INFO  demo.App - Hello World
17:44:05.500 [main] INFO  demo.App - Hello World
17:44:05.500 [main] INFO  demo.App - Hello World
17:44:06.500 [main] INFO  demo.App - Hello World
17:44:06.500 [main] INFO  demo.App - Hello World

在Sentinel控制台也可以监控实时流量:

2、Web样例

  • pom.xml
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.7.18</version>
	<relativePath /> <!-- lookup parent from repository -->
</parent>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>com.alibaba.csp</groupId>
		<artifactId>sentinel-core</artifactId>
		<version>1.8.6</version>
	</dependency>
	<dependency>
		<groupId>com.alibaba.csp</groupId>
		<artifactId>sentinel-annotation-aspectj</artifactId>
		<version>1.8.6</version>
	</dependency>
	<!-- 使用Sentinel控制台需要引入 -->
	<dependency>
		<groupId>com.alibaba.csp</groupId>
		<artifactId>sentinel-transport-simple-http</artifactId>
		<version>1.8.6</version>
	</dependency>
</dependencies>
  • 配置类
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;

@Configuration
public class SentinelConfig {

public static final String RESOURCE_KEY = "hello";
    
    @PostConstruct
    public void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource(RESOURCE_KEY);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
		//每秒最多2个请求
        rule.setCount(2);          
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
    	//让@SentinelResource注解生效
        return new SentinelResourceAspect();
    }
}
  • 控制器类
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;

@RestController
public class HelloController {

	@GetMapping("/hello")
	@SentinelResource(value = SentinelConfig.RESOURCE_KEY, blockHandler = "blocked")
	public String hello(String name) {
		return "hello " + name;
	}
	
	public String blocked(String name, BlockException e) {
		//方法签名必须与原方法一致,并多加一个BlockException参数
		return "Traffic throttling, please try again later~";
	}
}
  • 主程序
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

	public static void main(String[] args) {
		//控制台地址,使用Sentinel控制台需要,一般设置在启动参数里
		System.setProperty("csp.sentinel.dashboard.server", "localhost:8888");
		System.setProperty("project.name", "simple-demo");
		
		SpringApplication.run(App.class, args);
	}
}
  • 测试

访问http://localhost:8080/hello?name=jack,手工快速刷新浏览器,可以看到hello jackTraffic throttling, please try again later~交替出现。

使用JMeter启动10个线程运行20s:

查看Sentinel控制台监控:

由于Sentinel的”秒级”统计是滑动窗口(1秒被切成2个500 ms的bucket),在JMeter并发测试时可能出现窗口边界附近有大于2 个请求的情况。

例如:

时刻(ms) bucket 计数 窗口总和
0 bucket-0 1 1
400 bucket-0 2 2
900 bucket-1 1 3

虽然每个bucket都没超2,但整秒累加得到3,控制台就会显示3.0

参考资料