十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

advanced-java 高可用实战:基于 Hystrix timeout 机制为服务接口调用提供超时安全保护

advanced-java 高可用实战:基于 Hystrix timeout 机制为服务接口调用提供超时安全保护 advanced-java 高可用实战基于 Hystrix timeout 机制为服务接口调用提供超时安全保护【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/gh_mirrors/ad/advanced-java在复杂的分布式系统中服务间调用超时是引发系统抖动、线程资源耗尽乃至服务雪崩的高频诱因。本文以 advanced-java 仓库高可用模块中的 hystrix-timeout.md 为主体系统讲解 Hystrix 超时机制的核心配置TimeoutMilliseconds、TimeoutEnabled、与 fallback 降级、断路器、线程池隔离的联动关系并给出一个可运行的完整 Demo。读完本文你将掌握如何为依赖服务接口调用设置超时保护避免垃圾依赖把自身服务拖死。为什么要为接口调用做超时保护一般来说在调用依赖服务接口的时候最常遇到的问题就是超时。在一个复杂的分布式系统中超时是导致系统不稳定、系统抖动的重要原因之一一旦出现大量超时线程资源会被 hang 死系统吞吐量会大幅度下降甚至服务崩溃。特别是大公司里往往是多团队、大型协作的场景。你去调用各种各样的依赖服务甚至都不认识开发那个服务的人也不知道那个人的技术水平如何——引用 Peter Steiner 的那句名言On the Internet, nobody knows youre a dog也就是说在互联网的另一头你都不知道坐着的是不是一条狗。在这种背景下依赖服务接口的性能可能极不稳定有时候 2ms有时候 200ms甚至 2s都有可能。如果你不对各种依赖服务接口的调用做超时控制给服务提供安全保护那么很可能你的服务就被各种垃圾依赖服务的性能给拖死了——大量接口调用很慢大量线程被卡死。这里要特别说明的一点是超时保护与资源隔离是配套使用的。如果做了线程池资源隔离那么慢调用最多只是把线程池中的线程卡死而加上超时控制之后连卡死都不允许发生——超过设定时长就直接判为超时并走降级逻辑没必要让线程池里的线程全部 hang 死。关于资源隔离的更多细节可以参考 基于 Hystrix 线程池技术实现资源隔离 与 Hystrix 隔离策略细粒度控制。Hystrix 超时机制的两大核心配置在 Hystrix 中超时机制由两个核心参数控制超时时长与是否启用超时。两者都通过HystrixCommandProperties.Setter()的 Builder 模式进行配置。TimeoutMilliseconds超时时长如果一个 command 的运行时间超过了设定的时长那么 Hystrix 就认为该 command 超时并将其标识为 timeout同时执行 fallback 降级逻辑。TimeoutMilliseconds的默认值是1000也就是 1000ms。对应 Hystrix 配置项中的execution.isolation.thread.timeoutInMilliseconds。HystrixCommandProperties.Setter() .withExecutionTimeoutInMilliseconds(int)TimeoutEnabled是否启用超时机制这个参数用于控制是否要打开 timeout 机制默认值是true。对应 Hystrix 配置项中的execution.timeout.enabled。HystrixCommandProperties.Setter() .withExecutionTimeoutEnabled(boolean)两个参数的默认值与核心作用可以汇总如下参数Setter 方法默认值作用TimeoutMillisecondswithExecutionTimeoutInMilliseconds(int)1000ms超过该时长即判定 command 超时触发降级TimeoutEnabledwithExecutionTimeoutEnabled(boolean)true是否启用超时机制关闭后超时判定不再生效提示超时机制默认是开启的。只有在你明确知道某个依赖服务接口可能长时间运行、且不希望被强制判定超时时才考虑关闭它绝大多数场景下都应当保持开启。超时之后会发生什么与 fallback / 断路器 / 线程池的联动在 Hystrix 的 8 大执行步骤详见 深入 Hystrix 执行时内部原理中超时判定发生在步骤六执行 command。具体机制如下如果采用线程池隔离方式且HystrixCommand.run()或HystrixObservableCommand.construct()的执行时间超过了 timeout 时长command 所在的线程会抛出一个TimeoutException此时立即执行 fallback 降级机制不再等待run()/construct()返回的值超时事件还会被发送给断路器进行统计与调用成功、失败、Reject 等事件一起作为断路器状态机Closed / Open / Half-Open判定的输入详见 深入 Hystrix 断路器执行原理。同时超时是触发 fallback 降级的四种典型情况之一另三种是断路器打开、资源池已满、调用抛出异常。在 基于本地缓存的 fallback 降级机制 中明确提到访问外部依赖时间过长、报了TimeoutException异常时Hystrix 会调用 fallback 降级逻辑。这里有一个重要的边界需要说清楚我们是不可能真正终止掉一个调用严重延迟的依赖服务的线程的只能说给你抛出一个TimeoutException。也就是说超时降级是主调用线程不再等待结果、立刻走降级返回但那个被 hang 住的慢线程本身依然会在后台继续运行直到它自然结束。另外需要注意的是超时中断语义抛TimeoutException只对线程池隔离有效如果你选择信号量隔离command 运行在调用线程如 Tomcat 线程中无法被中断。这也是 Hystrix 隔离策略细粒度控制 中提到线程池对于网络访问请求、有超时的情况下可以避免调用线程阻塞的原因。实例 Demo500ms 超时 1s 休眠触发降级下面给出一个完整的可运行示例。我们在 command 中将超时时间设置为500ms然后在run()方法中设置休眠1s。这样请求过来后run()会直接休眠 1s执行时间必然超过 500ms 的超时时长结果就会因为超时而执行降级逻辑。public class GetProductInfoCommand extends HystrixCommandProductInfo { private Long productId; private static final HystrixCommandKey KEY HystrixCommandKey.Factory.asKey(GetProductInfoCommand); public GetProductInfoCommand(Long productId) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(ProductInfoService)) .andCommandKey(KEY) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() .withCoreSize(8) .withMaxQueueSize(10) .withQueueSizeRejectionThreshold(8)) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withCircuitBreakerEnabled(true) .withCircuitBreakerRequestVolumeThreshold(20) .withCircuitBreakerErrorThresholdPercentage(40) .withCircuitBreakerSleepWindowInMilliseconds(3000) // 设置是否打开超时默认是true .withExecutionTimeoutEnabled(true) // 设置超时时间默认1000(ms) .withExecutionTimeoutInMilliseconds(500) .withFallbackIsolationSemaphoreMaxConcurrentRequests(30))); this.productId productId; } Override protected ProductInfo run() throws Exception { System.out.println(调用接口查询商品数据productId productId); // 休眠1s TimeUtils.sleep(1); String url http://localhost:8081/getProductInfo?productId productId; String response HttpClientUtils.sendGetRequest(url); System.out.println(response); return JSONObject.parseObject(response, ProductInfo.class); } Override protected ProductInfo getFallback() { ProductInfo productInfo new ProductInfo(); productInfo.setName(降级商品); return productInfo; } }对上述代码中的几个关键配置做一下说明.withExecutionTimeoutEnabled(true)显式打开超时机制默认即为 true.withExecutionTimeoutInMilliseconds(500)把超时时间从默认的 1000ms 缩短为 500ms用于演示.withCoreSize(8)/.withMaxQueueSize(10)/.withQueueSizeRejectionThreshold(8)线程池相关参数coreSize 默认值为 10这里设置为 8队列相关参数用于控制请求积压与拒绝阈值详见 Hystrix 隔离策略细粒度控制.withFallbackIsolationSemaphoreMaxConcurrentRequests(30)设置降级逻辑允许的最大并发请求数为 30Hystrix 默认值是 10通过信号量机制限流超出直接 reject详见 基于本地缓存的 fallback 降级机制。在测试类中我们直接发起请求SpringBootTest RunWith(SpringRunner.class) public class TimeoutTest { Test public void testTimeout() { HttpClientUtils.sendGetRequest(http://localhost:8080/getProductInfo?productId1); } }运行测试后结果中可以看到打印出了降级商品相关信息ProductInfo(idnull, name降级商品, pricenull, pictureListnull, specificationnull, servicenull, colornull, sizenull, shopIdnull, modifiedTimenull, cityIdnull, cityNamenull, brandIdnull, brandNamenull) {id: 1, name: iphone7手机, price: 5599, pictureList:a.jpg,b.jpg, specification: iphone7的规格, service: iphone7的售后服务, color: 红色,白色,黑色, size: 5.5, shopId: 1, modifiedTime: 2017-01-01 12:00:00, cityId: 1, brandId: 1}对这两行输出可以做如下解读第一行是getFallback()返回的降级商品对象。由于run()休眠 1s 超过了 500ms 的超时阈值调用方通过execute()拿到的是降级结果——这正是超时保护起作用的直接证据第二行 JSON 是run()方法内部HttpClientUtils.sendGetRequest(...)之后System.out.println(response)打印的原始接口响应。它之所以仍会输出正是因为超时后那个慢线程并不会被真正终止休眠 1s 结束、真实接口返回后run()中后续的代码依然会在后台继续执行完只是它的返回值已经不被主调用链路采纳了。小结与实战建议综合 hystrix-timeout.md 及仓库内其他 Hystrix 相关文档可以把超时保护的落地要点总结如下开启超时机制withExecutionTimeoutEnabled(true)默认开启并为每个 command 设置合理的超时时长withExecutionTimeoutInMilliseconds默认 1000ms。时长的设定需要结合依赖接口的正常 P99 耗时来评估避免过短导致误判、过长失去保护意义。超时必须与 fallback 降级配套超时发生后立即执行getFallback()返回默认值或本地缓存数据实现快速失败fail-fast避免调用线程长时间阻塞。降级逻辑中建议不要再次发起网络请求如确有必要应将该调用放入另一个 HystrixCommand 中进行隔离见 基于本地缓存的 fallback 降级机制。理解超时的边界线程池隔离下超时会抛TimeoutException并触发降级但无法真正杀死被 hang 住的慢线程信号量隔离下 command 运行在调用线程中不具备同样的超时中断语义。超时事件会联动断路器超时、失败、Reject 等事件都会被送往断路器统计作为熔断决策的依据从而让超时保护与熔断、降级共同构成完整的高可用防线参见 深入 Hystrix 断路器执行原理。本文属于 advanced-java 仓库 高可用架构 系列的组成部分与 基于 Hystrix 线程池技术实现资源隔离、基于本地缓存的 fallback 降级机制、深入 Hystrix 执行时内部原理 等文档互相衔接组合阅读可以完整掌握 Hystrix 构建高可用服务的核心技术栈。【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/gh_mirrors/ad/advanced-java创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表