Skip to content

Commit 1afc40b

Browse files
committed
调整目录结构
1 parent 156db24 commit 1afc40b

30 files changed

+4624
-29
lines changed

中间件/Elasticsearch入门.md

Lines changed: 2438 additions & 0 deletions
Large diffs are not rendered by default.

中间件/Redis面试题.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ Redis cluster采用**虚拟槽分区**,所有的键根据哈希函数映射到
435435

436436
缓存穿透是指查询一个**不存在的数据**,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
437437

438+
怎么解决?
439+
438440
1. **缓存空值**,不会查数据库。
439441
2. 采用**布隆过滤器**,将所有可能存在的数据哈希到一个足够大的`bitmap`中,查询不存在的数据会被这个`bitmap`拦截掉,从而避免了对`DB`的查询压力。
440442

其他/实战篇.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
## OOM问题排查
2+
3+
[记一次OOM问题排查!](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247486106&idx=1&sn=0d528efad06f0a1440e02983bf336bca&chksm=ce98f7dcf9ef7ecafee10115f1d29bfba56dd8bf694122dd2cb69a53f333b2242522f88aa92e&token=1360123733&lang=zh_CN#rd)
4+
5+
6+
7+
## RT过长,排查思路
8+
9+
[Java诊断工具Arthas](https://mp.weixin.qq.com/s/TnLl2OW9XJLSZihcpgP7VQ)
10+
11+
12+
13+
## 限流算法
14+
15+
限流是指在系统面临高并发、大流量请求的情况下,限制新的流量对系统的访问,从而保证系统服务的安全性。常用的限流算法有计数器固定窗口算法、滑动窗口算法、漏斗算法和令牌桶算法。
16+
17+
### 计数器固定窗口算法
18+
19+
计数器固定窗口算法是最基础也是最简单的一种限流算法。原理就是对一段固定时间窗口内的请求进行计数,如果请求数超过了阈值,则舍弃该请求;如果没有达到设定的阈值,则接受该请求,且计数加1。当时间窗口结束时,重置计数器为0。
20+
21+
**优点**:实现简单,容易理解。
22+
23+
**缺点**:流量曲线可能不够平滑,有“突刺现象”,这样会有两个问题:
24+
25+
1. **一段时间内(不超过时间窗口)系统服务不可用**。比如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第1ms来了100个请求,然后第2ms-999ms的请求就都会被拒绝,这段时间用户会感觉系统服务不可用。
26+
2. **窗口切换时可能会产生两倍于阈值流量的请求**。比如窗口大小为1s,限流大小为100,然后恰好在某个窗口的第999ms来了100个请求,窗口前期没有请求,所以这100个请求都会通过。再恰好,下一个窗口的第1ms有来了100个请求,也全部通过了,那也就是在2ms之内通过了200个请求,而我们设定的阈值是100,通过的请求达到了阈值的两倍。
27+
28+
### 计数器滑动窗口算法
29+
30+
计数器滑动窗口算法是计数器固定窗口算法的改进,解决了固定窗口切换时可能会产生两倍于阈值流量请求的缺点。
31+
32+
滑动窗口算法在固定窗口的基础上,将一个计时窗口分成了若干个小窗口,然后每个小窗口维护一个独立的计数器。当请求的时间大于当前窗口的最大时间时,则将计时窗口向前平移一个小窗口。平移时,将第一个小窗口的数据丢弃,然后将第二个小窗口设置为第一个小窗口,同时在最后面新增一个小窗口,将新的请求放在新增的小窗口中。同时要保证整个窗口中所有小窗口的请求数目之后不能超过设定的阈值。
33+
34+
将计时窗口划分成一个小窗口,滑动窗口算法就退化成了固定窗口算法。而滑动窗口算法其实就是对请求数进行了更细粒度的限流,窗口划分的越多,则限流越精准。
35+
36+
**特点分析**
37+
38+
1. 避免了计数器固定窗口算法固定窗口切换时可能会产生两倍于阈值流量请求的问题;
39+
2. 和漏斗算法相比,新来的请求也能够被处理到,避免了漏斗算法的饥饿问题。
40+
41+
### 漏斗算法
42+
43+
漏斗算法的原理也很容易理解。请求来了之后会首先进到漏斗里,然后漏斗以恒定的速率将请求流出进行处理,从而起到平滑流量的作用。当请求的流量过大时,漏斗达到最大容量时会溢出,此时请求被丢弃。从系统的角度来看,我们不知道什么时候会有请求来,也不知道请求会以多大的速率来,这就给系统的安全性埋下了隐患。但是如果加了一层漏斗算法限流之后,就能够保证请求以恒定的速率流出。在系统看来,请求永远是以平滑的传输速率过来,从而起到了保护系统的作用。
44+
45+
**特点分析**
46+
47+
1. **漏桶的漏出速率是固定的,可以起到整流的作用**。即虽然请求的流量可能具有随机性,忽大忽小,但是经过漏斗算法之后,变成了有固定速率的稳定流量,从而对下游的系统起到保护作用。
48+
2. **不能解决流量突发的问题**。假如设定的漏斗速率是2个/秒,漏斗限流算法的容量是5。然后突然来了10个请求,受限于漏斗的容量,只有5个请求被接受,另外5个被拒绝。你可能会说,漏斗速率是2个/秒,然后瞬间接受了5个请求,这不就解决了流量突发的问题吗?不,这5个请求只是被接受了,但是没有马上被处理,处理的速度仍然是我们设定的2个/秒,所以没有解决流量突发的问题。而接下来我们要谈的令牌桶算法能够在一定程度上解决流量突发的问题。
49+
50+
### 令牌桶算法
51+
52+
令牌桶算法是对漏斗算法的一种改进,除了能够起到限流的作用外,还允许一定程度的流量突发。在令牌桶算法中,存在一个令牌桶,算法中存在一种机制以恒定的速率向令牌桶中放入令牌。令牌桶也有一定的容量,如果满了令牌就无法放进去了。当请求来时,会首先到令牌桶中去拿令牌,如果拿到了令牌,则该请求会被处理,并消耗掉拿到的令牌;如果令牌桶为空,则该请求会被丢弃。
53+
54+
比如过来10个请求,令牌桶算法和漏斗算法一样,都是接受了5个请求,拒绝了5个请求。与漏斗算法不同的是,令牌桶算法马上处理了这5个请求,处理速度可以认为是5个/秒,超过了我们设定的2个/秒的速率,即**允许一定程度的流量突发**。这一点也是和漏斗算法的主要区别。
55+
56+
**特点分析**
57+
58+
令牌桶算法是对漏桶算法的一种改进,除了能够在限制调用的平均速率的同时还允许一定程度的流量突发。
59+
60+
### 小结
61+
62+
**计数器固定窗口算法**实现简单,容易理解。和漏斗算法相比,新来的请求也能够被马上处理到。但是流量曲线可能不够平滑,有“突刺现象”,在窗口切换时可能会产生两倍于阈值流量的请求。而**计数器滑动窗口算法**作为计数器固定窗口算法的一种改进,有效解决了窗口切换时可能会产生两倍于阈值流量请求的问题。
63+
64+
**漏斗算法**能够对流量起到整流的作用,让随机不稳定的流量以固定的速率流出,但是不能解决**流量突发**的问题。**令牌桶算法**作为漏斗算法的一种改进,除了能够起到平滑流量的作用,还允许一定程度的流量突发。
65+
66+
令牌桶算法一般用于保护自身的系统,对调用者进行限流,保护自身的系统不被突发的流量打垮。如果自身的系统实际的处理能力强于配置的流量限制时,可以允许一定程度的流量突发,使得实际的处理速率高于配置的速率,充分利用系统资源。而漏斗算法一般用于保护第三方的系统,比如自身的系统需要调用第三方的接口,为了保护第三方的系统不被自身的调用打垮,便可以通过漏斗算法进行限流,保证自身的流量平稳的打到第三方的接口上。
67+
68+
参考:https://zhuanlan.zhihu.com/p/228412634
69+
70+
71+
72+
## 亿级数据分页查询难怎么解决?
73+
74+
无深翻页需求的实时在线应用:禁止深翻页,数据库 offset < N。
75+
76+
确有深翻页需求的离线分析应用:es搜scroll,jdbc自己维护这个读取游标的长链接,持续流式读取。
77+
78+
79+
80+
## 秒杀场景
81+
82+
并发修改库存,会加行锁。rt较高,tps上不去。两种方案:
83+
84+
1. 分库分表。减小锁粒度
85+
2. 降低锁的持有时间
86+
87+
可以在内存起一个队列,将用户请求放进队列。起一个异步线程,每200ms处理队列数据,批量扣库存。这样就不用每个请求都去扣减库存,减少行锁持有时间。
88+
89+
每个请求封装一个锁对象(锁里面包含购买数量),丢进队列,调用wait(200)。当扣库存完成后拿到锁对象执行notify,通知用户请求线程继续往下执行。即每个用户线程都会有一个锁对象,用来阻塞和通知,阻塞是最多200ms
90+
91+
92+
93+
## MySQL表数据量大
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
一、背景
2+
基于RokcetMQ可以实现异步处理、流量削锋、业务解耦,通常是依赖RocketMQ的发布订阅模式。今天分享RocketMQ的新特性,基于Request/Reply模式来实现RPC的功能。该模式是在v4.6作为RocketMQ新特性引入,但在在v4.7.1上才比较完善。
3+
4+
二、设计思路
5+
从整个数据流的角度上来说,发布/订阅模式中生产者和消费者之间是异步处理的,生产者负责把消息投递到RocketMQ上,而消费者负责处理数据,如果把生产者当做上游系统,消费者是下游系统,那么上下游系统之间是没有任何的状态交流的。而我们知道,RPC上下游系统之间是需要状态交互的,简单来说,要想实现RPC功能,在整个数据链路上,原先上下游系统之间是异步交互的模式,首先需要把异步模式换成同步模式。
6+
7+
异步模式:
8+
9+
把异步模式换成同步模式,需要在生产者发送消息到MQ之后,保持原有的状态,比如可以用一个Map集合去统一维护,等到消费者处理完数据返回响应后,再从Map集合中拿到对应的请求进行处理。其中涉及到怎么去标识一个请求,这里可以用UUID或者雪花id去标记。
10+
11+
同步模式:
12+
13+
RocketMQ整体的处理思路跟上面是类似的,DefaultMQProducerImpl#request负责RPC消息的下发,而DefaultMQPushConsumer中负责消息的消费。具体用法可以看RocketMQ源码example中的RPC部分。
14+
15+
16+
三、结构定义
17+
RocketMQ中是依赖于Message的Properties来区分不同的请求,在调用DefaultMQProducerImpl#request进行消息下发之间会先给消息设置不同的属性,通过属性来保证上下游之间的处理是同一个请求。
18+
19+
设置的属性有:
20+
21+
CORRELATION_ID:消息的标识Id,这里对应是一个UUID
22+
REPLY_TO_CLIENT:消息下发的客户端Id
23+
TTL:消息下发的超时时间,单位ms
24+
25+
其实就类似于HTTP请求中的头部内容一样。
26+
27+
之后还会校验一下消息中对应Topic的一个合法性。
28+
29+
四、消息下发
30+
RocketMQ将下发的客户端封装成RequestResponseFuture,包含客户端Id,请求超时时间,同时根据客户端Id维护在ConcurrentHashMap,调用DefaultMQProducerImpl#sendDefaultImpl下发消息,根据下发消息的回调函数确认消息下发的状态。
31+
32+
消息下发后会调用waitResponse,waitResponse调用CountDownLatch进入阻塞状态,等待消息消费之后的响应。
33+
34+
CountDownLatch中的计数器是1,说明每个请求都会独立隔离阻塞。
35+
36+
37+
五、消息响应
38+
当服务端(消费者)收到消息处理完返回响应时,会调用ReplyMessageProcessor#pushReplyMessage封装响应的内容,处理响应的头部信息和返回body的参数,最终封装成一个PUSH_REPLY_MESSAGE_TO_CLIENT的请求命令发给客户端。
39+
40+
客户端(生产者)收到请求后,会调用ClientRemotingProcessor#processRequest,判断是PUSH_REPLY_MESSAGE_TO_CLIENT命令会调用receiveReplyMessage,将接收到的数据封装成新的消息,接着调用响应处理的处理器。
41+
42+
ClientRemotingProcessor#processReplyMessage中主要做的从消息中获取消息的Id,从ConcurrentHashMap中定位到具体的请求,将返回消息封装到RequestResponseFuture中,同时CountDownLatch的计数值减1,此时线程阻塞状态被释放,之后便将消息响应给到客户端。
43+
44+
六、总结
45+
所以整体上看来,RocketMQ的Request/Reply模式,其实是利用客户端线程阻塞来换取请求异步变同步以及RocketMQ的回调机制从而间接的实现了RPC效果,但是相比直接RPC调用,数据的链路更长,性能肯定是会有损耗,但是请求会持久化,所以给了重复下发提供了可能。
46+
47+
————————————————
48+
版权声明:本文为CSDN博主「林风自在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
49+
原文链接:https://blog.csdn.net/lveex/article/details/122514893

0 commit comments

Comments
 (0)