想想为什么要使⽤MQ?
1.解耦,系统A在代码中直接调⽤系统B和系统C的代码,如果将来D系统接⼊,系统A还需要修改代码,过于⿇烦!2.异步,将消息写⼊消息队列,⾮必要的业务逻辑以异步的⽅式运⾏,加快响应速度3.削峰,并发量⼤的时候,所有的请求直接怼到数据库,造成数据库连接异常使⽤了消息队列会有什么缺点?
1.系统可⽤性降低:你想啊,本来其他系统只要运⾏好好的,那你的系统就是正常的。现在你⾮要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可⽤性降低
2.系统复杂性增加:要多考虑很多⽅⾯的问题,⽐如⼀致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增⼤。如何保证消息队列是⾼可⽤的?使⽤集群的⽅式维持MQ的可⾼⽤性。如何保证消息不被重复消费?
保证消息不被重复消费的关键是保证消息队列的幂等性,这个问题针对业务场景来答分以下⼏点:
1.⽐如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做⼀个唯⼀主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
2.再⽐如,你拿到这个消息做redis的set的操作,那就容易了,不⽤解决,因为你⽆论set⼏次结果都是⼀样的,set操作本来就算幂等操作。3.如果上⾯两种情况还不⾏,上⼤招。准备⼀个第三⽅介质,来做消费记录。以redis为例,给消息分配⼀个全局id,只要消费过该消息,将 ⽣产者的消息没有投递到MQ中怎么办?从⽣产者弄丢数据这个⾓度来看,RabbitMQ提供transaction和confirm模式来确保⽣产者不丢消息。transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。 然⽽缺点就是吞吐量下降了。因此,按照博主的经验,⽣产上⽤confirm模式的居多。⼀旦channel进⼊confirm模式,所有在该信道上⾯发布的消息都将会被指派⼀个唯⼀的ID(从1开始),⼀旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送⼀个Ack给⽣产者(包含消息的唯⼀ID),这就使得⽣产者知道消息已经正确到达⽬的队列了.如果rabiitMQ没能处理该消息,则会发送⼀个Nack消息给你,你可以进⾏重试操作。 2.消息队列丢数据 处理消息队列丢数据的情况,⼀般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使⽤,你可以在消息持久化磁盘后,再给⽣产者发送⼀个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么⽣产者收不到Ack信号,⽣产者会⾃动重发。那么如何持久化呢,这⾥顺便说⼀下吧,其实也很容易,就下⾯两步①、将queue的持久化标识durable设置为true,则代表是⼀个持久的队列②、发送消息的时候将deliveryMode=2 这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据。在消息还没有持久化到硬盘时,可能服务已经死掉,这种情况可以通过引⼊mirrored-queue即镜像队列,但也不能保证消息百分百不丢失(整个集群都挂掉)3.消费者丢数据 启⽤⼿动确认模式可以解决这个问题 ①⾃动确认模式,消费者挂掉,待ack的消息回归到队列中。消费者抛出异常,消息会不断的被重发,直到处理成功。不会丢失消息,即便服务挂掉,没有处理完成的消息会重回队列,但是异常会让消息不断重试。 ②⼿动确认模式,如果消费者来不及处理就死掉时,没有响应ack时会重复发送⼀条信息给其他消费者;如果监听程序处理异常了,且未对异常进⾏捕获,会⼀直重复接收消息,然后⼀直抛异常;如果对异常进⾏了捕获,但是没有在finally⾥ack,也会⼀直重复发送消息(重试机制)。 ③不确认模式,acknowledge=\"none\" 不使⽤确认机制,只要消息发送完成会⽴即在队列移除,⽆论客户端异常还是断开,只要发送完就移 除,不会重发。 如何保证消息的顺序性? 针对这个问题,通过某种算法,将需要保持先后顺序的消息放到同⼀个消息队列中。然后只⽤⼀个消费者去消费该队列。同⼀个queue⾥的消息⼀定是顺序消息的。我的观点是保证⼊队有序就⾏,出队以后的顺序交给消费者⾃⼰去保证,没有固定套路。例如B消息的业务应该保证在A消息后业务后执⾏,那么我们保证A消息先进queueA,B消息后进queueB就可以了。 因篇幅问题不能全部显示,请点此查看更多更全内容