Sentinel流控与熔断
参考: https://thinkwon.blog.csdn.net/article/details/103770879
项目结构
com.guo
├── guo-sentinel // sentinel限流熔断学习
│ └── guo-sentinel-base // [9204]消费端,限流、熔断在这里体现
│ └── guo-sentinel-provider // [9205]接口提供端
安装Sentinel控制台
Sentinel控制台是一个轻量级的控制台应用,它可用于实时查看单机资源监控及集群资源汇总,并提供了一系列的规则管理功能,如流控规则、降级规则、热点规则等。
下载</br> 可以从https://github.com/alibaba/Sentinel/releases下载sentinel-dashboard-$version.jar包。我这里下载的是 sentinel-dashboard-1.8.0.jar 版本。可以从百度云盘下载 提取码:0708
启动控制台
java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:\bgy\install\微服务\sentinel-dashboard-1.8.0.jar
其中-Dserver.port=8718用于指定Sentinel控制台端口为8718,D:\bgy\install\微服务\sentinel-dashboard-1.8.0.jar为下载的包路径地址。
打开控制台</br> Sentinel提供了一个可视化的操作平台,安装好之后,在浏览器中输入(http://localhost:8718 (opens new window))就可以访问了,默认的用户名和密码都是sentinel(我使用的是1.8.0版本)
限流
添加依赖
<!-- 在guo-sentinel导入依赖 -->
<dependencyManagement>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud.alibaba}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
<!-- 在guo-sentinel-base添加依赖 -->
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud.alibaba}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
添加bootstrap.yml配置
server:
port: 9100
address: localhost
spring:
application:
name: guo-sentinel-base
cloud:
sentinel:
transport:
dashboard: localhost:8718 #sentinel控制台的请求地址
按资源名称限流
在guo-sentinel-base编写需要限流的资源接口
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.guo.sentinel.handle.CustomBlockHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequestMapping("/rateLimit")
public class RateLimitController {
/**
* 按资源名称限流,需要指定限流处理逻辑
*
* @return
*/
@GetMapping("/byResource")
@SentinelResource(value = "byResource", blockHandler = "handleException")
public Map<String,Object> byResource() {
log.info("/rateLimit/byResource");
Map<String,Object> result = new HashMap<>();
result.put("name","按资源名称限流");
result.put("code",200);
return result ;
}
/**
* 按url限流,有默认的限流处理逻辑
*
* @return
*/
@GetMapping("byUrl")
@SentinelResource(value = "byUrl", blockHandler = "handleException")
public Map<String,Object> byUrl() {
log.info("/rateLimit/byResource");
Map<String,Object> result = new HashMap<>();
result.put("name","按url限流");
result.put("code",200);
return result ;
}
public Map<String,Object> handleException(BlockException exception) {
Map<String,Object> result = new HashMap<>();
result.put("name",exception.getClass().getCanonicalName());
result.put("code",200);
return result ;
}
@GetMapping("/customBlockHandler")
@SentinelResource(value = "customBlockHandler", blockHandler = "handleException", blockHandlerClass = CustomBlockHandler.class)
public Map<String,Object> blockHandler() {
Map<String,Object> result = new HashMap<>();
result.put("name","限流成功");
result.put("code",200);
return result ;
}
}
在项目guo-sentinel-provider编写远程测试接口
controller
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.guo.sentinel.service.UserService;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/insert")
public Map<String,Object> insert(@RequestBody Map<String,Object> user) {
return userService.insert(user);
}
@GetMapping("/{id}")
public Map<String,Object> getUser(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping("/listUsersByIds")
public Map<String,Object> listUsersByIds(@RequestParam List<Long> ids) {
return userService.listUsersByIds(ids);
}
@GetMapping("/getByUsername")
public Map<String,Object> getByUsername(@RequestParam String username) {
return userService.getByUsername(username);
}
@PostMapping("/update")
public Map<String,Object> update(@RequestBody Map<String,Object> user) {
return userService.update(user);
}
@PostMapping("/delete/{id}")
public Map<String,Object> delete(@PathVariable Long id) {
return userService.delete(id);
}
}
service
import java.util.List;
import java.util.Map;
public interface UserService {
Map<String,Object> insert(Map<String,Object> user);
Map<String,Object> getUser(Long id);
Map<String,Object> listUsersByIds(List<Long> ids);
Map<String,Object> getByUsername(String username);
Map<String,Object> update(Map<String,Object> user);
Map<String,Object> delete(Long id);
}
service.impl
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.guo.sentinel.service.UserService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class UserServiceImpl implements UserService {
@Override
public Map<String, Object> insert(Map<String, Object> user) {
log.info("新增:{}", user);
return null;
}
@Override
public Map<String, Object> getUser(Long id) {
log.info("获取:{}", id);
if(Long.valueOf("1").equals(id)) {
throw new RuntimeException("remote func is fail");
}
Map<String,Object> result = new HashMap<>();
result.put("reqData",id);
result.put("code","200");
return result ;
}
@Override
public Map<String, Object> listUsersByIds(List<Long> ids) {
log.info("获取集合:{}", ids);
return null;
}
@Override
public Map<String, Object> getByUsername(String username) {
log.info("根据用户名获取:{}", username);
if(1==1) throw new RuntimeException("模拟异常");
return null;
}
@Override
public Map<String, Object> update(Map<String, Object> user) {
log.info("更新:{}", user);
return null;
}
@Override
public Map<String, Object> delete(Long id) {
log.info("删除:{}", id);
return null;
}
}
在nacos配置guo-sentinel-base-dev.yml中添加远程调用接口地址
service-url:
user-service: http://localhost:9205
在sentinel控制台给资源添加限流规则
资源名称必须与流量控制定义的资源名称一致
快速访问接口发现资源被限流, 设置1QPS,连续访问2次以上发现,远程服务并未被调用,直接走了限流逻辑
##按url限流
在sentinel控制台给资源添加限流规则
资源名称必须与流量控制定义的资源访问全路径一致
自定义限流逻辑
用blockHandlerClass = CustomBlockHandler.class指定限流逻辑
import com.alibaba.csp.sentinel.slots.block.BlockException;
import java.util.HashMap;
import java.util.Map;
public class CustomBlockHandler {
public static Map<String,Object> handleException(BlockException exception) {
Map<String,Object> result = new HashMap<>();
result.put("name","自定义限流信息");
result.put("code",200);
return result ;
}
}
熔断
在guo-sentinel-base编写需要熔断的资源接口
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/breaker")
public class CircleBreakerController {
private static final Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);
@Autowired
private RestTemplate restTemplate;
@Value("${service-url.user-service}")
private String userServiceUrl;
@GetMapping("/fallback/{id}")
@SentinelResource(value = "fallback", fallback = "handleFallback")
public Map<String,Object> fallback(@PathVariable Long id) {
Map<String,Object> forObject = restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
System.out.println(forObject);
return forObject;
}
@GetMapping("/fallbackException/{id}")
@SentinelResource(value = "fallbackException", fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class})
public Map<String,Object> fallbackException(@PathVariable Long id) {
log.info("/breaker/fallbackException");
if (id == 1) {
throw new IndexOutOfBoundsException();
} else if (id == 2) {
throw new NullPointerException();
}
return restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
}
public Map<String,Object> handleFallback(Long id) {
Map<String,Object> result = new HashMap<>();
result.put("name","服务熔断");
result.put("code",200);
return result ;
}
public Map<String,Object> handleFallback2(Long id, Throwable e) {
LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass());
Map<String,Object> result = new HashMap<>();
result.put("name","服务熔断");
result.put("code",200);
return result ;
}
}
在feign接口上的限流与熔断
guo-sentinel-base添加feign接口相关依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
guo-sentinel-base中nacos添加配置项
# 打开sentinel对feign的支持
feign:
sentinel:
enabled: true
在应用启动类上添加启用feign的注解@EnableFeignClients
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class SentinelBaseApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelBaseApplication.class, args);
}
}
创建feign接口
controller
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.guo.sentinel.service.UserService;
@RestController
@RequestMapping("/user")
public class UserFeignController {
@Autowired
private UserService userService;
@PostMapping("/insert")
public Map<String,Object> insert(@RequestBody Map<String,Object> user) {
return userService.insert(user);
}
@GetMapping("/{id}")
public Map<String,Object> getUser(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping("/listUsersByIds")
public Map<String,Object> listUsersByIds(@RequestParam List<Long> ids) {
return userService.listUsersByIds(ids);
}
@GetMapping("/getByUsername")
public Map<String,Object> getByUsername(@RequestParam String username) {
return userService.getByUsername(username);
}
@PostMapping("/update")
public Map<String,Object> update(@RequestBody Map<String,Object> user) {
return userService.update(user);
}
@PostMapping("/delete/{id}")
public Map<String,Object> delete(@PathVariable Long id) {
return userService.delete(id);
}
}
service
import java.util.List;
import java.util.Map;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import com.guo.sentinel.service.impl.UserFallbackService;
// 注意value = "guo-sentinel-provider"值与服务提供方的服务注册名一致
@FeignClient(value = "guo-sentinel-provider", fallback = UserFallbackService.class)
public interface UserService {
@PostMapping("/user/insert")
Map<String,Object> insert(@RequestBody Map<String,Object> user);
@GetMapping("/user/{id}")
Map<String,Object> getUser(@PathVariable(value = "id") Long id);
@GetMapping("/user/listUsersByIds")
Map<String,Object> listUsersByIds(@RequestParam(value = "ids") List<Long> ids);
@GetMapping("/user/getByUsername")
Map<String,Object> getByUsername(@RequestParam(value = "username") String username);
@PostMapping("/user/update")
Map<String,Object> update(@RequestBody Map<String,Object> user);
@PostMapping("/user/delete/{id}")
Map<String,Object> delete(@PathVariable(value = "id") Long id);
}
fallback熔断逻辑UserFallbackService.java
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Service;
import com.guo.sentinel.service.UserService;
@Service
public class UserFallbackService implements UserService {
@Override
public Map<String, Object> insert(Map<String, Object> user) {
return this.installFallBack();
}
@Override
public Map<String, Object> getUser(Long id) {
return this.installFallBack();
}
@Override
public Map<String, Object> listUsersByIds(List<Long> ids) {
return this.installFallBack();
}
@Override
public Map<String, Object> getByUsername(String username) {
return this.installFallBack();
}
@Override
public Map<String, Object> update(Map<String, Object> user) {
return this.installFallBack();
}
@Override
public Map<String, Object> delete(Long id) {
return this.installFallBack();
}
private Map<String, Object> installFallBack() {
Map<String, Object> r = new HashMap<>();
r.put("code", 500);
r.put("msg", "调用失败,服务被降级");
return r;
}
}
在sentinel控制台配置熔断逻辑
统一限流处理
参考: https://zhuanlan.zhihu.com/p/150058613
使用nacos存储限流、熔断规则
默认情况下,当我们在Sentinel控制台中配置规则时,控制台推送规则方式是通过API将规则推送至客户端并直接更新到内存中。一旦我们重启应用,规则将消失。下面我们介绍下如何将配置规则进行持久化,以存储到Nacos为例。
先在guo-sentinel-base pom.xml中添加相关依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
修改bootstrap.yml添加规则配置
server:
port: 9204
spring:
application:
name: guo-sentinel-base
profiles:
active: dev
cloud:
sentinel:
transport:
# 配置Sentinel dashborad地址
dashboard: http://localhost:8718
port: 8719
# 添加Nacos数据源配置
datasource:
- nacos:
server-addr: localhost:8848
# guo-sentinel-base-flow-rules.json
data-id: ${spring.application.name}-flow-rules
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
ext-config:
# guo-sentinel-base-dev.yml
- data-id: ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
group: DEFAULT_GROUP
refresh: true
在nacos中新建配置guo-sentinel-base-flow-rules.json
[
{
"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:是否集群。
熔断相关参数解释
resource:资源名,即规则的作用对象
grade:熔断策略,支持慢调用比例/异常比例/异常数策略,默认为 慢调用比例
count:慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow:熔断时长,单位为 s
minRequestAmount:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入),默认为5
statIntervalMs:统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入),默认为1000ms
slowRatioThreshold:慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
代码地址
https://gitee.com/laiba_yun/test20210702
Sentinel流控与熔断的更多相关文章
- Spring Cloud & Alibaba 实战 | 第十二篇: 微服务整合Sentinel的流控、熔断降级,赋能拥有降级功能的Feign新技能熔断,实现熔断降级双剑合璧(JMeter模拟测试)
目录 一. Sentinel概念 1. 什么是Sentinel? 2. Sentinel功能特性 3. Sentinel VS Hystrix 二. Docker部署Sentinel Dashboar ...
- sentinel流控规则校验之源码分析
前言: 上节给大家把sentinel流控整个执行大致过了,但涉及到最核心的流控算法还没有讲,先提前说明一下 sentinel用的流控算法是令牌桶算法,参考了Guava的RateLimiter,有读过R ...
- 什么!Sentinel流控规则可以这样玩?
项目源码地址:公众号回复 sentinel,即可免费获取源码 前言 上一篇文章中,我们讲解了关于sentinel基本介绍以及流控规则中直接和快速失败的效果,有兴趣的可以去看上一篇文章,今天,我们给大家 ...
- 微服务架构 | 5.4 Sentinel 流控、统计和熔断的源码分析
目录 前言 1. Sentinel 的自动装配 1.2 依赖引入 1.3 SentinelWebAutoConfiguration 配置类 1.4 CommonFilter 过滤器 1.5 小结 2. ...
- 开发者说:Sentinel 流控功能在 SpringMVC/SpringBoot 上的实践
从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息.专栏所有内容均来自作者原创/投稿,本文是「开发者说」的第6篇,作者 Jas ...
- SpringBoot 2.0 + Nacos + Sentinel 流控规则集中存储
前言 Sentinel 原生版本的规则管理通过API 将规则推送至客户端并直接更新到内存中,并不能直接用于生产环境.不过官方也提供了一种 Push模式,扩展读数据源ReadableDataSource ...
- Sentinel流控规则
流控规则 注:Sentinel的监控页面一开始是没有东西,需要对监控的服务发起请求后才会出现 资源名:唯一名称,默认请求路径 针对来源:Sentinel可以针对调用者进行限流,填写微服务名,指定对哪个 ...
- Spring Cloud Alibaba整合Sentinel流控
前面我们都是直接通过集成sentinel的依赖,通过编码的方式配置规则等.对于集成到Spring Cloud中阿里已经有了一套开源框架spring-cloud-alibaba,就是用于将一系列的框架成 ...
- Sentinel Slot扩展实践-流控熔断预警实现
前言 前几天公司生产环境一个服务由于流量上升触发了 Sentinel 的流控机制,然后用户反馈访问慢,定位发现是 task 定时任务导致,后面 task 优化之后发布,流量恢复正常. 这是一个再正常不 ...
随机推荐
- Linux进阶之补充知识篇
一.Linux系统的主要特点: 开放性:指系统遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准 多用户:允许多个用户从相同或不同终端上同时使用同一台计算机 多任务:它是指计算机同时执行多个程 ...
- GO语言常用标准库04---flag读取命令行参数
package main import ( "flag" "fmt" "math" "os" ) /* go build ...
- 'utf-8' codec can't decode byte 0xd5 in position XXX: invalid continuation byte问题
找了一下午,各种资料搜集,愣是没搜出来答案. 结果今天早上,做一个小小的改变,就整出来了... 步骤如下: 1.打开excel,全选数据 2.新建记事本,粘贴,选择脚本,更改字体: 3.新建Excel ...
- JNDI注入和JNDI注入Bypass
之前分析了fastjson,jackson,都依赖于JDNI注入,即LDAP/RMI等伪协议 JNDI RMI基础和fastjson低版本的分析:https://www.cnblogs.com/pia ...
- TensorFlow用法
TensorFlow用法 什么是TensorFlow TensorFlow是一个开源软件库,用于使用数据流图进行数值计算.图中的节点表示数学运算,而图的边缘表示流动的多维数据数组(张量).这种灵活的体 ...
- TensorRT深度学习训练和部署图示
TensorRT深度学习训练和部署 NVIDIA TensorRT是用于生产环境的高性能深度学习推理库.功率效率和响应速度是部署的深度学习应用程序的两个关键指标,因为它们直接影响用户体验和所提供服务的 ...
- thymeleaf+Springboot实现自定义标签
在项目开发中,有一些组件不能满足我们快速开发的要求,我们需要封装一些组件来更加的便利我们.比如,我们可以封装一个下拉框组件,只要开发人员只有引用这个组件的标签,就能出现效果,而不用再去请求url,渲染 ...
- Appium 工作原理及 Desired Capabilities
一.Appium工作原理 脚本请求 --> 4723端口appium server --> 解析参数给PC端4724端口 --> 发送给设备4724端口 --> 通过设备472 ...
- 深入理解java虚拟机笔记Chapter11
运行期优化 即时编译 什么是即时编译? 当虚拟机发现某个方法或某段代码运行的特别频繁时,会把这段代码认为成热点代码: 在运行时,虚拟机会将这段代码编译成平台相关的机器码,并进行各种层次的优化. Hot ...
- Redisson 分布式锁实现之前置篇 → Redis 的发布/订阅 与 Lua
开心一刻 我找了个女朋友,挺丑的那一种,她也知道自己丑,平常都不好意思和我一块出门 昨晚,我带她逛超市,听到有两个人在我们背后小声嘀咕:"看咱前面,想不到这么丑都有人要." 女朋友 ...