当前位置: 首页 > 科技观察

一个天天用消息队列的人,不知道为什么用MQ,有点尴尬

时间:2023-03-12 03:18:23 科技观察

1。为什么要使用消息队列?分析:一个用消息队列的人,不知道自己为什么用,有点尴尬。不复习这点,很容易被质疑,开始胡说八道。答:对于这个问题,我们只回答三个最重要的应用场景(不可否认还有其他的,但只回答主要的三个),即以下六个词:解耦、异步、削峰(一)解耦传统模式:传统模式的缺点:系统之间的耦合度太强。如上图所示,A系统在代码中直接调用了B系统和C系统的代码。以后要是连上D系统,A系统也需要修改代码,麻烦!中间件模式:中间件模式的优点:将消息写入消息队列,需要消息的系统自己从消息队列订阅,所以系统A不需要做任何修改。(2)异步传统模式:传统模式的缺点:一些非本质的业务逻辑以同步的方式运行,过于耗时。中间件模式:中间件模式的优点:将消息写入消息队列,非必要的业务逻辑异步运行,加快响应速度(3)调峰传统模式传统模式的缺点:当并发量为大,所有的请求都直接去数据库,导致数据库连接异常。中间件模式:中间件模式的优点:A系统根据数据库能处理的并发量慢慢从消息队列中拉取消息。在生产中,这种短暂的峰值积压是允许的。2、使用消息队列有什么缺点?分析:如果一个使用MQ的项目连这个问题都没有考虑过,再引入MQ,会给自己的项目带来风险。当我们引入一项技术时,我们必须充分了解这项技术的缺点,才能预防它。记住,不要给公司挖坑!答:答案很简单,从以下两个角度。系统可用性降低:你认为,只要其他系统运行良好,你的系统就是正常的。现在你还要给它加一个消息队列,消息队列挂了,你的系统也不好。因此降低了系统可用性,增加了系统复杂度:需要考虑很多问题,比如一致性问题,如何保证消息不被重复消费,如何保证消息的可靠传输等。因此,需要考虑的东西更多,系统的复杂度也增加了。但是,我们仍然应该使用它。3、如何选择消息队列的类型?首先,博主只知道ActiveMQ、RabbitMQ、RocketMQ、Kafka,对ZeroMQ等MQ了解甚少,所以只能根据这四种MQ给出答案。分析:由于项目中使用了MQ,所以需要提前对业界流行的MQ进行调研。如果你连各个MQ的优缺点都不了解,可以根据自己的喜好使用某个MQ,或者针对项目进行挖掘。坑。如果面试官问:“你为什么要用这种MQ?”你直接回答“由领导决定”。这种回答很LOW。还是那句话,不要给公司挖坑。我们可以看到RabbitMQ的发布频率比ActiveMq高很多。至于RocketMQ和kafka,就不带大家去看了。简而言之,它们比ActiveMQ更活跃。详情可以自行查看。这里再提供一张性能对比表综合以上材料得出以下两点:(1)对于中小型软件公司,建议选择RabbitMQ。一方面,erlang语言天生就具有高并发性,其管理界面使用起来非常方便。俗话说,成则萧何,败则萧何!他的缺点也在这里。虽然RabbitMQ是开源的,但是国内有多少程序员可以自定义开发erlang呢?好在RabbitMQ的社区很活跃,可以解决开发问题。过程中遇到的bug,这个对于中小型公司来说非常重要。之所以不考虑rocketmq和kafka,一方面是中小软件公司不如互联网公司,数据量没那么大。在选择消息中间件的时候,首先要选择功能比较齐全的,kafka除外。之所以不考虑rocketmq,是因为rocketmq是阿里出品的。如果阿里放弃维护rocketmq,中小型公司一般找不到人进行rocketmq的定制化开发,所以不推荐。(2)大型软件公司根据具体用途在rocketMq和kafka之间进行选择。一方面,大型软件公司有足够的资金搭建分布式环境和足够大的数据量。对于rocketMQ,大型软件公司也可以抽出人力进行rocketMQ的定制化开发。毕竟国内有相当多的人有能力修改JAVA源码。至于Kafka,根据业务场景选择。如果有日志收集功能,Kafka绝对是首选。选择哪一个取决于使用场景。4、如何保证消息队列高可用?分析:第二点提到,引入消息队列后,系统的可用性下降。在生产中,没有人在独立模式下使用消息队列。因此,作为一个合格的程序员,应该对消息队列的高可用有深刻的理解。如果面试的时候,面试官问,你的消息中间件是怎么保证高可用的?如果你的回答只是表明你只能订阅和发布消息,面试官会怀疑你是不是自己玩弄自己,并没有在生产中使用。所以,请做一个爱思考、会思考、懂思考的程序员。答:要回答这个问题,需要对消息队列的集群模式有深入的了解。以rcoketMQ为例,其集群有多主模式、多主多从异步复制模式、多主多从同步双写模式。多主多从模式部署架构图(网上找的,懒,懒得画了):其实博主第一次看到这个图的时候,觉得跟Kafka差不多。它只是一个NameServer集群。在Kafka中,使用了zookeeper。两者都用于保存和发现master和slave。通信过程如下:Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定时从NameServer获取Topic路由信息,与提供Topic服务的BrokerMaster建立长连接,并向其发送心跳经纪人定期。Producer只能向Brokermaster发送消息,而Consumer则不同。它与同时提供Topic服务的Master和Slave建立长连接,可以订阅来自BrokerMaster和BrokerSlave的消息。kafka呢,为了对比,直接上传kafka的拓扑架构图(我一直在找,懒得画Logs,系统CPU,Memory等),几个broker(Kafka支持水平扩展,一般broker越多,集群吞吐量越高),几个消费者组,一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,并在ConsumerGroup变化时重新平衡。Producer采用push方式向broker发布消息,Consumer采用pull方式订阅并消费来自broker的消息。至于rabbitMQ,也有普通集群和镜像集群两种模式。自己看懂还是比较简单的,两个小时就能看懂。要求,在回答高可用问题的时候,要能够逻辑清晰的画出自己的MQ集群架构或者描述清楚。5、如何保证消息不被重复消费?分析:其实这个问题可以换个方式问,如何保证消息队列的幂等性?这个问题可以认为是消息队列领域的一个基础问题。换句话说,就是在考察你的设计能力。这个问题的答案可以根据具体的业务场景来回答,没有固定的答案。答:先说说为什么会造成重复消费?其实不管是哪种消息队列,重复消费的原因其实都是类似的。一般情况下,消费者在消费消息时,会在消费后向消息队列发送一条确认消息。消息队列会知道该消息已经被消费,并将该消息从消息队列中删除。只是不同的消息队列发送的确认信息不同而已。例如,RabbitMQ发送ACK确认信息,RocketMQ返回CONSUME_SUCCESS成功标志。Kafka其实有offset的概念。掌握教程),即每条消息都有一个偏移量。kafka消费消息后,需要提交offset,让消息队列知道自己消费了。重复消费的原因?就是因为网络传输等故障,没有向消息队列发送确认信息,导致消息队列不知道自己已经消费了这条消息,重新分发消息给其他消费者。如何解决?本题根据业务场景分为以下几点(1)比如你拿到这个消息做数据库的insert操作。这很容易。为此消息创建一个唯一的主键。即使重复消费,也会造成主键冲突,避免数据库出现脏数据。(2)再比如,你得到这个消息很容易做redis的set操作,没必要去解决。因为无论设置多少次结果都是一样的,所以设置操作被认为是幂等操作。(3)如果以上两种情况还不行,就用大招。为消费记录准备第三方媒介。以redis为例,给消息分配一个全局id,只要消息被消费,就会以K-V的形式写入redis。消费者开始消费之前,可以去redis中查看是否有消费记录。6、如何保证消费的可靠传输?分析:在使用消息队列的过程中,要保证消息不能被消费多了,也不能消费少了。如果无法实现可靠传输,可能会给企业带来数千万的财产损失。同样,如果在使用过程中不考虑可靠性传输,这不是给公司挖坑吗,拍拍屁股走人,公司损失的钱谁来承担。还是那句话,认真对待每一个项目,不要给公司挖坑。答:其实对于可靠传输,每个MQ都要从三个角度来分析:生产者丢数据,消息队列丢数据,消费者丢数据。丢失数据RabbitMQ(1)生产者丢失数据从生产者丢失数据的角度来看,RabbitMQ提供了事务和确认两种方式来保证生产者不丢失消息。事务机制就是在发送消息之前,先打开这个东西(channel.txSelect()),然后再发送消息。如果在发送过程中出现异常,则回滚(channel.txRollback()),如果发送成功,则提交(channel.txCommit())。但缺点是吞吐量降低。所以根据博主的经验,生产中多采用confirm模式。一旦频道进入确认模式,频道上发布的所有消息都将被分配一个唯一的ID(从1开始)。一旦消息被投递到所有匹配的队列,rabbitMQ将向生产者发送一个Ack(包括消息的唯一ID),这让生产者知道消息已经正确到达目标队列。如果rabiitMQ处理消息失败,它会向你发送一个Nack消息,你可以重试操作。处理Ack和Nack的代码如下(不知道怎么编码的就偷偷上传):println("nack:deliveryTag="+deliveryTag+"multiple:"+multiple);}@OverridepublicvoidhandleAck(longdeliveryTag,booleanmultiple)throwsIOException{System.out.println("ack:deliveryTag="+deliveryTag+"multiple:"+multiple);}});(2)消息队列丢失数据处理消息队列丢失数据的情况,一般是开启持久化磁盘的配置。这种持久化配置可以与确认机制结合使用。消息持久化到磁盘后,您可以向生产者发送Ack信号。这样,如果rabbitMQ在消息持久化到磁盘之前挂掉了,producer是收不到Ack信号的,producer会自动重发。那么如何持久化呢,这里就说一下吧,其实很简单,按照下面两步就可以了1.设置队列的持久化标志durable为true,表示它是一个持久化队列2.发送消息的时候,设置deliveryMode=2这样设置后,即使rabbitMQ挂了,重启后也可以恢复数据(3)消费者丢失数据消费者一般会丢失数据,因为他们采用了自动确认消息的方式。在这种模式下,消费者会自动确认收到信息。这时rahbitMQ会立即删除消息。在这种情况下,如果消费者因为异常而无法处理消息,那么消息就会丢失。作为解决方案,手动确认消息就足够了。KafkaProducer向Partition发布消息时,首先通过ZooKeeper找到Partition的Leader,然后无论Topic的ReplicationFactor有多少(即Partition有多少Replicas),Producer只发送消息给分区的领导者。Leader会将消息写入其本地Log。每个Follower从Leader拉取数据。针对以上情况,得出如下分析(1)生产者在Kafka生产中丢失数据,基本上是一个leader和多个follower。follower会同步leader的信息。因此,为了防止生产者丢失数据,做如下两个配置。第一个配置是在生产者端设置acks=all。这个配置保证只有follwer同步完成后才认为消息成功。在生产者端设置retries=MAX。一旦写入失败,就会无限重试。(2)消息队列数据丢失对于消息队列数据丢失的情况,无非是leader还没有同步数据就挂掉了。这时候,zooppeer会把其他follower切换成leader,数据就会丢失。对于这种情况,应该做两个配置。replication.factor参数,这个值必须大于1,即每个partition至少要有2个replicasmin.insync.replicas参数,这个值必须大于1,这是要求一个leader至少要有perceivethattheatleastasafollowerstillwithitself保持联系这两个配置加上上面producer的配置可以一起使用,基本可以保证kafka不丢数据。(3)消费者丢失数据。在这种情况下,offset一般是自动提交的,然后你在处理的过程中挂掉了。卡夫卡认为你处理了它。再次强调一下offset是什么offset:指的是kafka的topic中每个消费者组消费的下标。简单来说,一条消息对应一个偏移量下标。如果每次消费数据时都提交offset,则下一次消费将从提交的offset加一开始。比如一个topic有100条数据,我消费50条提交,那么此时Kafka服务器记录提交的offset是49(offset从0开始),那么下一次消费将从50。解决方法也很简单,改成手动提交即可。请自行查看ActiveMQ和RocketMQ7.如何保证消息的顺序?分析:其实并不是所有的公司都有这样的业务需求,但是这个问题还是需要重新审视的。答:解决这个问题,通过一定的算法,把需要有序保存的消息放到同一个消息队列中(kafka中的partition,rabbitMq中的queue)。然后只使用一个消费者来消费队列。可能有人会问:如果有多个consumer消费吞吐量怎么办?这个问题没有固定的答案。比如我们有一个微博操作,发微博,写评论,删除微博,这三个异步操作。如果是这样的业务场景,再试一次就好了。比如你这个消费者,首先进行了写评论的操作,但是此时微博还没有发,写评论肯定是失败了。稍等片刻。等待另一个消费者先执行写评论的操作,再执行,就成功了。总之,针对这个问题,我的观点是保证入队顺序足够,出队后的顺序留给消费者自己,保证没有固定套路。总结写到这里,希望读者在对本文提出的问题进行深入准备后,能够涵盖消息队列的大部分知识点。面试官不问这些问题怎么办,很简单,把几个问题解释清楚,突出下面考虑的全面性。最后,其实我并不提倡这种突击审核。希望大家能够放下基本功,做一个爱思考、懂思考、会思考的程序员。