当前位置: 首页 > news >正文

设计需求网站sem竞价推广代运营收费

设计需求网站,sem竞价推广代运营收费,网站建设公司格,做群头像的网站在线制作一、分布式系统遇到的问题 1、服务雪崩效应 在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题#xff0c;调用这个服务就会出现线程阻塞的情况#xff0c;此时若有大量的请求涌入#xff0c;就会出现多条线程阻塞等待#x…一、分布式系统遇到的问题 1、服务雪崩效应 在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题调用这个服务就会出现线程阻塞的情况此时若有大量的请求涌入就会出现多条线程阻塞等待进而导致服务瘫痪。 由于服务与服务之间的依赖性故障会传播会对整个微服务系统造成灾难性的严重后果这就是服务故障的 “雪崩效应” 。 雪崩发生的原因多种多样最常见原因 程序Bug大流量请求硬件故障缓存击穿 程序Bug: 比说我们以前我们支付模块增加一个功能我们监听一个Rocketmq消息然后进行业务的处理至于什么业务处理我们就不说了上线后就报警了我们的支付成功的订单直线下降这是什么原因我们查看日志有很多消费消息的日志一直在打我们马上回滚然后排查问题我们发现线上的功能消费设置的是CONSUME_FROM_FIRST_OFFSET 这个是一个新的订阅组第一次启动从队列的最前位置开始消费后续再启动接着上次消费的进度开始消费而我们监听的消息是已经存在的所以里面存在了很多的消息为此消费端一直消费对应的消息导致一直运行从而影响了支付的功能。我们改为CONSUME_FROM_LAST_OFFSET【一个新的订阅组第一次启动从队列的最后位置开始消费后续再启动接着上次消费的进度开始消费】。大流量请求在秒杀和大促开始前,如果准备不充分,瞬间大量请求会造成服务提供者的不可用。硬件故障可能为硬件损坏造成的服务器主机宕机, 网络硬件故障造成的服务提供者的不可访问。缓存击穿一般发生在缓存应用重启, 缓存失效时高并发所有缓存被清空时,以及短时间内大量缓存失效时。大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用。 我们无法完全杜绝雪崩源头的发生只有做好足够的容错保证在一个服务发生问 题不会影响到其它服务的正常运行。也就是雪落而不雪崩。 二、常见容错方案 要防止雪崩的扩散我们就要做好服务的容错容错说白了就是保护自己不被猪队友拖垮的一些措施, 下面介绍常见的服务容错思路和组件 1、常见的容错思路 常见的容错思路有隔离、超时、限流、熔断、降级这几种下面分别介绍一下。 1.1 隔离 大的方向我们可以进行物理隔离比如说我们可以把用户实例分为好几个组一个组为4台为核心服务提供另一组是2台为非核心的组提供服务这样就进行了物理隔离如果我们进入某个实例内我们想比如我们的RPC调用我们调用的请求都会先进入一个队列里面然后再消费那一个出了问题也会影响其他的服务调用那我们为每个服务的调用都会创建一个队列这样进行了队列隔离同时我们我在后面设置一个单独的线程池进行线程池隔离但是如果我们后面访问的下游如果没有隔离会有什么问题是不是还会出现问题比如我们后面进行访问的时候都是一个数据库。 1.1 超时 在上游服务调用下游服务的时候上游服务设置一个最大响应时间如果超过这个时间下游未作出反应上游服务就断开请求释放掉线程。 1.2 限流 限制请求核心服务提供者的流量使大流量拦截在核心服务之外这样可以更好的保证核心服务提供者不出问题对于一些出问题的服务可以限制流量访问。 计数器固定窗口算法计数器滑动窗口算法漏桶算法令牌桶算法 1.3 熔断 在互联网系统中当下游服务因访问压力过大而响应变慢或失败上游服务为了保护系统整体的可用性可以暂时切断对下游服务的调用。 这种牺牲局部保全整体的措施就叫做熔断。 服务熔断一般有三种状态 熔断关闭状态Closed 服务没有故障时熔断器所处的状态对调用方的调用不做任何限制。 熔断开启状态Open 后续对该服务接口的调用不再经过网络直接执行本地的fallback方法。 半熔断状态Half-Open 尝试恢复服务调用允许有限的流量调用该服务并监控调用成功率。如果成功率达到预期则说明服务已恢复进入熔断关闭状态如果成功率仍旧很低则重新进入熔断关闭状态。 【现实世界的断路器大家肯定都很了解断路器实时监控电路的情况如果发现电路电流异常就会跳闸从而防止电路被烧毁。 软件世界的断路器可以这样理解实时监测应用如果发现在一定时间内失败次数/失败率达到一定阈值就“跳闸”断路器打开——此时请求直接返回而不去调用原本调用的逻辑。跳闸一段时间后例如10秒断路器会进入半开状态这是一个瞬间态此时允许一次请求调用该调的逻辑如果成功则断路器关闭应用正常调用如果调用依然不成功断路器继续回到打开状态过段时间再进入半开状态尝试——通过”跳闸“应用可以保护自己而且避免浪费资源而通过半开的设计可实现应用的“自我修复“。 所以同样的道理当依赖的服务有大量超时时在让新的请求去访问根本没有意义只会无畏的消耗现有资源。比如我们设置了超时时间为1s,如果短时间内有大量请求在1s内都得不到响应就意味着这个服务出现了异常此时就没有必要再让其他的请求去访问这个依赖了这个时候就应该使用断路器避免资源浪费。】 1.4 降级 所谓降级就是我们调用的服务异常超时等原因不能正常返回的情况下我们返回一个缺省的值。 【由于降级经常和熔断一起使用所以就会有熔断降级的说法。】 2、常见的容错组件 Hystrix Hystrix是由Netflix开源的一个延迟和容错库用于隔离访问远程系统、服务或者第三方库防止 级联失败从而提升系统的可用性与容错性。 Resilience4J Resilicence4J一款非常轻量、简单并且文档非常清晰、丰富的熔断工具这也是Hystrix官方推 荐的替代产品。不仅如此Resilicence4j还原生支持Spring Boot 1.x/2.x而且监控也支持和prometheus等多款主流产品进行整合。 Sentinel Sentinel 是阿里巴巴开源的一款断路器实现本身在阿里内部已经被大规模采用非常稳定。 下面是三个组件在各方面的对比 SentinelHystrixresilience4j隔离策略信号量隔离并发线程数限流线程池隔离/信号量隔离信号量隔离熔断降级策略基于慢调用比例、异常比例、异常数基于异常比例基于异常比例、响应时间实时统计实现滑动窗口LeapArray滑动窗口基于 RxJavaRing Bit Buffer动态规则配置支持近十种动态数据源支持多种数据源有限支持扩展性多个扩展点插件的形式接口的形式基于注解的支持支持支持支持单机限流基于 QPS支持基于调用关系的限流有限的支持Rate Limiter集群流控支持不支持不支持流量整形支持预热模式与匀速排队控制效果不支持简单的 Rate Limiter 模式系统自适应保护支持不支持不支持热点识别/防护支持不支持不支持多语言支持Java/Go/CJavaJavaService Mesh 支持支持 Envoy/Istio不支持不支持控制台font color“red”提供开箱即用的控制台可配置规则、实时监控、机器发现等/fontfont color“red”简单的监控查看/fontfont color“red”不提供控制台可对接其它监控系统/font 三、sentinel基本操作 1、 什么是Sentinel Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。 Sentinel 具有以下特征: 丰富的应用场景Sentinel承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀即突发流量控制在系统容量可以承受的范围、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。完备的实时监控Sentinel提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。广泛的开源生态Sentinel提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。完善的 SPI 扩展点Sentinel提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。 Sentinel 分为两个部分: 核心库Java 客户端不依赖任何框架/库,能够运行于所有Java 运行时环境同时对Dubbo/Spring Cloud 等框架也有较好的支持。控制台Dashboard基于Spring Boot开发打包后可以直接运行不需要额外的Tomcat等应用容器。 2、基本概念 资源 资源就是Sentinel要保护的东西 资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容例如由应用程序提供的服务或由应用程序调用的其它应用提供的服务甚至可以是一段代码。 规则 规则就是用来定义如何进行保护资源的 围绕资源的实时状态设定的规则可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整 3、Sentinel 功能和设计理念 Sentinel的主要功能就是容错主要体现为下面这三个 3.1 流量控制上游 流量控制在网络传输中是一个常用的概念它用于调整网络包的数据。任意时间到来的请求往往是随机不可控的而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。 Sentinel作为一个调配器可以根据需要把随机的请求调整成合适的形状。 3.2 熔断降级下游 当检测到调用链路中某个资源出现不稳定的表现例如请求响应时间长或异常比例升高的时候则对这个资源的调用进行限制让请求快速失败或者其他处理方式避免影响到其它的资源而导致级联故障。 5、Sentinel快速开始 在官方文档中定义的Sentinel进行资源保护的几个步骤 定义资源定义规则检验规则是否生效 5.1 抛出异常的方式定义资源 Entry entry null; // 务必保证 finally 会被执行 try {// 资源名可使用任意有业务语义的字符串注意数目不能太多超过 1K超出几千请作为参数传入而不要直接作为资源名// EntryType 代表流量类型inbound/outbound其中系统规则只对 IN 类型的埋点生效entry SphU.entry(自定义资源名);// 被保护的业务逻辑// do something... } catch (BlockException ex) {// 资源访问阻止被限流或被降级// 进行相应的处理操作 } catch (Exception ex) {// 若需要配置降级规则需要通过这种方式记录业务异常Tracer.traceEntry(ex, entry); } finally {// 务必保证 exit务必保证每个 entry 与 exit 配对if (entry ! null) {entry.exit();} }5.2 api的实现 font color“red”msb-user项目中/font 引入依赖 dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-core/artifactIdversion1.8.1/version /dependency代码测试 Slf4j RestController public class ApiController {private static final String RESOURCE_NAME API-RESOURCE;RequestMapping(getInfo)public String getInfo(){Entry entry null;// 务必保证 finally 会被执行try {// 资源名可使用任意有业务语义的字符串注意数目不能太多超过 1K超出几千请作为参数传入而不要直接作为资源名// EntryType 代表流量类型inbound/outbound其中系统规则只对 IN 类型的埋点生效entry SphU.entry(RESOURCE_NAME);// 被保护的业务逻辑String str 业务逻辑正常处理;log.info(str);return str;} catch (BlockException ex) {// 资源访问阻止被限流或被降级// 进行相应的处理操作log.info(Block...!);return 业务被限流了;} catch (Exception ex) {// 若需要配置降级规则需要通过这种方式记录业务异常Tracer.traceEntry(ex, entry);} finally {// 务必保证 exit务必保证每个 entry 与 exit 配对if (entry ! null) {entry.exit();}}return null;}/*** 定义流控规则*/PostConstructprivate static void initFlowRules(){ListFlowRule rules new ArrayList();FlowRule rule new FlowRule();//设置受保护的资源rule.setResource(RESOURCE_NAME);// 设置流控规则 QPSrule.setGrade(RuleConstant.FLOW_GRADE_QPS);// 设置受保护的资源阈值rule.setCount(1);rules.add(rule);// 加载配置好的规则FlowRuleManager.loadRules(rules);} }缺点 业务侵入性很强需要在controller中写入非业务代码.配置不灵活 若需要添加新的受保护资源 需要手动添加 init方法来添加流控规则 5.3 注解方式定义资源 SentinelResource注解实现 在定义了资源点之后我们可以通过Dashboard来设置限流和降级策略来对资源点进行保护。同时还能 通过SentinelResource来指定出现异常时的处理策略。 SentinelResource用于定义资源并提供可选的异常处理和fallback 配置项。其主要参数如下: 属性作用value资源名称entryTypeentry类型标记流量的方向取值IN/OUT默认是OUTblockHandler处理BlockException的函数名称,函数要求br1.必须是publicbr/2.返回类型 参数与原方法一致br/3.默认需和原方法在同一个类中。若希望使用其他类的函数可配置blockHandlerClass并指定blockHandlerClass里面的方法。blockHandlerClass存放blockHandler的类,对应的处理函数必须static修饰。fallback用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常除了 exceptionsToIgnore 里面排除掉的异常类型进行处理。函数要求br/1.返回类型与原方法一致br/2.参数类型需要和原方法相匹配br/3.默认需和原方法在同一个类中。若希望使用其他类的函数可配置fallbackClass 并指定fallbackClass里面的方法。fallbackClass存放fallback的类。对应的处理函数必须static修饰。defaultFallback用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进行处理。若同时配置了 fallback 和 defaultFallback以fallback为准。函数要求br/1.返回类型与原方法一致br/2.方法参数列表为空或者有一个Throwable类型的参数。br/3.默认需要和原方法在同一个类中。若希望使用其他类的函数可配置fallbackClass 并指定 fallbackClass 里面的方法。exceptionsToIgnore指定排除掉哪些异常。排除的异常不会计入异常统计也不会进入fallback逻辑而是原样抛出。exceptionsToTrace需要trace的异常 引入依赖 dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-annotation-aspectj/artifactIdversion1.8.1/version /dependency切面支持 Configuration public class SentinelAspectConfiguration {Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();} }代码编写 font color“red”注意 这里面必须把以前的PostConstruct导入的规则给注释掉否则可能有冲突/font Slf4j RestController public class SentinelResourceController {//http://localhost:8001/testAspect/12GetMapping(/testAspect/{id})SentinelResource(value testAspect,fallback fallback,fallbackClass CommonException.class,blockHandler handleException,blockHandlerClass CommonException.class)public Result testAspect(PathVariable(id) Integer id){if(Integer.compare(id,Integer.parseInt(0)) -1){throw new IllegalArgumentException(参数异常);}log.info(处理业务信息);return Result.ok(测试注解方式限流正常);}PostConstructprivate static void initFlowRules(){ListFlowRule rules new ArrayList();FlowRule rule new FlowRule();//设置受保护的资源rule.setResource(testAspect);// 设置流控规则 QPSrule.setGrade(RuleConstant.FLOW_GRADE_QPS);// 设置受保护的资源阈值rule.setCount(1);rules.add(rule);// 加载配置好的规则FlowRuleManager.loadRules(rules);} } Slf4j public class CommonException {public static Result fallback(Integer id,Throwable e){log.error(出现业务异常);return Result.error(-1,业务异常);}public static Result handleException(Integer id, BlockException e){log.error(触发限流机制);return Result.error(-2,触发限流机制);} }这里的规则需要我们自己编写并且我们这里 5.4 整合springboot 引入依赖 dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependency修改配置 spring: cloud:sentinel: transport:port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可dashboard: localhost:8080 # 指定控制台服务的地址5.5 引入控制台 Sentinel 提供一个轻量级的控制台, 它提供机器发现、单机资源实时监控以及规则管理等功能。 第1步 下载jar包,解压到文件夹 https://github.com/alibaba/Sentinel/releases 第2步 启动控制台 # 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目) java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.1.jar把dashboard自己也当成一个资源加入到了dashboard中来进行监控如果不想把dashboard自己加入控制台监控可以使用简单启动指令如下: java -Dserver.port8080 -jar sentinel-dashboard-1.8.1.jar第4步: 访问控制台 用户可以通过如下参数进行配置 -Dsentinel.dashboard.auth.usernamesentinel 用于指定控制台的登录用户名为 sentinel -Dsentinel.dashboard.auth.password123456 用于指定控制台的登录密码为 123456如果省略这两个参数默认用户和密码均为 sentinel -Dserver.servlet.session.timeout7200 用于指定 Spring Boot 服务端 session 的过期时间如 7200 表示 7200 秒60m 表示 60 分钟默认为 30 分钟 访问http://localhost:8080/#/login ,默认用户名密码 sentinel/sentinel font color“red”Sentinel 会在客户端首次调用的时候进行初始化开始向控制台发送心跳包所以要确保客户端有访问量/font 补充了解控制台的使用原理 Sentinel的控制台其实就是一个SpringBoot编写的程序。我们需要将我们的微服务程序注册到控制台上, 即在微服务中指定控制台的地址, 并且还要开启一个跟控制台传递数据的端口, 控制台也可以通过此端口调用微服务中的监控程序获取微服务的各种信息。 6、Sentinel规则dashboard 6.1 流控规则 流量控制flow control其原理是监控应用流量的 QPS 或并发线程数等指标当达到指定的阈值时对流量进行控制以避免被瞬时的流量高峰冲垮从而保障应用的高可用性。一条限流规则主要由下面几个因素组成我们可以组合这些元素来实现不同的限流效果 Field说明默认值resource资源名即限流规则的作用对象count限流阈值grade限流阈值类型QPS 或并发线程数QPS 模式limitApp流控针对的调用来源default代表不区分调用来源strategy调用关系限流策略直接、链路、关联直接controlBehavior流量控制效果直接拒绝、Warm Up、匀速排队直接拒绝clusterMode是否集群限流否 流量控制其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标当达到指定的阈值时对流量进行控制以避免被瞬时的流量高峰冲垮从而保障应用的高可用性。 第1步: 点击【簇点链路】我们就可以看到访问过的接口地址然后点击对应的【流控】按钮进入流控规则配置页面。新增流控规则界面如下: **资源名**唯一名称默认是请求路径可自定义。 **针对来源**Sentinel可以针对调用者进行限流,填写【微服务名】,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)。 阈值类型/单机阈值 QPS每秒请求数量: 当调用该接口的QPS达到阈值的时候进行限流。线程数当调用该接口的线程数达到阈值的时候进行限流。 **是否集群**暂不需要集群 接下来我们以QPS为例来研究限流规则的配置。 6.1.1 简单配置 QPS 代码 //com.msb.controller.SimpleController RestController public class SimpleController {GetMapping(/simple/qps)public String qps(){//模拟一次网络延时try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}return 简单测试qps;}}流控规则 快速访问/simple/qps 接口观察效果。 此时发现当QPS 1的时候服务就不能正常响应而是返回Blocked by Sentinel (flow limiting)结果。 http://localhost:8001/simple/qps font color“red”BlockException异常统一处理/font springwebmvc接口资源限流入口在HandlerInterceptor的实现类AbstractSentinelInterceptor的preHandle方法中对异常的处理是BlockExceptionHandler的实现类 sentinel 1.7.1 引入了sentinel-spring-webmvc-adapter.jar 自定义BlockExceptionHandler 的实现类统一处理BlockException package com.msb.handle;Slf4j Component public class CustomBlockExceptionHandler implements BlockExceptionHandler {Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {log.info(BlockExceptionHandler BlockExceptione.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);} }线程数 并发数控制用于保护业务线程池不被慢调用耗尽。例如当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加对于调用者来说意味着吞吐量下降和更多的线程数占用极端情况下甚至导致线程池耗尽。 【为应对太多线程占用的情况业内有使用隔离的方案比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢线程池隔离。这种隔离方案虽然隔离性比较好但是代价就是线程数目太多线程上下文切换的 overhead 比较大特别是对低延时的调用有比较大的影响。】 font color“red”Sentinel 并发控制不负责创建和管理线程池而是简单统计当前请求上下文的线程数目正在执行的调用数目如果超出阈值新的请求会被立即拒绝效果类似于信号量隔离。并发数控制通常在调用端进行配置。/font 代码 //com.msb.controller.SimpleController#testThread GetMapping(/simple/thread) public String testThread(){//模拟一次网络延时try {Thread.sleep(2*1000);} catch (InterruptedException e) {e.printStackTrace();}return 简单测试线程; }流控规则 快速访问/simple/thread接口观察效果。 6.1.2 配置流控模式 点击上面设置流控规则的**【编辑】按钮然后在编辑页面点击【高级选项】**会看到有流控模式一栏。 sentinel共有三种流控模式分别是 直接默认指定来源对于该资源的访问达到限流条件时开启限流。关联当与该资源设置了关联的资源达到限流条件来源阈值类型单机阈值时开启限流 [适合做应用让步]链路当从某个上游资源接口访问过来的流量达到限流条件时开启限流。 下面呢分别演示三种模式 直接流控模式 直接流控模式是最简单的模式当指定的接口达到限流条件时开启限流。上面案例使用的就是直接流控模式。 关联流控模式 当两个资源之间具有资源争抢或者依赖关系的时候这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢读的速度过高会影响写得速度写的速度过高会影响读的速度。如果放任读写操作争抢资源则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢举例来说read_db 和 write_db 这两个资源分别代表数据库读写我们可以给 read_db 设置限流规则来达到写优先的目的设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时读数据的请求会被限流。 代码 RestController public class FlowControllerModleContoller {GetMapping(/flowModle/read)public String readRead(Integer orderId){return 读资源;}GetMapping(/flowModle/write)public String relation2(Integer orderId){return 写资源;} }流控规则 jmeter测试 查看监控图 链路流控模式 链路流控模式指的是当从某个接口过来的资源达到限流条件时开启限流。它的功能有点类似于针对 来源配置项区别在于针对来源是针对上级微服务而链路流控是针对上级接口也就是说它的粒度更细。 代码 编写controller //com.msb.controller.FlowControllerModleContoller GetMapping(/linkModle/test1) public String test1(Integer userId){userService.getUser();return 成功; } GetMapping(/linkModle/test2) public String test2(Integer userId){userService.getUser();return 成功; }编写service Service public class UserService {SentinelResource(value getUser)public String getUser(){return 获取到用户信息;} }流控规则 添加配置 我们需要将web-context-unify参数设置为false将其配置为false既可以根据不同的URL进行链路限流如果不配置将不会生效 jmeter测试 访问/linkModle/test1 6.1.3 流控效果 当 QPS 超过某个阈值的时候则采取措施进行流量控制。流量控制的效果包括以下几种快速失败直接拒绝、Warm Up预热、匀速排队排队等待。对应 FlowRule 中的 controlBehavior 字段。 快速失败默认 font color “red”当QPS超过任意规则的阈值后新的请求就会被立即拒绝拒绝方式为抛出FlowException/font这种方式适用于对系统处理能力确切已知的情况下比如通过压测确定了系统的准确水位时。 Warm Up 它从开始阈值到最大QPS阈值会有一个缓冲阶段一开始的阈值是最大QPS阈值的1/3然后慢慢增长直到最大阈值。 即预热/冷启动方式。当系统长期处于低水位的情况下当流量突然增加时直接把系统拉升到高水位可能瞬间把系统压垮。通过冷启动让通过的流量缓慢增加在一定时间内逐渐增加到阈值上限给冷系统一个预热的时间避免冷系统被压垮。 font color“red”冷加载因子: codeFactor 默认是3即请求 QPS 从 threshold / 3 开始经预热时长逐渐升至设定的 QPS 阈值。 /font 通常冷启动的过程系统允许通过的 QPS 曲线如下图所示 场景主要用于启动需要额外开销的场景例如建立数据库连接等。 代码 RestController public class FlowControlEffectController {RequestMapping(/warmup)public String testWarmUp(){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return 测试流控效果热启动;} }2、流控规则 3、jmeter测试 4、查看监控结果 查看你会发现首先会达到3 阈值的1/3然后等3秒后会达到阈值10 排队等待 让请求以均匀的速度通过单机阈值为每秒通过数量其余的排队等待 它还会让设置一个超时时间当请求超过超时间时间还未处理则会被丢弃。 该方式的作用如下图所示 这种方式主要用于处理间隔性突发的流量例如消息队列。想象一下这样的场景在某一秒有大量的请求到来而接下来的几秒则处于空闲状态我们希望系统能够在接下来的空闲期间逐渐处理这些请求而不是在第一秒直接拒绝多余的请求。 font color“red”注意匀速排队模式暂时不支持 QPS 1000 的场景。/font【因为他的单位毫秒所以最多是1秒通过一个也就是1000qps】 代码 com.msb.controller.FlowControlEffectController#testRateLimiter RequestMapping(/rateLimiter) public String testRateLimiter(){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return 测试流控效果匀速排队; }流控规则 jmeter测试 查看监控图 6.2 降级规则 除了流量控制以外对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块可能是另外的一个远程服务、数据库或者第三方 API 等。例如支付的时候可能需要远程调用银联提供的 API查询某个商品的价格可能需要进行数据库查询。然而这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况请求的响应时间变长那么调用服务的方法的响应时间也会变长线程会产生堆积最终可能耗尽业务自身的线程池服务本身也变得不可用。 熔断降级规则说明熔断降级规则DegradeRule包含下面几个重要的属性 Field说明默认值resource资源名即规则的作用对象grade熔断策略支持慢调用比例/异常比例/异常数策略慢调用比例count最小请求数慢调用比例模式下为慢调用临界 RT超出该值计为慢调用异常比例/异常数模式下为对应的阈值timeWindow熔断时长单位为 sminRequestAmount最小请求数熔断触发的最小请求数请求数小于该值时即使异常比率超出阈值也不会熔断1.7.0 引入5statIntervalMs统计时长单位为 ms如 60*1000 代表分钟级1.8.0 引入1000 msslowRatioThreshold比例阈值慢调用比例阈值仅慢调用比例模式有效1.8.0 引入 降级规则就是设置当满足什么条件的时候对服务进行降级。Sentinel提供了三个衡量条件平均响应时间、异常比例、异常数。 6.2.1 慢调用比例 慢调用比例 (SLOW_REQUEST_RATIO)选择以慢调用比例作为阈值需要设置允许的慢调用 RT即最大的响应时间请求的响应时间大于该值则统计为慢调用。当单位统计时长statIntervalMs内请求数目大于设置的最小请求数目并且慢调用的比例大于阈值则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态HALF-OPEN 状态若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断若大于设置的慢调用 RT 则会再次被熔断。 代码 //com.msb.controller.DegradationRuleController#slowRequestRatio RequestMapping(/slowRequestRatio) public String slowRequestRatio(){try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return 熔断降级慢调用比例; }流控规则 jmeter测试 查看监控图 查看效果发生流控每两秒重试一次我们可以通过查看jemter中查看结果树来判断 会发现有个重试每次重试间隔应该是两秒 6.2.2 异常比例 当单位统计时长statIntervalMs内请求数目大于设置的最小请求数目并且异常的比例大于阈值则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态HALF-OPEN 状态若接下来的一个请求成功完成没有错误则结束熔断否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0]代表 0% - 100%。 代码 //com.msb.controller.DegradationRuleController#errorRatio // 下面这个方式有待研究 private AtomicInteger atomicInteger new AtomicInteger(1); RequestMapping(/errorRatio) public String errorRatio(){atomicInteger.getAndIncrement();log.info(现在的数值{},atomicInteger.get() );if (atomicInteger.get() % 2 0){//模拟异常和异常比率int i 1/0;}return 熔断降级异常比例; } 流控规则 jmeter测试 查看监控图 6.2.3 异常数 当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态HALF-OPEN 状态若接下来的一个请求成功完成没有错误则结束熔断否则会再次被熔断。 font color “red”注意:异常降级仅针对业务异常对 Sentinel 限流降级本身的异常BlockException不生效。/font 代码 //com.msb.controller.DegradationRuleController#errorCount RequestMapping(/errorCount) public String errorCount(){atomicInteger.getAndIncrement();if (atomicInteger.get() % 2 0){//模拟异常和异常比率int i 1/0;}return 熔断降级异常数量; }流控规则 jmeter测试 查看监控图 6.3 热点规则 热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。 何为热点热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据并对其访问进行限制。比如 商品 ID 为参数统计一段时间内最常购买的商品 ID 并进行限制用户 ID 为参数针对一段时间内频繁访问的用户 ID 进行限制 font color“red”热点参数限流会统计传入参数中的热点参数并根据配置的限流阈值与模式对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制仅对包含热点参数的资源调用生效。/font 代码 //http://localhost:8001/hotspot/123 RestController public class HotSpotRuleController {RequestMapping(/hotspot/{id})SentinelResource(value hotspot,blockHandlerClass CommonException.class,blockHandler handleException,fallbackClass CommonException.class,fallback fallback)public Result info(PathVariable(id) Integer id){return Result.ok(成功获取数据);} }注意 热点规则需要使用SentinelResource(“resourceName”)注解否则不生效参数必须是7种基本数据类型才会生效 流控规则 注意 资源名必须是SentinelResource(value“资源名”)中 配置的资源名热点规则依赖于注解 这里有个坑经过上面的配置只是对接口的限流如果具体到参数需要再热点规则中重新设置参数 具体到参数值限流配置参数值为1,限流阈值为2 jmeter测试 查看监控图 6.4 授权规则 很多时候我们需要根据调用来源来判断该次请求是否允许放行这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源origin限制资源是否通过 若配置白名单则只有请求来源位于白名单内时才可通过若配置黑名单则请求来源位于黑名单时不通过其余的请求通过。 例如活动和订单都会调用用户系统获取用户信息我们可以将活动设置为黑名单 上面的【资源名】和【授权类型】不难理解但是【流控应用】怎么填写呢 其实这个位置要填写的是来源标识Sentinel提供了RequestOriginParser 接口来处理来源。 只要Sentinel保护的接口资源被访问Sentinel就会调用RequestOriginParser 的实现类去解析访问来源。 第1步: 自定义来源处理规则 //http://localhost:8001/grant RestController public class GrantController {RequestMapping(/grant)public Result grant(){return Result.ok(授权规则测试成功);} }Component public class RequestOriginParserDefinition implements RequestOriginParser{Overridepublic String parseOrigin(HttpServletRequest request) { String serviceName request.getParameter(serviceName); return serviceName;} }第2步: 授权规则配置 这个配置的意思是只有serviceNameorder不能访问(黑名单) 第3步: 访问 http://localhost:8001/grant?serviceNameorder观察结果 6.4.1在feign中将参数设置进去 msb-order项目中光阴如引入feign的代码可以参看Feign的文档 public class FeignAuthRequestInterceptor implements RequestInterceptor {private String serviceName;public FeignAuthRequestInterceptor(String serviceName) {this.serviceName serviceName;}Overridepublic void apply(RequestTemplate template) {template.header(serviceName,serviceName);} }Configuration public class FeignConfig {Value(${spring.application.name})private String serviceName;/*** 自定义拦截器* return*/Beanpublic FeignAuthRequestInterceptor feignAuthRequestInterceptor(){return new FeignAuthRequestInterceptor(serviceName);} }在msb-stock项目中增加配置 Component public class RequestOriginParserDefinition implements RequestOriginParser {Overridepublic String parseOrigin(HttpServletRequest request) {String serviceName request.getHeader(serviceName);return serviceName;} }可以对异常进行包装 package com.msb.order.handle; Slf4j Component public class CustomBlockExceptionHandler implements BlockExceptionHandler {Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {log.info(BlockExceptionHandler BlockExceptione.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);} }6.5 系统规则 系统保护规则是从应用级别的入口流量进行控制从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。 系统保护规则是应用整体维度的而不是资源维度的并且仅对入口流量生效。入口流量指的是进入应用的流量EntryType.IN比如 Web 服务或 Dubbo 服务端接收的请求都属于入口流量。 系统规则支持以下的模式 Load 自适应仅对 Linux/Unix-like 机器生效系统的 load1 作为启发指标进行自适应系统保护。当系统 load1 超过设定的启发值且系统当前的并发线程数超过估算的系统容量时才会触发系统保护BBR 阶段。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。CPU usage1.5.0 版本当系统 CPU 使用率超过阈值即触发系统保护取值范围 0.0-1.0比较灵敏。平均 RT当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护单位是毫秒。并发线程数当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。入口 QPS当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。 这些不是很好测试我们可以此时一下QPS 代码 RestController public class SystemRuleController {RequestMapping(/systemRule)public Result systemRule(){return Result.ok(测试系统规则QPS);} }流控规则 jmeter测试 查看监控图 大于阈值之后进行限流 6.6 集群规则 https://github.com/alibaba/Sentinel/wiki/%E9%9B%86%E7%BE%A4%E6%B5%81%E6%8E%A7 为什么要使用集群流控呢假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50但机器数可能很多比如有 100 台。这时候我们很自然地就想到找一个 server 来专门来统计总的调用量其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。 另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器我们给每台机器设置单机限流阈值为 10 QPS理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量结合单机限流兜底可以更好地发挥流量控制的效果 部署方式 独立模式Alone即作为独立的 token server 进程启动独立部署隔离性好但是需要额外的部署操作。独立模式适合作为 Global Rate Limiter 给集群提供流控服务。 嵌入模式Embedded即作为内置的 token server 与服务在同一进程中启动。在此模式下集群中各个实例都是对等的token server 和 client 可以随时进行转变因此无需单独部署灵活性比较好。但是隔离性不佳需要限制 token server 的总 QPS防止影响应用本身。嵌入模式适合某个应用集群内部的流控。 4.8 Sentinel规则持久化 推送模式说明优点缺点原始模式API将规则推送至客户端并直接更新到内存中扩展写数据源简单、无任何依赖不保证一致性;规则保存在内存中重启不建议用于生产环境PULL模式客户端主动向某个规则管理中心定期轮询拉取规则br /这个规则中心可以是 RDBMS.文件等简单、无任何依赖规则持久化不保证一致性实时性不保证拉取过于频繁也可能会有性能问题PUSH模式规则中心统一推送客户端通过注册监听br /方式有更好的实时性和一致性保证。生产Nacosbr /、Zookeeper等配置中心。这种方式有更好的实时性和一致性保证。br /生产环境下一般采用push模式的数据源。规则持久化一致性快速引入第三方依赖 4.8.1 原始模式 如果不做任何修改Dashboard 的推送规则方式是通过 API 将规则推送至客户端并直接更新到内存中 4.8.2Pull模式 pull 模式的数据源如本地文件、RDBMS 等一般是可写入的。使用时需要在客户端注册数据源将对应的读数据源注册至对应的 RuleManager将写数据源注册至 transport 的 WritableDataSourceRegistry 中。以本地文件数据源为例**** font color“red”本地文件数据源会定时轮询文件的变更读取规则。/font这样我们既可以在应用本地直接修改文件来更新规则也可以通过 Sentinel 控制台推送规则。以本地文件数据源为例推送过程如下图所示 大家可以发现整个过程比较繁重并且每次都需要更新本地文件他的性能有一定的影响 4.8.3 Push模式 生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心ZooKeeper, Nacos, Apollo等等我们在sentinel Dashboard配置的规则会同步到Nacos中 Sentinel Dashboard也会从Nacos中获取规则Nacos会讲规则推送给Sentinel客户端 复制测试代码 在sentinel-dashboard中的测试包中会有对阿波罗nacos和zk的支持 对nacos进行复制copy到rule规则下 我们会发现对应的类会报红这是应为对应的sentinel包没有引入进去 我们进入对应的pom会发现里面有不对应的sentinel包只是scope设置的是test我们需要去掉范围就可以 修改代码改造我们nacos注册地址 改造对应的NacosConfig文件 更改注入的类 这里就是相当于他对规则的所有操作就是走我们nacos的操作。 改造页面新增菜单 app\scripts\directives\sidebar\sidebar.html 启动dashboard 如果启动后没有发现流控规则 nacos 那我们可以使用无痕模式 此时我们增加一个流控规则 此时我们查看nacos就会发现里面已经同步了我们增加的流控规则此时我们可以在nacos修改规则也可以在sentinel修改规则 更改客户端 增加依赖 dependencygroupIdcom.alibaba.csp/groupIdartifactIdsentinel-datasource-nacos/artifactId /dependency添加配置 spring:cloud:sentinel:datasource:# 名称随意flow:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-flow-rulesgroupId: SENTINEL_GROUP# 规则类型取值见# org.springframework.cloud.alibaba.sentinel.datasource.RuleTyperule-type: flowdegrade:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-degrade-rulesgroupId: SENTINEL_GROUPrule-type: degradesystem:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-system-rulesgroupId: SENTINEL_GROUPrule-type: systemauthority:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-authority-rulesgroupId: SENTINEL_GROUPrule-type: authorityparam-flow:nacos:server-addr: localhost:8848dataId: ${spring.application.name}-param-flow-rulesgroupId: SENTINEL_GROUPrule-type: param-flow4.9 RestTemplate整合Sentinel 引入restTemplate EnableDiscoveryClient SpringBootApplication EnableFeignClients public class OrderApplication {BeanLoadBalancedSentinelRestTemplatepublic RestTemplate restTemplate(){return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(OrderApplication.class);} } package com.msb.controller;import com.msb.util.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;RestController public class RestTemplateController {Autowiredprivate RestTemplate restTemplate;RequestMapping(/testRestTemplate/{userId})public String getUser(PathVariable Integer userId){String result restTemplate.getForObject(http://msb-user:10001/user/ userId, String.class);return result;} }流控规则 测试 http://localhost:8001/testRestTemplate/222 异常丑陋的更改 重写这些方法 EnableDiscoveryClient SpringBootApplication EnableFeignClients public class OrderApplication {BeanLoadBalancedSentinelRestTemplate(blockHandler handleException,blockHandlerClass GlobalException.class,fallback fallback,fallbackClass GlobalException.class)public RestTemplate restTemplate(){return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(OrderApplication.class);}}package com.msb.exception;import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpRequest; import com.msb.util.Result; import org.springframework.http.client.ClientHttpRequestExecution;public class GlobalException {public static SentinelClientHttpResponse handleException(HttpRequest request,byte[] body, ClientHttpRequestExecution execution, BlockException ex) {Result r Result.error(-1, 被限流啦);try {return new SentinelClientHttpResponse(new ObjectMapper().writeValueAsString(r));} catch (JsonProcessingException e) {e.printStackTrace();}return null;}public static SentinelClientHttpResponse fallback(HttpRequest request,byte[] body, ClientHttpRequestExecution execution, BlockException ex) {Result r Result.error(-2, 被异常降级啦);try {return new SentinelClientHttpResponse(new ObjectMapper().writeValueAsString(r));} catch (JsonProcessingException e) {e.printStackTrace();}return null;} }源码分析 我们想我们ribbon是怎样restTemplate 以前是不是通过类LoadBalancerInterceptor 来进行整合的那我们的sentinel是不是也可是通过拦截器来进行整合的 在这里进行拦截的。 4.10 Feign整合Sentinel 4.9 Feign整合Sentinel 引入sentinel的依赖 !--sentinel客户端-- dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependency!-- openfeign 远程调用 -- dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId /dependency在配置文件中开启Feign对Sentinel的支持 feign:sentinel: enabled: true创建限流处理类 Component public class FallbackOrderFeignServiceFactory implements FallbackFactoryUserFeignService {Overridepublic UserFeignService create(Throwable throwable) {return new UserFeignService() {Overridepublic String getUserName(Integer userId) {if (throwable instanceof FlowException) {return Result.error(100,接口限流了);}return Result.error(-1,服务降级了);}};} } 为被容错的接口指定容错类 //value用于指定调用nacos下哪个微服务 //fallbackFactory指定限流处理类 FeignClient(value msb-user,fallbackFactory FallbackOrderFeignServiceFactory.class) public interface UserFeignService {GetMapping(/user/{userId})Result getUserName(PathVariable Integer userId); } 修改controller RestController public class FeignOrderController {Autowiredprivate UserFeignService userFeignService;RequestMapping(/getOrders/{userId})public Result getOrders(PathVariable(userId) Integer userId){Result resultUserInfo userFeignService.getUserName(userId);/*** 处理其它业务*/return resultUserInfo;} }修改UserController RestController public class UserController {GetMapping(/user/{userId})public Result getUserName(PathVariable Integer userId){return Result.ok(善缘老师);} }修改流控规则 进行调用 http://localhost:8001/getOrders/213 流控效果 四、限流算法 1、计数器固定窗口算法 计数器固定窗口算法是最简单的限流算法实现方式也比较简单。就是通过维护一个单位时间内的计数值每当一个请求通过时就将计数值加1当计数值超过预先设定的阈值时就拒绝单位时间内的其他请求。如果单位时间已经结束则将计数器清零开启下一轮的计数。 package com.msb.sentinel; import com.msb.sentinel.exception.BlockException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class CounterRateLimit implements RateLimit , Runnable {/*** 阈值*/private Integer limitCount;/*** 当前通过请求数*/private AtomicInteger passCount;/*** 统计时间间隔*/private long period;private TimeUnit timeUnit;private ScheduledExecutorService scheduledExecutorService;public CounterRateLimit(Integer limitCount) {this(limitCount, 1000, TimeUnit.MILLISECONDS);}public CounterRateLimit(Integer limitCount, long period, TimeUnit timeUnit) {this.limitCount limitCount;this.period period;this.timeUnit timeUnit;passCount new AtomicInteger(0);this.startResetTask();}Overridepublic boolean canPass() throws BlockException {if (passCount.incrementAndGet() limitCount) {throw new BlockException();}return true;}//启动一定定时任务线程每过一个周期就将我们通过个数重置为0private void startResetTask() {scheduledExecutorService Executors.newSingleThreadScheduledExecutor();scheduledExecutorService.scheduleAtFixedRate(this, 0, period, timeUnit);}Overridepublic void run() {passCount.set(0);} } 但是这种实现会有一个问题举个例子: 假设我们设定1秒内允许通过的请求阈值是99如果有用户在时间窗口的最后几毫秒发送了99个请求紧接着又在下一个时间窗口开始时发送了200个请求那么这个用户其实在一秒内成功请求了198次显然超过了阈值但并不会被限流。其实这就是临界值问题那么临界值问题要怎么解决呢 2、计数器滑动窗口算法 算法演示 计数器滑动窗口法就是为了解决上述固定窗口计数存在的问题而诞生。前面说了固定窗口存在临界值问题要解决这种临界值问题显然只用一个窗口是解决不了问题的。假设我们仍然设定1秒内允许通过的请求是200个但是在这里我们需要把1秒的时间分成多格假设分成5格格数越多流量过渡越平滑每格窗口的时间大小是200毫秒每过200毫秒就将窗口向前移动一格。为了便于理解可以看下图 图中将窗口划为5份每个小窗口中的数字表示在这个窗口中请求数所以通过观察上图可知在当前时间快200毫秒允许通过的请求数应该是70而不是200只要超过70就会被限流因为我们最终统计请求数时是需要把当前窗口的值进行累加进而得到当前请求数来判断是不是需要进行限流。 那么滑动窗口限流法是完美的吗 细心观察的我们应该能马上发现问题滑动窗口限流法其实就是计数器固定窗口算法的一个变种。流量的过渡是否平滑依赖于我们设置的窗口格数也就是统计时间间隔格数越多统计越精确但是具体要分多少格我们也说不上来呀… package com.msb.sentinel;import com.msb.sentinel.exception.BlockException; import lombok.Data; import lombok.extern.slf4j.Slf4j;import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; Slf4j public class SlidingWindowRateLimit implements RateLimit, Runnable {/*** 阈值*/private Integer limitCount;/*** 当前通过的请求数*/private AtomicInteger passCount;/*** 窗口数*/private Integer windowSize;/*** 每个窗口时间间隔大小*/private long windowPeriod;private TimeUnit timeUnit;private Window[] windows;private volatile Integer windowIndex 0;private Lock lock new ReentrantLock();public SlidingWindowRateLimit(Integer limitCount) {// 默认统计qps, 窗口大小5this(limitCount, 5, 200, TimeUnit.MILLISECONDS);}/*** 统计总时间 windowSize * windowPeriod*/public SlidingWindowRateLimit(Integer limitCount, Integer windowSize, Integer windowPeriod, TimeUnit timeUnit) {this.limitCount limitCount;this.windowSize windowSize;this.windowPeriod windowPeriod;this.timeUnit timeUnit;this.passCount new AtomicInteger(0);this.initWindows(windowSize);this.startResetTask();}Overridepublic boolean canPass() throws BlockException {lock.lock();if (passCount.get() limitCount) {throw new BlockException();}windows[windowIndex].passCount.incrementAndGet();passCount.incrementAndGet();lock.unlock();return true;}private void initWindows(Integer windowSize) {windows new Window[windowSize];for (int i 0; i windowSize; i) {windows[i] new Window();}}private ScheduledExecutorService scheduledExecutorService;private void startResetTask() {scheduledExecutorService Executors.newSingleThreadScheduledExecutor();scheduledExecutorService.scheduleAtFixedRate(this, windowPeriod, windowPeriod, timeUnit);}Overridepublic void run() {// 获取当前窗口索引Integer curIndex (windowIndex 1) % windowSize;// 重置当前窗口索引通过数量并获取上一次通过数量Integer count windows[curIndex].passCount.getAndSet(0);windowIndex curIndex;// 总通过数量 减去 当前窗口上次通过数量passCount.addAndGet(-count);}Dataclass Window {private AtomicInteger passCount;public Window() {this.passCount new AtomicInteger(0);}} }3、漏桶算法 上面所介绍的两种算法都不能非常平滑的过渡下面就是漏桶算法登场了 什么是漏桶算法 漏桶算法以一个常量限制了出口流量速率因此漏桶算法可以平滑突发的流量。其中漏桶作为流量容器我们可以看做一个FIFO的队列当入口流量速率大于出口流量速率时因为流量容器是有限的当超出流量容器大小时超出的流量会被丢弃。 下图比较形象的说明了漏桶算法的原理其中水龙头是入口流量漏桶是流量容器匀速流出的水是出口流量。 漏桶算法的特点 漏桶具有固定容量出口流量速率是固定常量流出请求入口流量可以以任意速率流入到漏桶中流入请求如果入口流量超出了桶的容量则流入流量会溢出新请求被拒绝 package com.msb.sentinel;import com.msb.sentinel.exception.BlockException; import lombok.extern.slf4j.Slf4j;import javax.swing.*; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Objects; import java.util.concurrent.*; import java.util.concurrent.locks.LockSupport;public class LeakyBucketRateLimit implements RateLimit, Runnable {/*** 出口限制qps*/private Integer limitSecond;/*** 漏桶队列*/private BlockingQueueThread leakyBucket;private ScheduledExecutorService scheduledExecutorService;public LeakyBucketRateLimit(Integer bucketSize, Integer limitSecond) {this.limitSecond limitSecond;this.leakyBucket new LinkedBlockingDeque(bucketSize);scheduledExecutorService Executors.newSingleThreadScheduledExecutor();//1 秒1000000000 纳秒// 计算出一个请求需要多少纳秒long interval (1000 * 1000 * 1000) / limitSecond;scheduledExecutorService.scheduleAtFixedRate(this, 0, interval, TimeUnit.NANOSECONDS);}Overridepublic boolean canPass() throws BlockException {if (leakyBucket.remainingCapacity() 0) {throw new BlockException();}//如果立即可行且不违反容量限制则将指定的元素插入此双端队列表示的队列中即此双端队列的尾部// 并在成功时返回 true如果当前没有空间可用则返回 false。leakyBucket.offer(Thread.currentThread());//所有请求进来只要有容量就会停止到这里// 如果没有容量就停止在上行代码LockSupport.park();return true;}Overridepublic void run() {// 以固定的速率去唤醒他这样执行的速度就固定值Thread thread leakyBucket.poll();if (Objects.nonNull(thread)) {LockSupport.unpark(thread);}} }不过因为漏桶算法限制了流出速率是一个固定常量值所以漏桶算法不支持出现突发流出流量。但是在实际情况下流量往往是突发的。 4、令牌桶算法 令牌桶算法是漏桶算法的改进版可以支持突发流量。不过与漏桶算法不同的是令牌桶算法的漏桶中存放的是令牌而不是流量。 那么令牌桶算法是怎么突发流量的呢 最开始令牌桶是空的我们以恒定速率往令牌桶里加入令牌令牌桶被装满时多余的令牌会被丢弃。当请求到来时会先尝试从令牌桶获取令牌相当于从令牌桶移除一个令牌获取成功则请求被放行获取失败则阻塞活拒绝请求。 令牌桶算法的特点 最多可以存发b个令牌。如果令牌到达时令牌桶已经满了那么这个令牌会被丢弃每当一个请求过来时就会尝试从桶里移除一个令牌如果没有令牌的话请求无法通过。 令牌桶算法限制的是平均流量因此其允许突发流量只要令牌桶中有令牌就不会被限流 package com.msb.sentinel;import com.msb.sentinel.exception.BlockException; import org.apache.commons.lang3.StringUtils;import java.util.concurrent.*;public class TokenBucketRateLimit implements RateLimit, Runnable {/*** token 生成 速率 每秒*/private Integer tokenLimitSecond;/*** 令牌桶队列*/private BlockingQueueString /* token */ tokenBucket;private static final String TOKEN __token__;private ScheduledExecutorService scheduledExecutorService;public TokenBucketRateLimit(Integer bucketSize, Integer tokenLimitSecond) {this.tokenLimitSecond tokenLimitSecond;this.tokenBucket new LinkedBlockingDeque(bucketSize);scheduledExecutorService Executors.newSingleThreadScheduledExecutor();long interval (1000 * 1000 * 1000) / tokenLimitSecond;scheduledExecutorService.scheduleAtFixedRate(this, 0, interval, TimeUnit.NANOSECONDS);}Overridepublic boolean canPass() throws BlockException {String token tokenBucket.poll();if (StringUtils.isEmpty(token)) {throw new BlockException();}return true;}Overridepublic void run() {if (tokenBucket.remainingCapacity() 0) {return;}tokenBucket.offer(TOKEN);} }
http://www.sadfv.cn/news/340946/

相关文章:

  • 襄阳市网站建设企业一站式网站建设
  • 谈谈网站建设会有哪些问题做小程序商城
  • 免费服装网站模板怎么做网站可手机看
  • 关键词挖掘工具爱站网网站服务类型
  • 网站受众群体阿里云部署一个自己做的网站吗
  • 网站开发分销系统网站运营成功案例
  • 建设一个旅游平台网站需要多少资金网站推广是怎么做的
  • 网站后台制作用的软件wordpress修改页面样式
  • 第一推是谁做的网站虚拟邮箱注册网站
  • 北京 网站设计郑州网站服务公司
  • 怎么建个人网站化妆品网站优势
  • 公司建立网站的作用有一个企业网站的建设流程
  • wordpress海外建站西安网站制作优化
  • 西安网站建设推荐苏州艺术家网站建设
  • 淘宝客论坛响应式php网站下载wordpress用户注册收不到邮件
  • 聊城建设网站西安公司网站开发
  • 桂林企业网站建设产品展示小程序
  • 安徽盛绿建设网站旅游网站营销
  • google play官网谷歌aso优化
  • 做催收的网站建立网站的目标
  • 甘肃省住房和城乡建设厅网站国外网站建设的发展
  • 网业qq福建seo快速排名优化
  • 服务好的网站建设平台云商城是什么
  • 2018年做网站赚钱深圳微信开发
  • 山西制作网站生活中优秀的产品设计
  • 广州网站设计开发公司软文代发代理
  • 企业如何做网站外包多少钱西安博威建设工程有限公司招聘网站
  • 响应式和非响应式网站站长工具在线
  • 网站建设不能在淘宝发布wordpress添加标签后哪里显示
  • 好的建设网站网站建设好评语