面试题:高并发场景/接口被刷怎么办?
【1】架构和细节
首先参考上篇博文:聊聊高并发应用中秒杀场景的方案实现,其中限流,缓存等思想是一致的。
这里需要注意的是,如果在现有集群环境下,负载压力仍然很大怎么办?观察上图最后一步—容器化技术。使用Docker+K8s(Kubernetes)构建Docker集群环境。当QPS超过阈值后,自动扩容。比如扩容两个虚拟机,将登录子系统从四节点扩容到6节点,然后修改nginx配置文件。在流量高峰期过后,再触发自动收缩技术,减少虚拟机,减少资源的消耗。
上面从架构层面简要描述了一下流程,下面再来看看具体细节。
【2】常见的限流手段
① 手动实现负载均衡
如下图红色框选中的地方,让你选择一下,再下载。
② 图形验证码
12306故意把验证码弄得模糊不清,影响消费者下单,从而限流。但是BATJ在主页均未使用。
③ 容器限流
常见的web容器其实也具备限流的功能,以Tomcat容器为例,其Connector其中一种配置有如下几个参数:
acceptCount:如果Tomcat的线程都忙于响应,新来的连接会进入队列排队。如果超出排队大小,则拒绝连接。
maxConnection:瞬时最大连接数,超出的会排队等待。
maxThreads:Tomcat能用来处理请求的最大线程数,如果请求处理量一直远远大于最大线程数则可能会僵死。
④ 限流总资源数
如果有的资源是稀缺资源(如数据库连接、线程),而且可能多个系统都在使用它,那么需要限制使用。可以使用池化技术来限制总资源数:连接池、线程池。比如分配给每个应用的数据库连接是100,那么本应用最多可以使用100个资源,超出了可以等待或者抛异常。
⑤ 从代码级限流某个接口的总并发/请求数
如果接口可能会有突发访问情况,但又担心访问量太大造成崩溃,如抢购业务。这个时候就需要限制这个接口的总并发请求数了。因为粒度比较细,可以为每个接口都设置相应的阈值。
可以使用Java中的AtomicLong进行限流:
try{ if(atomic.incrementAndGet()>限流数){ //拒绝请求 } //处理请求 }finally{ atomic.decrementAndGet(); }
⑥ Nginx限流
Nginx提供了一个叫ngx_http_limit_req_module的模块进行流量控制,Nginx是基于漏桶算法实现。
⑦ 消息队列
通过RabbitMQ、RocketMQ、ActiveMQ、ZeroMQ、Kafka把流量做匀,限制高流量涌入。
【3】Hystrix服务熔断与降级
其相关术语解释如下:
线程隔离
信号量隔离
线程隔离和信号量隔离对比
- | 线程隔离 | 信号量隔离 |
线程 | 与调用线程非相同线程 | 与调用线程相同(容器线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量大小) |
Hystrix执行流程图
熔断
一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采取的一种保护机制,所以很多地方把熔断亦称为过载保护。很多时候刚开始可能只是系统出现了局部的、小规模的故障。然而由于种种原因,故障影响的范围越来越大,最终导致了全局性的后果。
如果某个目标服务调用慢或者有大量超时,此时熔断该服务的调用,对于后续调用请求,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好则恢复调用。
请求缓存