基于timeout机制为服务接口调用超时提供安全保护


基于 timeout 机制为服务接口调用超时提供安全保护

一般来说,在调用依赖服务的接口的时候,比较常见的一个问题就是超时。超时是在一个复杂的分布式系统中,导致系统不稳定,或者系统抖动。出现大量超时,线程资源会被 hang 死,从而导致吞吐量大幅度下降,甚至服务崩溃。

你去调用各种各样的依赖服务,特别是在大公司,你甚至都不认识开发一个服务的人,你都不知道那个人的技术水平怎么样,对那个人根本不了解。

Peter Steiner 说过,"On the Internet, nobody knows you're a dog",也就是说在互联网的另外一头,你都不知道甚至坐着一条狗。

https://www.aiknowledge.cn/images/互联网 Java 工程师进阶知识完全扫盲/220px-Internet_dog..webp

像特别复杂的分布式系统,特别是在大公司里,多个团队、大型协作,你可能都不知道服务是谁的,很可能说开发服务的那个哥儿们甚至是一个实习生。依赖服务的接口性能可能很不稳定,有时候 2ms,有时候 200ms,甚至 2s,都有可能。

如果你不对各种依赖服务接口的调用做超时控制,来给你的服务提供安全保护措施,那么很可能你的服务就被各种垃圾的依赖服务的性能给拖死了。大量的接口调用很慢,大量的线程被卡死。如果你做了资源的隔离,那么也就是线程池的线程被卡死,但其实我们可以做超时控制,没必要让它们全卡死。

TimeoutMilliseconds

在 Hystrix 中,我们可以手动设置 timeout 时长,如果一个 command 运行时间超过了设定的时长,那么就被认为是 timeout,然后 Hystrix command 标识为 timeout,同时执行 fallback 降级逻辑。

TimeoutMilliseconds 默认值是 1000,也就是 1000ms。

HystrixCommandProperties.Setter() ..withExecutionTimeoutInMilliseconds(int) ​

TimeoutEnabled

这个参数用于控制是否要打开 timeout 机制,默认值是 true。

HystrixCommandProperties.Setter() .withExecutionTimeoutEnabled(boolean) ​

实例 Demo

我们在 command 中,将超时时间设置为 500ms,然后在 run() 方法中,设置休眠时间 1s,这样一个请求过来,直接休眠 1s,结果就会因为超时而执行降级逻辑。

public class GetProductInfoCommand extends HystrixCommand<ProductInfo> { 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; } } ​

在测试类中,我们直接发起请求。

@SpringBootTest @RunWith(SpringRunner.class) public class TimeoutTest { @Test public void testTimeout() { HttpClientUtils.sendGetRequest("http://localhost:8080/getProductInfo?productId=1"); } } ​

结果中可以看到,打印出了降级商品相关信息。

ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null) {"id": 1, "name": "iphone7手机", "price": 5599, "pictureList":"https://www.aiknowledge.cn/images/互联网 Java 工程师进阶知识完全扫盲/a.jpg,b.webp", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1} ​
作者与出处
原作者:doocs
来源:doocs
许可证:CC BY-SA 4.0
整理人: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: doocs 转发
评论区 (0)
U