限流的目的防止恶意请求流量、恶意攻击,或者防止流量超过系统峰值
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
spring:
cloud:
sentinel: # 位置在cloud元素下, 与nacos是并列关系
transport:
dashboard: localhost:8180 # 指定sentinel控制台地址。
package com.jt.provider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/provider")
public class ProviderSentinelController {
@GetMapping("/sentinel01")
public String doSentinel01(){
return "sentinel 01 test ...";
}
}
当关联的资源达到阈值,就限流自己
比如: 一个是读取订单信息接口,一个是写入订单信息接口, 可利用关联模式, 一旦写入请求多,就限制读的请求
@GetMapping("/sentinel02")
public String doSentinel02(){
return "sentinel 02 test ...";
}
只记录指定链路入口的流量
package com.jt.provider.service;
@Service
public class ResourceService{
@SentinelResource("doGetResource")
public String doGetResource(){
return "doGetResource";
}
}
@Autowired
private ResourceService resourceService;
@GetMapping("/sentinel03")
public String doSentinel03() throws InterruptedException {
resourceService.doGetResource();
return "sentinel 03 test";
}
流控模式为链路模式时,sentinel 1.7.2以后版本,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,
因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合
sentinel:
web-context-unify: false # 默认为true, 会将所有的请求链路聚合到sentinel_spring_web_context 链路下
//AtomicLong 类支持线程安全的自增自减操作
private AtomicLong atomicLong=new AtomicLong(1);
@GetMapping("/sentinel04")
public String doSentinel04() throws InterruptedException {
//获取自增对象的值,然后再加1
long num=atomicLong.getAndIncrement();
if(num%2==0){//模拟50%的慢调用比例
Thread.sleep(200);
}
return "sentinel 04 test";
}
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理
//AtomicLong 类支持线程安全的自增自减操作
private AtomicLong atomicLong=new AtomicLong(1);
@GetMapping("/sentinel04")
public String doSentinel04() throws InterruptedException {
//获取自增对象的值,然后再加1
long num = atomicLong.getAndIncrement();
if(num%2 == 0){//模拟50%的慢调用比例
// Thread.sleep(200);
throw new RuntimeException("出现异常");
}
return "sentinel 04 test";
}
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。
定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理。
package com.jt.provider.controller;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/*自定义降级异常处理对象*/
@Slf4j
@Component
public class ServiceBlockExceptionHandler implements BlockExceptionHandler {
/*处理BlockException类型以及子类类型的异常
*
* */
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
//设置响应数据编码
response.setCharacterEncoding("utf-8");
//告诉客户端响应数据的类型,以及客户端显示内容的编码
response.setContentType("text/html;charset=utf-8");
//向客户端响应一个json格式的字符串
//String str="{\"status\":429,\"message\":\"访问太频繁了\"}";
Map<String,Object> map=new HashMap<>();
map.put("status", 429);
map.put("message","访问太频繁了");
String jsonStr=new ObjectMapper().writeValueAsString(map);
PrintWriter out = response.getWriter();
out.print(jsonStr);
out.flush();
out.close();
}
}
package com.jt.provider.service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
@Service
public class ResourceService {
/*
* @SentinelResource使用此注解描述的方法,
* 在此方法被访问时,会在sentinel的簇点链路中显示,
* 此注解中指定的名字就是资源名,我们可以对这个资源
* 的访问,按照指定的链路进行限流设计.
*
* 此注解中的blockHandlerClass用于指定,出现限流异常时的异常处理类,
* blockHandler属性用于指定异常处理类中的方法(此方法的返回类型,参数
* 要与 @SentinelResource注解描述的方法参数一致,可以加BlockException异常
* 类型参数,而且方法必须是静态.)
* fallbackClass 用于指定业务异常处理类,fallback用于指向业务处理类
* 中的异常处理方法(此方法的返回类型,参数要与@SentinelResource注解描
* 述的方法参数一致,可以加Throwable异常类型参数)
* @return
*/
@SentinelResource(value="doGetResource",
blockHandlerClass = ResourceBlockHandler.class,
blockHandler = "call",
fallback = "call",
fallbackClass = ResourceFallbackHandler.class)
public String doGetResource(){
// int a=100,b=0;
// a=a/b;
return "do get resource";
}
}
package com.jt.provider.service;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ResourceBlockHandler {
/**
* 注意此方法中的异常类型必须为BlockException (它是所有限流,降级等异常的父类类型)
* @param ex
* @return
*/
public static String call(BlockException ex){
log.error("block exception {}", ex.getMessage());
return "访问太频繁了,稍等片刻再访问";
}
}
热点参数限流会统计传入参数中的热点数据,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。
热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
其中,Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
//http://localhost:8082/provider/sentinel/findById?id=10
@GetMapping("/sentinel/findById")
@SentinelResource("resource")
public String doFindById(@RequestParam("id") Integer id){
return "resource id is "+id;
}
Sentinel的系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load(负载)、RT(响应时间)、入口 QPS 、线程数和CPU使用率五个维度监控应用数据,
让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性
根据调用方来限制资源是否通过, 使用 Sentinel 的授权规则的黑白名单控制的功能
新建Controller类实现RequestOriginParser
package com.jt.provider.controller;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/*构建DefaultRequestOriginParser对象, 对请求数据进行解析
* 1.请求头
* 2.请求行
* 3.请求体 */
@Component
public class DefaultRequestOriginParser implements RequestOriginParser {
/*当设置了授权规则后, 系统底层拦截到请求, 会调用此方法, 对请求数据进行解析
* http://ip;port/path?参数名origin=xxx*/
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getParameter("origin");
return origin;
}
}