周末有空,想到之前项目中用到了消息队列,当时只是简单用了一下工具包,开发完功能需求之后,自己又去认真看来一下原理,现在有空就和小伙伴们分享一下吧,希望有问题的地方大家可以指正,互相交流。
我们项目是在物联网场景下的,需要采集底层设备的数据,然后对数据进行分析处理,因为数据上传是频率还是很高的,所以项目中用了消息队列进行数据缓冲,上层从队列中读取设备上传的数据,进行数据清理,再进行前端展示,项目刚开始用的是EMQ,是不到大家有没有听说过,当时用这个的原因是因为项目中有个对设备数据生成实时告警的需求,而EMQ中的告警引擎的功能很适合这个需求,所以就用了,后面由于种种原因,还是换回了kafka哈哈哈,不得不说还是kafka顶啊。
其实市面上的消息队列有很多,比如rabbitMQ,activeMQ,kafka,redis由于能够存储很多的数据数据结构也能用来作为消息队列,消息队列固然很香,尤其是面对其异步处理的机制能够应对很多高并发的数据请求来缓解数据库压力,但是不知道小伙伴们有没有和我同样的苦恼,在一些场景下面,会出现消息重复消费,消息丢失,消息消费顺序胡乱的情况,这个是很致命的,会产生很大的生产环境事故,一不小心可能就领盒饭了,所以怎么调教好消息队列很是关键,下面我就以自己项目中的kafka举例,分析和解决这些问题。
消息丢失的场景的原因在生产者,Broker,消费者中都有体现,这里我分别对着三个做出相应的分析,然后给出相应的解决方案。
在生产者层面:
这里我就用spring整合kafka的工具包为例了,在生产者发送消息是,会调用kafkaTemplate.send()时候,kafka的配置文件是有个ack参数配置的,ack的配置参数有三个,如下:
0:代表生产者将消息发送到Broker后,不等Broker回复确认发送成功消息,直接发送下一条消息。
1:默认方式,生产者等Broker回复确认发送成功之后,再接着发送下一条消息。
-1:等Broker的所有Follower回复确认消息之后再接着发送下一条消息。
注:这里的Broker和Follower的相关概念在后面我会详细介绍,此处就先略过,大家目前知道有这个东西存在就好。
所以一般设置为0是很危险的,很容易就会造成消息丢失,因为在生产者发送消息的过程中,可能就会一些网络抖动的原因,导致消息不能成功到达Broker,而在Broker中只会已经到达已经成功提交的消息做信息持久化,写入到日志文件中。
在springBoot中的kafkaTemplate.send()最好就上回调函数,这样就可以用过回调函数来判断消息是否成功提交,如果提交失败,我们可以采取重试的方式来保证消息不会丢失。
在Broker层面:
当生产者的消息成功发送到Broker中的时候,可能因为Broker宕机的原因,导致消息不能够持久化,所以我们最好进行集群部署,采用多个Broker的方式,至少要保证一个Broker是正常存活的,不然也会造成消息的丢失。
在消费者层面:
在消费费中有个offset的概念,是用来表示当前消费者消费到哪一条消息了,就像我们看书时用到的标签一样,到我们看到那一页了就会用书签记录下来,防止下一次会忘记自己看到了哪里,但是这里就有一个问题,一个顺序的问题,比如我要看到99页,是先把书签放到99页然后去读书,还是先看到99页然后再放上书签 ,这里想一下,如果先放上书签,途中看到95页就有事没看了,那么下一次重新看书时候,就会丢失95-99页的内容,这个情况在kafka中就反映在消费者端手动提交和自动提交的问题上。
自动提交的场景在消息消费失败的情况下还是会更新offset,导致消息丢失。而在手动提交提交的情况下,当消息消费失败时,可以进行重试消费,但是这种情况可以解决消息丢失的问题,也会引出新的问题,那就是消息会重复消费。
在这里就要提到一个幂等性的概念,简而言之就是无论执行一个任务多次,其结果是不变的,比如abs(1),无论执行多少次abs(1)都是1,而1++的结果是会随着执行次数,每次的结果都是不相同的,这个幂等性就体现在生产者的消息的发送和消费者消息的消费。 刚刚我们在上文中有提到,在生产者端消息发送失败失败后,会进行消息的重新发送,那么这个消息的重新发送的重复场景我们该如何规避掉的,也就是怎么避免一条消息会被重复的发送到Broker,这里用了一种空间换时间的方式,就是在每次发送的消息中加一个唯一key值,在Broker中会对重复的消息进行去重,在消费也是这样,面对消息的重复消费,也是通过这种加字段的方式来避免消息的重复消费。
关于如何避免消费消息顺序的问题,由于本文篇幅的原因,我放在后面一篇文章中再和大家慢慢讲解,好了,我们再来回顾一下,上面所讲的内容,首先是避免消息的丢失,我们通过生产者,Broker,消费者三个方面分别进行分析和解决,其次是消息的重复的消费,在这里我们引出了幂等性的概念,通过空间换时间的方法来避免消息的重复发送和重复消费,希望大家可以慢慢消化,有什么错误还请大家多多指教。
下一篇:如何防止kafka消息顺序混乱问题