阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?

本文原文链接

尼恩说在前面

在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:

如何确定系统的最佳线程数?

5000qps,下游一个接口响应时间 500ms,接口超时时间 1S,一台机器4核8g,如何设计线程池的核心线程数、最大线程数、队列,需要多少台机器

5000qps访问 一个 500ms响应时间接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?

最近有小伙伴在面试阿里,又遇到了相关的面试题。小伙伴懵了,因为没有遇到过,所以支支吾吾的说了几句,面试官不满意,面试挂了。

所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书

本文目录

本文的配套视频, 尼恩的参考答应

详见链接:https://mp.weixin.qq.com/s/JFWjDSQ4HRGbZhj9ei3t6Q

线程使用的两个核心规范

首先看编程规范中, 有两个很重要的,与线程有关的需要强制执行的规范:

规范一:【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

说明:Java线程的创建非常昂贵,需要JVM和OS(操作系统)配合完成大量的工作:

1)消耗内存资源:必须为线程堆栈分配和初始化大量内存块,其中包含至少1MB的栈内存。

2)消耗CPU资源:需要进行系统调用,以便在OS(操作系统)中创建和注册内核线程,大量内核线程调度会导致CPU上下文过度切换。

所以,Java高并发应用频繁创建和销毁线程的操作将是非常低效的,而且是不被编程规范所允许的。

如何降低Java线程的创建成本?必须使用到线程池。使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

以上的内容,在尼恩的 《Java 高并发核心编程 卷2》 进行了详细介绍。

规范二:【强制】 线程池不允许使用Executors去创建快捷线程池 ,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors返回的线程池对象的弊端如下:

  • FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
  • CachedThreadPool和ScheduledThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

通过以上规范,说明我们应用中,需要用自定义线程池。然而,由于构造一个线程池竟然有7个参数

### 使用QPS和并发线程数配置流量控制策略 为了有效管理API或服务的请求数量,可以利用两种主要方法:基于每秒查询数量(Queries Per Second, QPS)以及基于并发线程的数量来进行流控。这两种机制各有特点,在实际应用中可以根据具体需求灵活选用。 #### 基于QPS的限流 设定固定的QPS阈值能够有效地防止短时间内大量请求涌入系统造成过载。例如,若将QPS设为20,则意味着在一秒钟内最多允许有20次成功的HTTP调用到达目标端点[^2]。对于超出此速率的新入站请求,默认行为通常是立即返回错误响应给客户端,告知其稍后再试;或者也可以选择将其放入队列等待执行机会到来再处理。 ```python from time import sleep import threading class RateLimiter: def __init__(self, qps_limit): self.qps_limit = qps_limit self.lock = threading.Lock() self.tokens = self.qps_limit self.last_refill_time = None def _refill_tokens(self): current_time = int(time.time()) if not self.last_refill_time or (current_time != self.last_refill_time): with self.lock: elapsed_seconds = max(current_time - self.last_refill_time, 0) new_tokens = min( self.qps_limit, self.tokens + elapsed_seconds * self.qps_limit ) self.tokens = new_tokens self.last_refill_time = current_time def allow_request(self): self._refill_tokens() if self.tokens >= 1: with self.lock: self.tokens -= 1 return True return False ``` 上述Python代码实现了一个简单的令牌桶算法来模拟按固定频率发放许可的过程,从而达到限制QPS的效果。 #### 并发线程数限制 另一种常见的做法是通过限定同时运行的任务实例总数——即所谓的“最大并发连接数”。这种方式特别适用于那些可能长时间占用资源的操作场景,如数据库查询或是涉及外部依赖的服务调用等。一旦当前活跃的工作单元达到了预定义的最大值,后续尝试启动新任务的动作就会被暂时搁置直至有足够的空间可用为止[^4]。 ```python semaphore = threading.Semaphore(value=5) def limited_concurrent_task(): try: semaphore.acquire() # 请求获取信号量 print(f"Task started at {time.strftime('%H:%M:%S')}") sleep(3) # 模拟工作负载持续时间 print(f"Task finished at {time.strftime('%H:%M:%S')}") finally: semaphore.release() # 完成后释放信号量 ``` 在这个例子中,`Semaphore`对象用来跟踪并控制系统内部可容纳的同时在线作业上限。每当发起一个新的异步操作之前都需要先申请获得相应的许可证;而在完成之后则要及时归还以便让其他待命中的进程得以继续前进。 综上所述,合理运用QPS与并发线程数作为参数调整工具可以帮助维护良好的性能表现和服务稳定性,确保即使面对突发性的高访问压力也依然能保持正常运作状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值