消息中间价键

283 阅读7分钟

1 Kafka

1.1 kafka简介:

消息队列的好处 : 解耦合 、 异步处理 、 流量削峰

消息队列的缺点 系统可用性降低、系统复杂度提高、一致性问题

1.1 消费模式:

一对一消费 : 消息生产者发布消息到Queue队列中,通知消费者从队列中拉取消息进行消费。消息被消费之后则删除,Queue支持多个消费者,但对于一条消息而言,只有一个消费者可以消费,即一条消息只能被一个消费者消费。

一对多消费 :也就是发布订阅模式,发布到topic中的数据可以被多个消费者消费,消费之后,数据不会被清除,kafka默认保留一段时间,再删除。

1.2 文件存储方式

Kafka文件存储也是通过本地落盘的方式存储的,主要是通过相应的log与index等文件保存具体的消息文件。生产者不断向log文件追加消息。为了防止文件过大,导致定位效率下降,常选择每写1G文件,重新创建一个log文件。kafka使用分片+索引的方式来进行定位。

1.3 kafka如何保证数据安全?

  • 生产者ISR(同步副本集) : 为保证producer发送的数据能够可靠的发送到指定的topic中,topic的每个partition收到producer发送的数据后,都需要向producer发送ack,如果producer收到ack就会进行下一轮的发送,否则重新发送数据
  • 副本数据同步全部follower同步完成完成发送ack,这种方案在容错率上面更加有优势,同时对于分区的数据而言,每个分区都有大量的数据。虽然网络延迟较高,但是网络延迟对于Kafka的影响较小。

1.4 如何保证消息不被重复消费,保证消息的幂等性?

这个需要结合具体业务来处理,下面举几个例子:

  • 要写数据库,那么就根据该消息生成唯一主键,在写入之前,先判断该主键是否存在,若存在,则不需要插入改为更新操作
  • 写redis , 没有问题,每次操作都是set,天然具备幂等性
  • 复杂一些,需要生产者发送消息时,每次都带上一个全局唯一的id值,类型与订单号,处理之前先根据id去查询一下,看看是否已经处理过该条消息。
  • 基于数据库的唯一键来保证重复数据不会差入多条。

1.5 数据一致性问题:

  • follower故障: follower发生故障后会被临时提出ISR,等待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步,等待该follower的LEO大于等于该partition的HW,即follower追上leader之后,就可以重新加入ISR了。

  • leader故障: 从follower中选举出一个 ,将其升级为leader。为了保证多个副本之间的数据的一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader中同步数据。

1.6 如何保证消息安全传输,不丢失?

1.6.1 生产者弄丢数据

设置了acks=all,一定不会丢,要求是,你的leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

1.6.2 kafka弄丢数据

给每一个leader配置至少一个follower ; leader至少感知到一个follower已经复制过消息之后,才能返回消息写入成功的信息。

1.6.3 消费者弄丢数据

kafka会自动提交offset,只要把自动提交关闭,在处理结束后手动提交offset,就可以保证数据不会丢失。但是这种情况可能会导致消息的重复消费,需要额外的操作保证它自己的幂等性。

1.7 kafka如何保证消息消费的顺序

写N个内存queue,具有相同key值的数据都放在同一个内存queue中,然后对于每一个N线程来讲,内部的消息消费都一定是顺序消费的。

1.8 如何解决消息队列的延时以及过期消费的问题?消息满了要如何处理?

1.8.1 消息堆积

  • 修复consumer的问题
  • 新建一个topic,容量是原来的10倍那么大
  • 写一个临时发访consumer的程序,并征召其他的机器一起以10倍的速度来消费堆积的消息
  • 堆积的数据消费完毕之后,再恢复到原来的配置

1.8.2 消息过期失效了怎么办?

批量重导 当消息大量堆积的时候,可以先将消息丢弃掉,等过掉高峰期的时候 ,再编写程序,将丢失的消息重新查到,写入到消息队列中。

1.8.3 消息满了 (存疑)

临时写程序,接入数据来消费。消费一个丢弃一个,快速消费掉所有的数据,然后在等到有空的时候,将数据补回来。

2 RabbitMQ

2.1 RabbitMQ的高可用性:

RabbitMQ有三种模式:单机模式、普通集群模式、镜像集群模式,其中单机模式只是自己玩玩,没实际应用价值;普通集群模式没有高可用性,只是一个普通的集群,这个模式主要是提高吞吐量。镜像集群模式可以保证高可用性:创建的queue,无论是元数据还是queue中的消息,都会存在于多个实例上,即每个RabbitMQ节点都有这个queue的一个完整镜像。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。

这样做,可以保证任何一个机器宕机后,其他节点仍然保存有queue的完整数据。但是性能开销过大,而且不是分布式的 ,没有扩展性可言。

2.2 如何保证消息安全传输,不丢失?

2.2.1 生产者弄丢了数据

可以开启RabbitMQ提供的事务功能,在消息成功被RabbitMQ接收后,再完成事务,否则进行回滚操作。但是这样子吞吐量会下降。可以在生产者设置开启confirm模式,每条需要写入的消息都会分配一个全局唯一的id值,写入成功后,RabbitMQ会返回一个ACK,表明接收成功。若超过一定时间没有接收到这个确认信号的话 ,重新写入。这二者的区别在于,事务处理是同步的 ,而confirm是异步的 ,可以减轻系统的压力。

2.2.2 RabbitMQ自己弄丢了数据

开启RabbitMQ的持久化操作,

2.2.3 消费端弄丢了数据

比如刚刚消费到,但是还没处理,进行就挂了,消息就丢失了。针对这个问题,需要用到RabbitMQ提供的ack机制。关闭RabbitMQ的自动ack,在自己代码里确保处理完的时候,再在程序里面ack。

2.3 RabbitMQ如何保证消息的有序消费

拆分多个queue,每个queue对应一个consumer,只是多一些queue,就可以做到有序消费。

3 如何自己设计一个消息队列呢

写一点自己的思考:

  • 消息队列 ,首先肯定要支持自动伸缩,也就是在线扩容。可以参考kafka的设计理念:broker-topic-partition,每个partition放一个机器,如果容量不够了,增加机器就好
  • 消息的持久化机制,要落地磁盘的。落磁盘的话,采用顺序写的方式,效率更高。
  • 消息队列的可用性:参考kafka的高可用机制,采用多副本机制进行处理
  • 支持数据0丢失:等副本里面也存储了消息之后,才算消息写入成功。