经典面试题——让你设计一个限流的系统怎么做?
Posted Javaesandyou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了经典面试题——让你设计一个限流的系统怎么做?相关的知识,希望对你有一定的参考价值。
保障服务稳定的三大利器:熔断降级、服务限流和故障模拟。限流系统是当前很多系统都需要考虑的场景。首先在nginx层面是可以做限流的,除此之外,在微服务层面还是有很多空间可以施展的。
限流的话,主要思路分为请求数量限制和消费能力限制两类。前者主要是限制一段时间内的总并发数、后者主要是限制消费者的消费能力。
限流算法
限流算法来说,主要包含令牌桶算法、漏桶算法和计数器等。对于简单的计数器算法,通过AtomicLong#incrementAndGet()来进行粗暴的控制,因为容易导致“突刺现象”(比如单位时间1s内的前10ms,已经通过了100个请求,那后面的990ms,只能拒绝),所以这里不做推荐。
令牌桶算法
令牌工厂:匀速生成令牌
令牌桶:拥有固定的令牌数
应用者:一次可以申请N个令牌,没有令牌不能进行后续处理。
如果使用Redis来实现的话会比较简单,大概思路如下:
1/ 获取令牌:依靠List的leftPop来获取令牌
Object result = redisTemplate.opsForList().leftPop("limit_list");
2/ 向令牌桶添加令牌
redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString());
漏桶算法
漏桶:容量固定
流速:任意
流出水滴:固定速率
滑动窗口
用Redis的list数据结构可以轻而易举的实现该功能:保证每N秒内至多M个请求,缺点就是zset的数据结构会越来越大。实现方式相对也是比较简单的。
1/ 请求进来:UUID生成唯一的value;score用当前的时间戳
redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime);
2/ 限流:zset的range方法可以统计两个时间戳内的请求,达到限流效果
Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime -
面试题:如何设计一个高并发的系统?
这道面试题涉及的知识点比较多,主要考察的是面试者的综合技术能力。高并发系统的设计手段有很多,主要体现在以下五个方面。
1、前端层优化
① 静态资源缓存:将活动页面上的所有可以静态的元素全部静态化,尽量减少动态元素;通过 CDN、浏览器缓存,来减少客户端向服务器端的数据请求。
② 禁止重复提交:用户提交之后按钮置灰,禁止重复提交。
③ 用户限流:在某一时间段内只允许用户提交一次请求,比如,采取 IP 限流。
2、中间层负载分发
可利用负载均衡,比如 nginx 等工具,可以将并发请求分配到不同的服务器,从而提高了系统处理并发的能力。
nginx 负载分发的五种方式:
① 轮询(默认)每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器不能正常响应,nginx 能自动剔除故障服务器。
② 按权重(weight)使用 weight 参数,指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况,配置如下:
upstream backend {
server 192.168.0.14 weight=10;
server 192.168.0.15 weight=10;
}
③ IP 哈希值(ip_hash)每个请求按访问 IP 的哈希值分配,这样每个访客固定访问一个后端服务器,可以解决 session 共享的问题,配置如下:
upstream backend {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
④ 响应时间(fair)按后端服务器的响应时间来分配请求,响应时间短的优先分配,配置如下:
upstream backend {
fair;
server server1.com;
server server2.com;
}
⑤ URL 哈希值(url_hash)按访问 url 的 hash 结果来分配请求,和 IP 哈希值类似。
upstream backend {
hash $request_uri;
server server1.com;
server server2.com;
}
3、控制层(网关层)优化
限制同一个用户的访问频率,限制访问次数,防止多次恶意请求。
4、服务层优化
① 业务服务器分离:比如,将秒杀业务系统和其他业务分离,单独放在高配服务器上,可以集中资源对访问请求抗压。
② 采用 MQ(消息队列)缓存请求:MQ 具有削峰填谷的作用,可以把客户端的请求先导流到 MQ,程序在从 MQ 中进行消费(执行请求),这样可以避免短时间内大量请求,导致服务器程序无法响应的问题。
③ 利用缓存应对读请求,比如,使用 Redis 等缓存,利用 Redis 可以分担数据库很大一部分压力。
5、数据库层优化
① 合理使用数据库引擎 ② 合理设置事务隔离级别,合理使用事务 ③ 正确使用数据库索引
- 尽量使用主键查询,而非其他索引,因为主键查询不会触发回表查询。
- 不做列运算,把计算都放入各个业务系统实现
- 查询语句尽可能简单,大语句拆小语句,减少锁时间
- 不使用 select * 查询
- or 查询改写成 in 查询
- 不用函数和触发器
- 避免 %xx 查询
- 少用 join 查询
- 使用同类型比较,比如 ‘123‘ 和 ‘123‘、123 和 123
- 尽量避免在 where 子句中使用 != 或者 <> 操作符,查询引用会放弃索引而进行全表扫描
- 列表数据使用分页查询,每页数据量不要太大
- 用 exists 替代 in 查询
- 避免在索引列上使用 is null 和 is not null
- 尽量使用主键查询
- 避免在 where 子句中对字段进行表达式操作
- 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型
④ 合理分库分表 ⑤ 使用数据库中间件实现数据库读写分离 ⑥ 设置数据库主从读写分离
【END】
热文推荐:史上最全面试题详解
关注下方二维码,订阅更多精彩内容
老王著
转发更佳
喜欢作者
以上是关于经典面试题——让你设计一个限流的系统怎么做?的主要内容,如果未能解决你的问题,请参考以下文章