应用场景:秒杀 大促 下单 订单回流处理
QPS(query per second) 每秒请求数,就是说服务器在一秒的时间内处理了多少个请求
controller再加个请求
@RequestMapping("flow")
public String flow(){
return "正常访问";
}
这是如果你快速访问 http://localhost:8861/order/flow 就会显示
Blocked by Sentinel (flow limiting)
自定义流控信息
@RequestMapping("flow")
@SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
public String flow(){
return "正常访问";
}
public String flowBlockHandler(BlockException e){
return "流控";
}
重启后 规则就没了 得重新设置 后续设置持久化
并发线程数用于保护业务线程池不被慢调用耗尽
Sentinel并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号呈隔离。并发数控制通常在调用端进行配置。
controller层添加代码
@RequestMapping("flowThread")
@SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
public String flowThread() throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
return "正常访问";
}
//上面先新添加的 下面是原来的 为了直观我就放上来了
public String flowBlockHandler(BlockException e){
return "流控";
}
控制台设置
两个浏览器访问 http://localhost:8861/order/flowThread
出现结果 一个再访问 另一个就会被流控的现象
不想老是用@SentinelResource 来处理异常的话,可以定义一个BlockException统一异常处理,每一个方法的异常处理结果一样就适用,每一个方法的异常处理结果不想一样就不适用
1.加统一返回结果类 Result
package com.tian.order.domain;
public class Result<T> {
private Integer code;
private String msg;
private T data;
public Result(Integer code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static Result error(Integer code, String msg){
return new Result(code,msg);
}
}
2.加统一处理类MyBlockExceptionHandler
package com.tian.order.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tian.order.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
Logger log= LoggerFactory.getLogger(this.getClass());
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws Exception {
//e.getRule() 资源 规则的信息信息
log.info("BlockExceptionHandler BlockException============="+e.getRule());
Result r=null;
if(e instanceof FlowException){
//流控
r=Result.error(100,"接口限流了");
}else if(e instanceof DegradeException){ //降级
r=Result.error(101,"服务降级了");
}
else if(e instanceof ParamFlowException){
r=Result.error(102,"热点参数限流了");
}
else if(e instanceof SystemBlockException){
r=Result.error(103,"触发系统保护规则了");
}
else if(e instanceof AuthorityException){
r=Result.error(104,"授权规则不通过");
}
//返回json数据
response.setStatus(500);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getWriter(),r);
}
}
3.注释掉@SentinelResource
@RequestMapping("flow")
//@SentinelResource(value = "flow",blockHandler = "flowBlockHandler")
public String flow(){
return "正常访问";
}
4.控制台添加流控规则
5.访问 http://localhost:8861/order/flow
快速访问页面出现 {"code":100,"msg":"接口限流了","data":null}
应用场景
对数据库又增 又查的 那么就对增(插入)增加这个规则,让查询被流控
例 生成订单访问量大的话就让查询订单限流
controller层 代码
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
return "生成订单";
}
@RequestMapping("/get")
public String get(){
return "查询订单";
}
控制台增加规则
访问 http://localhost:8861/order/add (1s中3次以上)
再访问 http://localhost:8861/order/get 出现限流
getUser
/ \
/ \
/order/test1 /order/test2
上图中来自入口/order/test1和/order/test2的请求都调用到了资源getUser,Sentinel允许只根据某个入口的统计信息对资源限流
1.service层
public interface IOrderService {
String getUser();
}
@Service
public class OrderServiceImpl implements IOrderService{
//使用了@SentinelResource,就不会被全局统一异常处理了
@SentinelResource(value = "getUser",blockHandler = "blockHandlerGetUser")
public String getUser() {
return "查询用户";
}
public String blockHandlerGetUser(BlockException e) {
return "流控用户";
}
}
2.controller层
@Autowired
IOrderService orderService;
@RequestMapping("/test1")
public String test1(){
return orderService.getUser();
}
@RequestMapping("/test2")
public String test2(){
return orderService.getUser();
}
3.控制台配置规则
访问 http://localhost:8861/order/test1 和 http://localhost:8861/order/test2
发现链路模式不生效,配置文件添加
spring.cloud.sentinel.web-contenxt-unity:false # 默认将调用链收敛
重启访问 发现/order/test1随便快速点击访问都可以,但/order/test2会被流控
比如秒杀活动,一下子很多流量访问一个资源 ,这个时候就可以用预热流控,
效果是 先让一部分人进来,经过缓存(例如redis),如果没有就去数据库,
然后数据库再把值缓存到redis中,那后面慢慢进来的就会去redis层查,没有的去数据库查,数据库再慢慢补全查询记录到redis。
冷加载因子: codeFactor默认是3,即请求QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的QPS 阈值。
访问 http://localhost:8861/order/flow 快速点击 发现刚开始没几下就限流了,后面快速点击几十下都不限流
1s10个请求 4个线程
快速失败结果
设置为排队等待 先让多的请求等待5s,等在空闲的时间再执行这些请求,超过时间则不执行
排队等待结果 (完全利用了空闲时间)
测试代码
@RequestMapping("flowThread")
//@SentinelResource(value = "flowThread",blockHandler = "flowBlockHandler")
public String flowThread() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
return "正常访问";
}
启动 控制台配置规则
@RequestMapping("/err")
public String err(){
int i=1/0;
return "hello";
}
启动 控制台配置规则
order-openfeign-sentinel模块
1.依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
2.service层 StockFeignService
@FeignClient(value = "stock-nacos",path = "/stock")
public interface StockFeignService {
//和stock-nacos模块中的一个方法一样
@RequestMapping("/reduct")
public String reduct2();
}
3.controller层
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
StockFeignService stockFeignService;
@RequestMapping("/add")
public String add(){
System.out.println("下单成功!");
String msg = stockFeignService.reduct2();
return "hello feign"+msg;
}
}
4.配置文件
server:
port: 8041
#应用名称 nacos会将该名称当作服务名称
spring:
application:
name: product-service
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
username: nacos
password: nacos
namespace: public
5.主启动类
@SpringBootApplication
@EnableFeignClients
public class OrderNacosApplication {
public static void main(String[] args) {
SpringApplication.run(OrderNacosApplication.class,args);
}
}
stock-nacos模块
controller层
@RequestMapping("/stock")
public class StockController {
@RequestMapping("/reduct2")
public String reduct2(){
int i=1/0;
System.out.println("扣减库存");
return "扣减库存"+port;
}
}
访问测试 http://localhost:8041/order/add 页面报500
整合sentinel
order-openfeign-sentinel模块
1.导入依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
2.编写配置文件
feign:
sentinel:
# openfeign整合sentinel
enabled: true #默认是false
3.编写降级方法 StockFeignServiceFallback
@Component
public class StockFeignServiceFallback implements StockFeignService{
public String reduct2() {
return "降级啦!!!";
}
}
4.StockFeignService接口调用
@FeignClient(value = "stock-nacos",path = "/stock",fallback = StockFeignServiceFallback.class)
public interface StockFeignService {
@RequestMapping("/reduct")
public String reduct2();
}
5.访问测试 http://localhost:8041/order/add 页面显示 hello feign降级啦!!!
1.controller添加代码
/**
* 热点规则,必须使用@SentinelResource
* @param id
* @return
*/
@RequestMapping("/get/{id}")
@SentinelResource(value = "getById",blockHandler = "HotBlockHander")
public String getById(@PathVariable Integer id){
System.out.println("正常访问");
return "正常访问";
}
public String HotBlockHander(@PathVariable Integer id,BlockException e){
return "热点异常处理";
}
2.控制台配置规则
3.测试访问
现象 http://localhost:8861/order/get/2 无论你请求多快都可以访问
http://localhost:8861/order/get/1 请求快了页面就显示 热点异常处理
好处 无依赖
坏处 应用重启规则就会消失,仅用于简单测试,不能用于生产环境
1.配置规则
[{
"resource": "/order/flow",
"controlBehavior": 0,
"count":2,
"grade":1,
"limitApp":"default",
"strategy":0
}]
order-sentinel项目添加依赖
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
编写配置
server:
port: 8861
spring:
application:
name: order-sentinel
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8888
web-context-unify: false # 默认将调用链收敛
datasource:
flow-rule:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
dataId: order-sentinel-flow-rule
rule-type: flow
访问 http://localhost:8861/order/flow 已经有流控规则了,只要你刷新快就会被流控