随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel包括服务端和客户端,服务端有可视化界面,客户端需引入jar后即可和服务端通信并完成限流功能。
运行服务端
因为我们还没有接入sentinel客户端,所有dashboard是空的:
客户端接入
1.新建Spring Boot项目,pom.xml导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.naixin</groupId>
<artifactId>sentinel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sentinel</name>
<description>Demo project for Sentinel</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.14</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.修改配置文件application.yum
server:
port: 8077
spring:
application:
name: nanlistSentinel
cloud:
#向sentinel控制台注册服务
sentinel:
transport:
dashboard: 192.168.8.144:8082
#客户端端口号
port: 8719
#取消控制台懒加载
eager: true
3.Application启动类
package com.naixin.sentinel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import java.net.InetAddress;
@SpringBootApplication
@Slf4j
public class SentinelApplication {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext application = SpringApplication.run(SentinelApplication.class, args);
Environment env = application.getEnvironment();
log.info("\n----------------------------------------------------------\n\t" +
"Application '{}' is running! Access URLs:\n\t" +
"Login: \thttp://{}:{}/\n\t" +
"----------------------------------------------------------",
env.getProperty("spring.application.name"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port"));
System.out.println("╭フ哇、內誰說↘生活萁拾狠简单濄勒今天鹫是明天⊕_⊙");
}
}
4.编辑Controller
package com.naixin.sentinel.web;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by liangguannan on 2021/11/4.
*/
@RestController
@RequestMapping("/sen")
public class SentinalController {
/**
* 需要通过Sentinel来控制流量的地方使用@SentinelResource注解
* 按url限流,有默认的限流处理逻辑
*
* @return
*/
@SentinelResource(value = "getSentinalTop")
@GetMapping("/getSentinalTop")
public String getSentinalTop(){
return "Hello -- TheShy";
}
/**
* 需要通过Sentinel来控制流量的地方使用@SentinelResource注解
* 按url限流,有默认的限流处理逻辑
*
* @return
*/
@SentinelResource(value = "getSentinalMid")
@GetMapping("/getSentinalMid")
public String getSentinalMid(){
return "Hello -- Rookie";
}
}
启动客户端
选择合适的方式接入 Sentinel,然后在应用启动时加入 JVM 参数-Dcsp.sentinel.dashboard.server=consoleIp:port指定控制台地址和端口。确保客户端有访问量,Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,将客户端纳入到控制台的管辖之下。
如果是本地启动的sentinel 本地客户端可以在idea 中设置:
容灾组件Sentinel控制台不显示应用问题
启动客户端sentinel,发现发送请求后,在sentinel控制台监控不到发送的请求:
服务器上查看日志发现报如下错误:
c.a.c.s.dashboard.metric.MetricFetcher : Failed to fetch metric from <http://172.16.100.141:8721/metric?startTime=1563865044000&endTime=1563865050000&refetch=false> (ConnectionException: Connection refused: no further information)
说明发送了内网地址(因为我是在服务器上启动的sentinel),导致fetch拉取埋点信息不通,这是因为控制台主动通过接口来客户端拉信息,但是如果控制台访问不通客户端,就得把本地测试打成jar包,部在服务器上的控制台。
启动客户端服务:
nohup java -jar -Dcsp.sentinel.dashboard.server=192.168.8.144:8082 sentinel-0.0.1-SNAPSHOT.jar>log_p.file 2>&1 &
访问下controller端口:
查看sentinel-dashboard控制台:
Sentinel 的3种保护场景
流量控制:不被上游压垮。当访问量超过服务器所能处理的请求数量QPS就让其它请求进行排队等待或者抛弃掉。
熔断降级:不被下游拖垮。如果下游微服务响应时间过长或者异常比较多时直接使用一个默认值作为下游微服务的响应,不再继续等待下游微服务的响应。
系统负载保护:如果系统的外界资源如(内存、CPU)过高,此时如果该机器再接收请求可能会将服务器累垮,此时可以将该请求转发到集群中的其它机器上,如果其它机器负载也很高,就平衡一下机器能处理的请求的数量,当超过这个数量进行排队等待处理。
注意:流量控制和熔断降级都是针对某个资源进行配置的,而系统负载保护是针对服务器进行配置的。
流量控制:
流量控制,其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标,当达到指定的阈值时
对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
配置流控规则:
阀值类型:QPS
单机阀值:2
意思就是:该接口每秒最多允许进入两个请求。
访问http://192.168.8.144:8077/sen/getSentinalTop 并且快速刷新,观察sentinel流控是否生效。
熔断降级:
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例、异常数,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。一旦后端服务不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力。
RT模式(官网上叫慢调用比例):根据响应时间进行熔断。
平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。
@SentinelResource注解使用详解
在需要通过Sentinel来控制流量的地方使用@SentinelResource注解,在定义了资源点之后,我们就可以通过Dashboard来设置限流和降级策略来对资源点进行保护了。同时,也可以通过@SentinelResource来指定出现限流和降级时候的异常处理策略。
Sentinel提供了这样的功能,让我们可以另外定义一个方法来代替被限制或异常服务返回数据,这就是fallback和blockHandler。(fallback是负责所有异常的,如果没有配置blockHandler,那么超出sentinel设置阈值的请求也会进到fallback里面。 两者区别:blockHandler只是fallback的一个特殊情况,只捕捉BlockHandlerException。)
fallback:失败调用,若本接口出现未知异常,则调用fallback指定的接口。
blockHandler:sentinel定义的失败调用或限制调用,若本次访问被限流或服务降级,则调用blockHandler指定的接口。
package com.naixin.sentinel.web;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by liangguannan on 2021/11/4.
* 若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
* 若未配置 blockHandler、fallback 和 defaultFallback,
* 则被限流降级时会将 BlockException 直接抛出。
*/
@RestController
@RequestMapping("/sen")
public class SentinalController {
/**
* 需要通过Sentinel来控制流量的地方使用@SentinelResource注解
* 按url限流,有默认的限流处理逻辑
*
* @return
*/
@SentinelResource(value = "getSentinalTop",blockHandler="testBlockHandler")
@GetMapping("/getSentinalTop")
public String getSentinalTop(){
return "Hello -- TheShy";
}
/**
* 保证调用这些被限制服务的调用者,让他们拿到一个合理的结果,而不是返回去一个异常。
* blockHandler:sentinel定义的失败调用或限制调用,若本次访问被限流或服务降级,则调用blockHandler指定的接口。
* @param b
* @return
*/
public String testBlockHandler (BlockException b) {
return "TheShy:被限流降级";
}
/**
* @SentinelResource注解除了可以用来做限流控制之外,还能实现与Hystrix类似的熔断降级策略。
*
* @return
*/
@SentinelResource(value = "getSentinalMid", fallback = "helloFallback")
@GetMapping("/getSentinalMid")
public String getSentinalMid(){
try {
Thread.sleep(1000);//休息1000毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hello -- Rookie";
}
/**
* fallback:失败调用,若本接口出现未知异常,则调用fallback指定的接口。
* @return
*/
public String helloFallback() {
return "Rookie:被限流降级";
}
}
更多注解属性说明
@SentinelResource注解最主要的两个用法:限流控制和熔断降级的具体使用案例介绍完了。另外,该注解还有一些其他更精细化的配置,比如忽略某些异常的配置、默认降级函数等等,具体可见如下说明:
value:资源名称,必需项(不能为空)
entryType:entry类型,可选项(默认为 EntryType.OUT)
fallback:fallback函数名称,可选项,用于在抛出异常的时候提供 fallback处理逻辑。fallback函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。fallback函数签名和位置要求: 返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个 Throwable类型的参数用于接收对应的异常。
fallback函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass为对应的类的 Class 对象,注意对应的函数必需为 static函数,否则无法解析。 defaultFallback(since 1.6.0):默认的 fallback函数名称,可选项,通常用于通用的 fallback逻辑(即可以用于很多服务或方法)。默认 fallback函数可以针对所有类型的异常(除了exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback和 defaultFallback,则只有 fallback会生效。defaultFallback函数签名要求:返回值类型必须与原函数返回值类型一致;
方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
defaultFallback函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。