目录
一、概念
1.1 介绍
1.2 快速配置
二、基础功能演示
三、流控规则
四、降级规则
五、@SentinelResource注解
5.1 按资源名限流演示
5.2 自定义限流处理类
六、热点Key限流
6.1 基本演示
6.2 热点规则添加例外项
七、系统规则
八、服务熔断功能(+ribbon/openFeign)
8.1 sentinel+ribbon
8.2 sentinel+openFeign
九、规则持久化
Sentinel是一个轻量级的流量控制与熔断降级Java库。类似于Hystrix。可以处理服务使用过程中的各种问题,如服务雪崩、服务降级、服务熔断、服务限流等。
下载:下载jar包,例如sentinel-dashboard-1.7.0.jar
使用前提:Jdk8、8080端口未被占用。
开启:在jar包目录下执行下面指令。
java -jar sentinel-dashboard-1.7.0.jar
最后输入地址: http://localhost:8080 即为Sentinel的后台管理界面
1.建立新工程-cloudalibaba-sentinel-service8401
2.POM
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.csp
sentinel-datasource-nacos
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
cn.hutool
hutool-all
4.6.3
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
3.XML
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
4.主启动
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401
{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
5.业务类
@RestController
public class FlowLimitController
{
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
return "------testB";
}
}
6.测试
首先确保启动了Nacos和Sentinel服务。
连续输入: localhost:8401/testA localhost:8401/testB
Sentinel 后台的数据:(QPS-每秒的请求数)
RT(平均响应时间,秒级)
平均响应时间超出阈值且在时间窗口内通过的请求>=5,两个条件同时满足后触发降级
窗口期过后关闭断路器
RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)
异常比列(秒级)
QPS >= 5 且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
异常数(分钟级)
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
注:Sentinel的断路器是没有半开状态!
简单来说,@SentinelResource 用来标识资源名以及定义违反规则和代码出错时的兜底方法,类似于HystrixCommand 定义FallBack。系统默认有兜底方法,默认提示:Blocked by Sentinel (flow limiting)
还是在上面的 cloudalibaba-sentinel-service8401 中
1.POM引入自定义包
com.atguigu.springcloud
cloud-api-commons
${project.version}
2.新建Controller
@RestController
public class RateLimitController
{
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource()
{
return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception)
{
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
3.配置流控规则
4.测试
输入: localhost:8401/byResource 频次大于1时,会返回我们自定义的错误信息。
注:最好按照资源名设置流控规则,如果按照URL设置,只会使用默认的兜底方法。
上面方式的问题:
1 系统默认的,没有体现我们自己的业务要求。
2 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
3 每个业务方法都添加一个兜底的,那代码膨胀加剧。
4 全局统一的处理方法没有体现。
1.自定义限流处理类- CustomerBlockHandler
public class CustomerBlockHandler
{
public static CommonResult handleException(BlockException exception){
return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler");
}
}
2.修改Controller
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleException")
public CommonResult customerBlockHandler()
{
return new CommonResult(200,"按客户自定义限流处理逻辑");
}
3.测试
设置流控规则
输入:
localhost:8401/rateLimit/customerBlockHandler 频次大于1时,会返回我们自定义的错误信息。
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作
1.修改Controller
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
{
return "-----dealHandler_testHotKey";
}
2.设置热点规则
方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理
3.测试
输入: http://localhost:8401/testHotKey?p1=abc 频率超过1,报错
输入: http://localhost:8401/testHotKey?p1=abc&p2=33 频率超过1,报错
输入: http://localhost:8401/testHotKey?p2=abc 频率超过1,不报错
上面的示例中,参数p1当QPS超过1秒1次点击后马上被限流,假如我们期望p1参数是某个特殊值时,它的限流阈值和平时不一样。(就是在特定条件下允许例外),可以这样设置:
注:热点参数的注意点,参数必须是基本类型或者String
这个设置要慎重,因为全局的,影响到整个服务节点。不再演示
有两种方案:sentinel+ribbon / sentinel+openFeign
1.新建cloudalibaba-provider-payment9003/9004两个一样的做法,只是端口不同
2.POM
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.atguigu.springcloud
cloud-api-commons
${project.version}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
3.YML
server:
port: 9003 #记得改
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
4.主启动
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
5.业务类
@RestController
public class PaymentController
{
@Value("${server.port}")
private String serverPort;
public static HashMap hashMap = new HashMap<>();
static
{
hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
}
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id)
{
Payment payment = hashMap.get(id);
CommonResult result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment);
return result;
}
}
6.新建 cloudalibaba-consumer-nacos-order84
7.POM
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.atguigu.springcloud
cloud-api-commons
${project.version}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
8.YML
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
port: 8719
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
9.主启动
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84
{
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
10.业务类
10.1 ApplicationContextConfig
@Configuration
public class ApplicationContextConfig
{
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
10.2 CircleBreakerController
@SentinelResource 设置fallback 、blockHandler 属性,可以分别处理运行时异常和违反sentinel规则的兜底方法
@RestController
@Slf4j
public class CircleBreakerController
{
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler")
public CommonResult fallback(@PathVariable Long id)
{
CommonResult result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id,CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录");
}
return result;
}
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"fallback,无此流水,exception "+e.getMessage(),payment);
}
public CommonResult blockHandler(@PathVariable Long id,BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
11.测试
在sentinel中进行配置:
首先输入: http://localhost:84/consumer/fallback/1 正常显示数据
连续输入上面的地址 blockHandler->
{
"code": 445,
"message": "blockHandler-sentinel限流,无此流水: blockException null",
"data": {
"id": 1,
"serial": "null"
}
}
再输入: http://localhost:84/consumer/fallback/4 fallback->
{
"code": 444,
"message": "兜底异常handlerFallback,exception内容 IllegalArgumentException,非法参数异常....",
"data": {
"id": 4,
"serial": "null"
}
}
注:若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
1.消费端引入openfeign依赖
org.springframework.cloud
spring-cloud-starter-openfeign
2.修改YML,激活sentinel对feigh的支持
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
3.新建业务接口 PaymentService,带@FeignClient注解
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id);
}
4.新建异常处理业务类 PaymentFallbackService,实现 PaymentService
@Component
public class PaymentFallbackService implements PaymentService
{
@Override
public CommonResult paymentSQL(Long id)
{
return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
5.修改Controller
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id)
{
return paymentService.paymentSQL(id);
}
6.主启动类添加@EnableFeignClients注解
7.测试
输入: http://localhost:84/consumer/paymentSQL/1 正常显示
此时关闭8003、8004 返回fallback:
{
"code": 44444,
"message": "服务降级返回,---PaymentFallbackService",
"data": {
"id": 4,
"serial": "errorSerial"
}
}
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化,将限流配置规则持久化进Nacos保存。
比如我们希望对cloudalibaba-sentinel-service8401上的规则进行持久化:
1.POM
com.alibaba.csp
sentinel-datasource-nacos
2.YML
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
3.Nacos中添加规则配置
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
4.测试
重启8041服务,发送如下请求:
http://localhost:8401/rateLimit/byUrl
刷新sentinel,可以看到持久化的配置规则了: