在上一节中我们使用控制台简单实现了接口限流,这里的限流针对所有的MVC接口,但实际开发中,不仅需要对接口限流,也需要对某个方法的调用、某个外部资源的调用等都实现限流。这时候就需要手动定义限流的资源点,再另外配置限流策略。
本文将使用@SentinelResource
注解灵活的定义资源点,同时讲解如何配置控制策略。
自定义资源点
第一步:新建模块alibaba-sentinel-reource
,其中pom.xml
和application.properties
与alibaba-sentinel-rate-limiting
一致
第二步:将SentinelResourceAspect
注入Bean中,这个类就是对注解支持的配置Bean
package com.demo;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class AlibabaSentinelResourceApplication {
public static void main(String[] args) {
SpringApplication.run(AlibabaSentinelResourceApplication.class, args);
}
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
第三步:创建service类,同时加上@SentinelResource
注解,用来标识这个方法需要控制流量
package com.demo;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class EchoService {
@SentinelResource(value = "EchoService#echo")
public String echo(String str) {
log.info(str);
return str;
}
}
第四步:创建HTTP请求
package com.demo;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@AllArgsConstructor
@RestController
public class EchoController {
private final EchoService echoService;
@GetMapping("echo")
public String echo() {
String str = echoService.echo("echo: " + new Date());
log.info(str);
return str;
}
}
实现限流与熔断降级
在定义资源点后,可以通过Sentinel控制台面板来设置限流规则与降级策略,同时通过@SentinelResource
指定出现对应的异常处理策略。
限流控制
在Sentinel控制台对service进行流控,新增一个流控规则,如下图:
流控规则添加完成之后,通过Postman的Collection Runner发起多条请求,如下图:
可以看到一秒内发起了6次请求,前3次正常返回200 OK
,而后3次请求出现了500
错误,说明创建的流控规则已经生效,再看程序控制台,输出了如下日志:
2021-04-16 08:53:45.394 ERROR 22510 --- [io-8004-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.reflect.UndeclaredThrowableException] with root cause
com.alibaba.csp.sentinel.slots.block.flow.FlowException: null
限流的异常处理
默认情况下,Sentinel对控制资源的限流处理是抛出异常,显然这不够优雅。我们对程序做一些调整,根据实际业务针对限流做特殊处理。
比如修改EchoService
:
package com.demo;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class EchoService {
@SentinelResource(value = "EchoService#echo", blockHandler = "echoBlockHandler")
public String echo(String str) {
return str;
}
public String echoBlockHandler(String str, BlockException ex) {
log.error("echoBlockHandler: " + ex.getRuleLimitApp());
return "block: " + str;
}
}
可以看到在原先的基础上,做了如下更改:
- 通过
@SentinelRsource
的blockHandler
属性指定具体的回调函数; - 实现
blockHandler
回调函数,根据源码的注释说明,该方法的传参必须与资源点echo()
的传参一样,并且最后加上BlockException
异常参数;同时,返回类型也必须一样。
代码改好之后,重启应用。注意重启应用之后,Sentinel控制台需要重新添加流控规则。使用Postman访问接口,此时所有的接口都返回200 OK
,不再返回异常信息,如下图:
同时控制台也会打印出我们定义的日志,如下:
2021-04-16 16:49:43.478 ERROR 23610 --- [nio-8004-exec-5] com.demo.EchoService : echoBlockHandler: default
2021-04-16 16:49:43.478 INFO 23610 --- [nio-8004-exec-5] com.demo.EchoController : block: echo: Fri Apr 16 16:49:43 CST 2021
代码示例
- Github:
- Gitee: