首先创建父maven项目springcloudalibaba
,在pom文件中引入如下依赖,需要注意版本对应关系
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.11.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloudalibaba</name>
<description>springcloudalibaba</description>
<modules>
<module>Order-sentinel</module>
</modules>
<properties>
<java.version>1.8</java.version>
<spring.cloud.alibaba.version>2.2.5.RELEASE </spring.cloud.alibaba.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
</properties>
<dependencies>
<!-- springboot场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 依赖放在dependencyManagement中,子项目引用前需要声明,依赖不在dependencyManagement中,子项目默认直接继承-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
随后在父maven项目下创建子maven项目Order-sentinel
,pom文件如下
<parent>
<groupId>com.example</groupId>
<artifactId>springcloudalibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>org.example</groupId>
<artifactId>Order-sentinel</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
创建一个Controller控制器
@RestController
@Slf4j
public class HelloController {
private static final String RESOURCE_NAME = "hello";
private static final String USER_RESOURCE_NAME = "user";
private static final String DEGRADE_RESOURCE_NAME = "degrade";
// 进行流控
@RequestMapping("/hello")
public String hello(){
Entry entry = null;
try {
// 通过接口路径定义资源名称,sentinel是通过资源进行限制的
entry = SphU.entry(RESOURCE_NAME);
// 被保护的业务逻辑
String str = "hello world";
log.info("========="+str+"===========");
return str;
}catch (BlockException e1){
// 资源阻止访问,被限制或者被降级
// 进行相应的处理操作
log.info("block!");
return "被流控了";
}catch (Exception e2){
// 若需要配置降级规则,需要通过这种方式记录业务异常
Tracer.traceEntry(e2,entry);
}finally {
if(entry!=null){
entry.exit();
}
}
return null;
}
/**
* @description 改善接口中资源定义和被流控后降级的处理方法
* 使用步骤:1、引入依赖
* 2、配置bean
* value 定义资源
* blockHandler 设置流控降级后的处理方法(默认该方法声明在同一个类中)
* 如果不在同一个类中可以通过 blockHandlerClass 设置,且方法需要用static修饰
* @return: com.tuling.sentineldemo.entity.User
*/
@RequestMapping("/user")
@SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForGetUser")
public User getUser(String id){
return new User("测试");
}
/**
* 注意 :
* 1、一定要public
* 2、返回值一定要和源方法一样,包含源方法参数,可以在参数最后加上异常类
*
*/
public User blockHandlerForGetUser(String id,BlockException e){
e.printStackTrace();
return new User("被流控了!");
}
@PostConstruct
private static void initFlowRules(){
// 流控规则
List<FlowRule> rules = new ArrayList<>();
// 流控
FlowRule rule = new FlowRule();
// 设置受保护的资源
rule.setResource(RESOURCE_NAME);
// 设置流控规则(QPS)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
rule.setCount(2);
rules.add(rule);
// 流控
FlowRule rule1 = new FlowRule();
// 设置受保护的资源
rule1.setResource(USER_RESOURCE_NAME);
// 设置流控规则(QPS)
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 设置受保护的资源阈值
rule1.setCount(1);
rules.add(rule1);
// 加载配置好的规则
FlowRuleManager.loadRules(rules);
}
}
启动项目,浏览器快速访问user和hello接口,会看到被流控的字样,表明流控规则设置生效了
先下载安装好对应版本的dashboard,dashboard控制面板下载安装教程请看这篇《sentinel控制面板dashboard的下载安装教程》
随后在项目yml配置文件中进行相应的配置
server:
port: 8860
spring:
application:
name: order-sentinel
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
web-context-unify: false # 打开调用链路
order-sentinel
是对应的服务名,127.0.0.1:8080
为dashboard程序对应的访问ip和端口号
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
System.out.println("BlockExceptionHandler ========="+e.getRule());
String r = null;
if(e instanceof FlowException){
r = "被流控了";
}else if(e instanceof DegradeException){
r = "被降级处理了";
} else if (e instanceof ParamFlowException) {
r = "热点参数限流了";
} else if (e instanceof SystemBlockException) {
r = "触发了系统保护规则";
} else if (e instanceof AuthorityException) {
r = "授权规则不通过";
}
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getWriter(),r);
}
}
配置了全局BlockException异常处理后,当某个接口被流控或者降级处理导致异常会通过该方法进行处理
当前整个项目目录结构如下
controllert层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private IOrderService orderService;
@GetMapping("/addOrder")
public String addOrder(){
System.out.println("下单成功");
return "下单成功";
}
@GetMapping("/getOrder")
public String getOrder(){
System.out.println("查询订单成功");
return "查询订单成功";
}
@GetMapping("/flow")
// @SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
public String flow(){
return "返回成功";
}
@GetMapping("/flowThread")
@SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
public String flowThread() throws InterruptedException {
Thread.sleep(5000);
return "返回成功";
}
public String flowBlockHandler(BlockException e){
return "被流控了";
}
@RequestMapping("/test1")
public String test1(){
return orderService.getUser();
}
@RequestMapping("/test2")
public String test2(){
return orderService.getUser();
}
}
service层
public interface IOrderService {
String getUser();
}
service实现类
@Service
public class OrderServiceImpl implements IOrderService {
@Override
@SentinelResource(value = "getUser",blockHandler = "blockExceptionGetUser")
public String getUser() {
return "查询用户";
}
public String blockExceptionGetUser(BlockException e) {
return "流控";
}
}
最后,启动项目,依次访问各个接口,让各个接口能被dashboard识别并注册到控制面板中
。
进入dashboard控制面板,点击簇点链路,可以看到各个接口并能做出相应的规则设置
对/order/addOrder资源进行流控规则设置,输入以上对应的值点击新增按钮,随后请求order/addOrder接口,只要在一秒内请求超过两次后会出现流控效果。QPS就表示为每秒请求数
同样为flowThread设置流控规则,由于flowThread接口设置了让线程睡眠5秒,让该接口积攒了超过两条线程后会出现流控效果
现有下单接口addOrder和查询订单接口getOrder,当下单接口请求量高于一定阈值,为了节省服务器性能,保障下单接口正常可用,可以对查询订单接口进行限流,此时可以用到关联模式。对查询订单接口设置如上流控规则,当下单接口每秒请求数超过2次,则查询订单接口不可用。
链路模式是当两个不同的接口调用一个公用的方法,可以对该方法设置流控规则。当公用的方法每秒请求超过一定限制可以通过链路模式对某个调用它的接口进行限流。
这里也可以对test1下的getUser设置规则,效果一样
当getUser请求数每秒超过2次,则/order/test1被流控,/order/test2正常调用
---------待更新------------