【模块十一】分布式篇MQ消息中间件篇☞参考答案

编程

  • 为什么要用消息队列

异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。

削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,消息队列在中间可以起到一个缓冲的作用,把消息暂存在消息队列中,下游服务就可以按照自己的节奏进行慢慢处理。

二、消息队列优点与缺点

优点:

异步处理,应用解耦,流量削锋和消息通讯

缺点:

1.系统可用性降低:

2.系统复杂性增加:需要考虑的东西多了,比如:一致性问题,如何保证消息不被重复消费,保证消息的可靠传输性等

三、消息队列怎么选型?

(1)中小型软件公司,建议选RabbitMQ.一方面,erlang语言天生具备高并发的特性,而且他的管理界面用起来十分方便。他的弊端也在这里,虽然RabbitMQ是开源的,然而国内有几个能定制化开发erlang的程序员呢?所幸,RabbitMQ的社区十分活跃,可以解决开发过程中遇到的bug,这点对于中小型公司来说十分重要。不考虑rocketmq和kafka的原因是,一方面中小型软件公司不如互联网公司,数据量没那么大,选消息中间件,应首选功能比较完备的,所以kafka排除。不考虑rocketmq的原因是,rocketmq是阿里出品,如果阿里放弃维护rocketmq,中小型公司一般抽不出人来进行rocketmq的定制化开发,因此不推荐。
(2)大型软件公司,根据具体使用在rocketMq和kafka之间二选一。一方面,大型软件公司,具备足够的资金搭建分布式环境,也具备足够大的数据量。针对rocketMQ,大型软件公司也可以抽出人手对rocketMQ进行定制化开发,毕竟国内有能力改JAVA源码的人,还是相当多的。至于kafka,根据业务场景选择,如果有日志采集功能,肯定是首选kafka了。具体该选哪个,看使用场景。

 

四、MQ的死信队列

kafka没有重试机制不支持消息重试,也没有死信队列,因此使用kafka做消息队列时,如果遇到了消息在业务处理时出现异常,就会很难进行下一步处理。应对这种场景,需要自己实现消息重试的功能。

造成原因:

A.消息被拒绝(basic.reject/ basic.nack)并且不再重新投递 requeue=false

B.消息超期 (rabbitmq  Time-To-Live -> messageProperties.setExpiration())

C.队列超载(队列满了,无法再添加数据到mq中)

五、Kafka中有哪几个组件及其作用

Topic 主题:可以理解为一个队列,一个 Topic 又分为一个或多个分区

Producer生产者:在Kafka,生产者发布通信以及向Kafka主题发布消息。

Consumer消费者:Kafka消费者订阅了一个主题,并且还从主题中读取和处理消息。

Broker经纪人:在管理主题中的消息存储时,我们使用Kafka Brokers。一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。

Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。

Partition分区:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。

Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。

六、Kafka 采用 Pull 模式,还是 Push 模式 

一般消息系统,消费者存在两种消费模型:

 推送模型(Push):优势在于消息实时性高。劣势在于没有考虑consumer消费能力和饱和情况,容易导致producer压垮consumer。

拉取模型(Pull):优势在可以控制消费速度和消费数量,保证consumer不会出现饱和。劣势在于当没有数据,会出现空轮询,消耗CPU。

kafka采用pull模式

Kafka最初考虑的问题是,customer应该从brokers拉取消息,还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息

一些消息系统比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式

Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略

Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发。

七、RabbitMq与Kafaka的区别

1)在架构模型方面

RabbitMQ的broker由Exchange,Binding,queue组成,以broker为中心;有消息的确认机制。

Kafka 由producer,broker,consumer组成,以consumer为中心,无消息确认机制(可是kafka的确有ack机制,不明白?)。

2)在吞吐量

rabbitMQ在吞吐量方面稍逊于kafka

kafka具有高的吞吐量,内部采用消息的批量处理,zero-copy机制

3)在可用性方面

rabbitMQ支持miror的queue,主queue失效,miror queue接管。

kafka的broker支持主备模式。

4)在集群负载均衡方面

rabbitMQ的负载均衡需要单独的loadbalancer进行支持。

kafka采用zookeeper对集群中的broker、consumer进行管理,可以注册topic到zookeeper上;通过zookeeper的协调机制,producer保存对应topic的broker信息,可以随机或者轮询发送到broker上;并且producer可以基于语义指定分片,消息发送到broker的某分片上。

八、如何保证消息不丢失 =>>如何保证消息的可靠传输性

1.RabbitMQ

1.生产者丢数据—confirm机制

从生产者弄丢数据这个角度来看,RabbitMQ提供transaction(事务机制:同步)和confirm(确认机制:异步)来确保生产者不丢消息。

transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。生产者这块避免数据丢失,都是用confirm机制的

解决方案:死信队列

启动重试机制,超过一定阈值的重试还是失败,将消息放到死信队列,消费端并对死信队列进行监听,根据业务需要进行相应的处理。

2.消息队列丢数据—持久化机制

处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步
1、将queue的持久化标识durable设置为true,则代表是一个持久的队列
2、发送消息的时候将deliveryMode=2

这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据

3.消费者丢数据—手动提交

消费者丢数据一般是因为采用了自动确认消息模式。这种模式下,消费者会自动确认收到信息。这时rahbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就会丢失该消息。至于解决方案,采用手动提交确认消息即可。

2.Kafka

1.生产者丢数据—ack机制:Ackownledge确认机制

在kafka生产中,基本都有一个leader和多个follwer。follwer会去同步leader的信息。因此,解决方案:为了避免生产者丢数据,做如下两点配置

确认机制:第一个配置要在producer端设置acks=all。这个配置保证了,follwer同步完成后,才认为消息发送成功。 request.required.acks = all

##消息的确认模式

 ##0:不保证消息的到达确认,只管发送,低延迟但是会出现消息的丢失,在某个server失败的情况下,有点像TCP

 ##1(默认值):发送消息,并会等待leader 收到确认后,一定的可靠性

 ## all:发送消息,等待leader收到确认,并进行复制操作后,才返回,最高的可靠性

重发机制:在producer端设置retries=MAX,一旦写入失败,这无限重试

message.send.max.retries =3

2.消息队列丢数据

针对消息队列丢数据的情况,无外乎就是,数据还没同步,leader就挂了,这时zookpeer会将其他的follwer切换为leader,那数据就丢失了。

解决方案:针对这种情况,应该做两个配置。

replication.factor参数,这个值必须大于1,即要求每个partition必须有至少2个副本

min.insync.replicas参数,这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系

这两个配置加上上面生产者的配置联合起来用,基本可确保kafka不丢数据

3.消费者丢数据—手动提交

这种情况一般是自动提交了offset,然后你处理程序过程中挂了。kafka以为你处理好了。再强调一次offset是干嘛的
offset:指的是kafka的topic中的每个消费组消费的下标。简单的来说就是一条消息对应一个offset下标,每次消费数据的时候如果提交offset,那么下次消费就会从提交的offset加一那里开始消费。

解决方案:enable.auto.commit:fals关闭自动提交,采用手动提交确认消息即可。

九、如何保证消息的顺序性

RabbitMQ:需要保证顺序的消息投递到同一个queue中,这个queue只能有一个consumer,如果需要提升性能,可以用内存队列做排队,然后分发给底层不同的worker来处理。

Kafka:Kafka分布式的单位是partition,同一个partition用一个write ahead log组织,所以可以保证FIFO的顺序。不同partition之间不能保证顺序。但是绝大多数用户都可以通过message key来定义,因为同一个key的message可以保证只发送到同一个partition。

Kafka 中发送1条消息的时候,可以指定(topic, partition, key) 3个参数。partiton 和 key 是可选的。如果你指定了 partition,那就是所有消息发往同1个 partition,就是有序的。并且在消费端,Kafka 保证1个 partition 只能被1个 consumer 消费或者你指定 key(比如 orderId),具有同1个 key 的所有消息,会发往同1个 partition。

十、如何保证消息不被重复消费 =>>如何保证消息幂等性

1.重复消费产生原因

消费者消费后没有commit offset(程序崩溃/强行kill/消费耗时)

正常情况下,消费者在消费消息时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset

2.解决重复消费方案

(1)比如,你拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键,那么就算出现重复消费的情况,也会导致主键冲突,从而避免数据库出现脏数据。
(2)再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就是幂等操作。
(3)如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可

十一、消息消费失败怎么办—报文落表

业务处理异常时,暂不提交offset,利用数据库(关系型或非关系型)保存失败的消息记录,根据失败策略处理相应消息。保存好记录之后可以提交offset。

失败处理可以隔五分钟再往对应的消息队列发送该消息(发送成功就次数+1,将消息id也传入消息队列,方便记录失败次数)复杂情况可能需要记录消息失败的次数,到达一定次数后,改为手工处理。

十二、如何解决消息积压问题

1.积压原因

要么是发送变快了,要么是消费变慢了。

2.积压处理

发送端性能优化: 设置合理的并发和批量大小

消费端性能优化: 考虑采用扩容消费者群组的方式

十三、如何保证消息队列的高可用

kafka的高可用性

kafka一个最基本的架构认识:多个broker组成,每个broker是一个节点;你创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition就放一部分数据。

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即可。这就有所谓的高可用性了。

写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)

消费的时候,只会从leader去读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。

以上是 【模块十一】分布式篇MQ消息中间件篇☞参考答案 的全部内容, 来源链接: utcz.com/z/512651.html

回到顶部