docs(img): change img path
@ -22,7 +22,7 @@ RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模
|
||||
#### 普通集群模式(无高可用性)
|
||||
普通集群模式,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。但是你**创建的 queue,只会放在一个 RabbitMQ 实例上**,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。
|
||||
|
||||

|
||||

|
||||
|
||||
这种方式确实很麻烦,也不怎么好,**没做到所谓的分布式**,就是个普通集群。因为这导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个 queue 所在实例消费数据,前者有**数据拉取的开销**,后者导致**单实例性能瓶颈**。
|
||||
|
||||
@ -33,7 +33,7 @@ RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模
|
||||
#### 镜像集群模式(高可用性)
|
||||
这种模式,才是所谓的 RabbitMQ 的高可用模式,跟普通集群模式不一样的是,你创建的 queue,无论元数据还是 queue 里的消息都会**存在于多个实例上**,然后每次你写消息到 queue 的时候,都会自动把**消息同步**到多个实例的 queue 上。
|
||||
|
||||

|
||||

|
||||
|
||||
这样的话,好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很重!第二,这么玩儿,就**没有扩展性可言**了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并没有办法线性扩展你的 queue。
|
||||
|
||||
@ -50,7 +50,7 @@ Kafka 0.8 以前,是没有 HA 机制的,就是任何一个 broker 宕机了
|
||||
|
||||
Kafka 0.8 以后,提供了 HA 机制,就是 replica(复制品) 副本机制。每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本。然后所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。只能读写 leader?很简单,**要是你可以随意读写每个 follower,那么就要 care 数据一致性的问题**,系统复杂度太高,很容易出问题。Kafka 会均匀的将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。
|
||||
|
||||

|
||||

|
||||
|
||||
这么搞,就有所谓的**高可用性**了,因为如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的,如果这上面有某个 partition 的 leader,那么此时会**重新选举**一个新的 leader 出来,大家继续读写那个新的 leader 即可。这就有所谓的高可用性了。
|
||||
|
||||
|
@ -13,7 +13,7 @@ Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一
|
||||
|
||||
但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。
|
||||
|
||||

|
||||

|
||||
|
||||
其实重复消费不可怕,可怕的是你没考虑到重复消费之后,**怎么保证幂等性**。
|
||||
|
||||
@ -32,6 +32,6 @@ Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一
|
||||
- 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
|
||||
- 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
|
||||
|
||||

|
||||

|
||||
|
||||
当然,如何保证 MQ 的消费是幂等性的,需要结合具体的业务来看。
|
||||
|
@ -14,17 +14,17 @@
|
||||
先看看顺序会错乱的俩场景:
|
||||
- RabbitMQ:一个 queue,多个 consumer,这不明显乱了;
|
||||
|
||||

|
||||

|
||||
|
||||
- kafka:一个 topic,一个 partition,一个 consumer,内部多线程,这不也明显乱了。
|
||||
|
||||

|
||||

|
||||
|
||||
### 解决方案
|
||||
#### RabbitMQ
|
||||
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
|
||||

|
||||

|
||||
|
||||
#### kafka
|
||||
一个 topic,一个 partition,一个 consumer,内部单线程消费;写 N 个内存 queue,然后 N 个线程分别消费一个内存 queue 即可。
|
||||

|
||||

|
||||
|
@ -10,7 +10,7 @@
|
||||
数据的丢失问题,可能出现在生产者、MQ、消费者中,咱们从 RabbitMQ 和 Kafka 分别来分析一下吧。
|
||||
|
||||
### RabbitMQ
|
||||

|
||||

|
||||
|
||||
#### 生产者弄丢了数据
|
||||
|
||||
@ -61,7 +61,7 @@ RabbitMQ 如果丢失了数据,主要是因为你消费的时候,**刚消费
|
||||
|
||||
这个时候得用 RabbitMQ 提供的`ack`机制,简单来说,就是你关闭 RabbitMQ 的自动`ack`,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里`ack`一把。这样的话,如果你还没处理完,不就没有`ack`?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。
|
||||
|
||||

|
||||

|
||||
|
||||
### Kafka
|
||||
|
||||
|
@ -28,13 +28,13 @@
|
||||
#### 解耦
|
||||
看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃......
|
||||
|
||||

|
||||

|
||||
|
||||
在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!
|
||||
|
||||
如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。
|
||||
|
||||

|
||||

|
||||
|
||||
**总结**:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。
|
||||
|
||||
@ -43,13 +43,13 @@
|
||||
#### 异步
|
||||
再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。
|
||||
|
||||

|
||||

|
||||
|
||||
一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。
|
||||
|
||||
如果**使用 MQ**,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快!
|
||||
|
||||

|
||||

|
||||
|
||||
#### 削峰
|
||||
每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。
|
||||
@ -58,11 +58,11 @@
|
||||
|
||||
但是高峰期一过,到了下午的时候,就成了低峰期,可能也就 1w 的用户同时在网站上操作,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力。
|
||||
|
||||

|
||||

|
||||
|
||||
如果使用 MQ,每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。而 MQ 每秒钟 5k 个请求进来,就 2k 个请求出去,结果就导致在中午高峰期(1 个小时),可能有几十万甚至几百万的请求积压在 MQ 中。
|
||||
|
||||

|
||||

|
||||
|
||||
这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是 A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给解决掉。
|
||||
|
||||
|
BIN
img/kafka-order-1.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
img/kafka-order-2.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
img/mq-1.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
img/mq-10.png
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
img/mq-11.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
img/mq-2.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
img/mq-3.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
img/mq-4.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
img/mq-5.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
img/mq-6.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
img/mq-7.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
img/mq-8.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
img/mq-9.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
img/rabbitmq-message-lose-solution.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
img/rabbitmq-message-lose.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
img/rabbitmq-order-1.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
img/rabbitmq-order-2.png
Normal file
After Width: | Height: | Size: 29 KiB |