概述

任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们要根据系统的处理能力对流量进行控制。

流量控制的原理是监控应用的QPS或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性

Sentinel简介

sentinel是面向分布式服务框架的轻量级流量控制框架,主要以流量为切入点,从流量控制,熔断降级,系统负载保护等多个维度来维护系统的稳定性

Sentinel流量控制简介

Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示

通过java实现网站流量评估 流量控制 java_通过java实现网站流量评估

流量控制有以下几个角度:

资源的调用关系,例如资源的调用链路,资源和资源之间的关系;

运行指标,例如 QPS、线程池、系统负载等;

控制的效果,例如直接限流、冷启动、排队等。

Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。

Sentinel流量控制功能的使用

接下来,以b2b2c电商系统Javashop为例,具体说明Sentinel流量控制功能的使用

1. pom.xml文件中引入依赖
1 
2 com.alibaba.csp
3 sentinel-transport-simple-http
4 1.8.0
5 
6 
7 org.springframework.cloud
8 spring-cloud-starter-alibaba-sentinel
9 0.2.0.RELEASE
10 
11 
12 com.alibaba.csp
13 sentinel-spring-webmvc-adapter
14 1.8.0
15

2.application.yml加入sentinel配置
1 spring:2 cloud:3 sentinel:4 transport:5 #sentinel控制台地址(可有可无,不影响程序运行)6 dashboard: localhost:8080
7 sentinel:8 #true:限制所有资源 false:只限制resources下定义的资源9 limit-all: true
10 #限流阈值,每秒钟限制的请求数11 count: 5
12 #要限流的资源(若限制所有资源,此项配置无效)13 resources:14 - 'GET:/regions/{id}/children'
15 - 'GET:/regions/depth/{depth}'
3.初始化限流规则
1 @Component2 public class InitFlowRules implementsServletContextListener {3
4 @Autowired5 privateRequestMappingHandlerMapping handlerMapping;6 @Autowired7 privateSentinelConfig sentinelConfig;8
9 private final Logger logger = LoggerFactory.getLogger(this.getClass());10
11 @Override12 public voidcontextInitialized(ServletContextEvent sce) {13 List resources = new ArrayList<>();14
15
16 //添加所有资源的规则
17 if(sentinelConfig.isLimitAll()){18 Set rmSet =handlerMapping.getHandlerMethods().keySet();19 for(RequestMappingInfo rm : rmSet) {20 try{21 String path =rm.getPatternsCondition().toString();22 String method =rm.getMethodsCondition().toString();23 path = path.replace("[", "").replace("]", "");24 method = method.replace("[", "").replace("]", "");25 String resource = method + ":" +path;26 resources.add(resource);27 }catch(Exception e){28 e.printStackTrace();29 }30 }31 }else{32 resources =sentinelConfig.getResources();33 }34
35 if(!StringUtil.isNotEmpty(resources)){36 return;37 }38
39 List rules = new ArrayList<>();40 Integer count =sentinelConfig.getCount();41 for(String resource : resources){42 FlowRule rule = newFlowRule();43 rule.setResource(resource);44 rule.setGrade(RuleConstant.FLOW_GRADE_QPS);45 rule.setCount(count);46 rules.add(rule);47 logger.info(resource);48 }49 FlowRuleManager.loadRules(rules);50 }51
52 @Override53 public voidcontextDestroyed(ServletContextEvent sce) {54
55 }56 }

4.配置限流拦截器

1 @Configuration2 public class SentinelWebMvcConfiguration implementsWebMvcConfigurer {3
4 @Autowired5 privateSentinelConfig sentinelConfig;6
7
8 @Override9 public voidaddInterceptors(InterceptorRegistry registry) {10 SentinelWebMvcConfig sentinelWebMvcConfig = newSentinelWebMvcConfig();11 //指定 请求方法 POST GET 等等
12 sentinelWebMvcConfig.setHttpMethodSpecify(true);13 //默认使用统一Web上下文 如果希望支持链路关系的流控策略则应该设置为false
14 sentinelWebMvcConfig.setWebContextUnify(true);15 //统一的 BlockException 处理 FlowException(BlockException) 会被 JVM 的 UndeclaredThrowableException 包裹一层 某种原因并不能捕获到异常
16 sentinelWebMvcConfig.setBlockExceptionHandler(newDefaultBlockExceptionHandler());17 //用来标识来源 可针对性的对特定客户端的请求进行流控 limitApp18 //sentinelWebMvcConfig.setOriginParser(request -> {19 //String remoteAddr = "default";20 //try {21 //remoteAddr = getIpAddress(request);22 //} catch (IOException e) {23 //e.printStackTrace();24 //}25 //return remoteAddr;26 //});27 //sentinelWebMvcConfig.setOriginParser(request -> request.getParameter("app"));28
29 //对原始的URL进行处理,比如去掉锚点之类的 /foo/bar?a=3#title -> /foo/bar?a=330 //sentinelWebMvcConfig.setUrlCleaner( );
31 registry.addInterceptor(new SentinelWebInterceptor(sentinelWebMvcConfig)).addPathPatterns("/**");32 }33
34 }
5.限流全局异常处理
1 public class DefaultBlockExceptionHandler implementsBlockExceptionHandler {2
3 @Override4 public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throwsException {5 httpServletResponse.setStatus(500);6 httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");7 httpServletResponse.getWriter().print("{\"code\":\"500\",\"message\":\"请求被限制,请稍后重试\"}");8 httpServletResponse.getWriter().flush();9 }10 }