问题总结1

mq

想想为什么要使用MQ?

1.解耦,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

2.异步,将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度

3.削峰,并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常

使用了消息队列会有什么缺点?

1.系统可用性降低:你想啊,本来其他系统只要运行好好的,那你的系统就是正常的。现在你非要加个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性降低

2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。

如何保证消息队列是高可用的?

使用集群的方式维持MQ的可高用性。

rabbitmq消息丢失的几种情况?

  1. 生产者发送消息丢失
    a.丢失原因:当生产者向mq发送消息过程中,网络原因导致MQ没有接收到消息,生产者以为收到。
    b.解决方法:事务机制,confrm机制
    transaction机制就是说,发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。
    然而缺点就是吞吐量下降了。因此,按照博主的经验,生产上用confirm模式的居多。一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

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

那么如何持久化呢,这里顺便说一下吧,其实也很容易,就下面两步

①、将queue的持久化标识durable设置为true,则代表是一个持久的队列

②、发送消息的时候将deliveryMode=2

这样设置以后,rabbitMQ就算挂了,重启后也能恢复数据。在消息还没有持久化到硬盘时,可能服务已经死掉,这种情况可以通过引入mirrored-queue即镜像队列,但也不能保证消息百分百不丢失(整个集群都挂掉)
镜像队列:将queue镜像到cluster中其他的节点之上。 在该实现下,如果集群中的一个节点失效了,queue能自动地切换到镜像中的另一个节点以保证服务的可用性

  1. 消费者丢失消息
    a.丢失原因:自动ack的时候就是默认机制情况下,ack会自动返回成功,表明发送成功,下次就不会发送消息了。但如果此时消费者还没处理完就宕机了,那么这个消息就有可能丢失
    b.解决方法:必须关闭自动ack,改成手动ack,然后每次在代码里确认处理完成以后再ack一把。
    启用手动确认模式可以解决这个问题
    ①自动确认模式,消费者挂掉,待ack的消息回归到队列中。消费者抛出异常,消息会不断的被重发,直到处理成功。不会丢失消息,即便服务挂掉,没有处理完成的消息会重回队列,但是异常会让消息不断重试。

②手动确认模式,如果消费者来不及处理就死掉时,没有响应ack时会重复发送一条信息给其他消费者;如果监听程序处理异常了,且未对异常进行捕获,会一直重复接收消息,然后一直抛异常;如果对异常进行了捕获,但是没有在finally里ack,也会一直重复发送消息(重试机制)。

③不确认模式,acknowledge=“none” 不使用确认机制,只要消息发送完成会立即在队列移除,无论客户端异常还是断开,只要发送完就移除,不会重发。

如何保证消息的顺序性?

针对这个问题,通过某种算法,将需要保持先后顺序的消息放到同一个消息队列中。然后只用一个消费者去消费该队列。同一个queue里的消息一定是顺序消息的。我的观点是保证入队有序就行,出队以后的顺序交给消费者自己去保证,没有固定套路。例如B消息的业务应该保证在A消息后业务后执行,那么我们保证A消息先进queueA,B消息后进queueB就可以了。

1、RabbitMQ的消息不被顺序消费的情况

    queue(队列)中的消息只能被一个消费者所消费,然后多个消费者在消费消息的过程中是无序的,多个消费者消费同一个queue,顺序错乱就会导致数据的不一致。这和我们预期的结果不符,如果这样的情况很多,那么就造成了数据库中的数据完成不对,同步工作也是白费了。

2、RabbitMQ保证消息顺序性的措施

    2.1 拆分多个 queue,每个 queue 对应一个 consumer(消费者),就是多一些 queue而已
    2.2 一个 queue 对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的  作者:黑马程序员广州中心3.

3.考虑重复消费(慎用)
那么如果我们要求严格有序呢?就是中间不能出现缺口。那么这种就不能依靠时间戳了,那我们可以添加一个整型的version字段,消息里也携带了一个version字段,每次更新数据的时候我们采用这种更新方式(乐观锁): update tbl set …, version=version+1 where version=@message.version。如果这条语句没有更新成功,则返回行数就不为1,这个时候我们抛出异常让消息队列重试,等到合适的消息到来的时候就会更新成功了。使用version字段这种方式可能就要producer和consumer进行协调了(其实就是有些耦合了),因为消息里也要携带version字段。但是设计这个version字段的时候也要一些考虑,我更建议的更新方式是update tbl set …,version=@message.newversion where version=@message.oldversion。这样如果发送消息的version因为某些原因(比如有些更新并不发送消息)没有严格递增两边也可以兼容。还有一点是,要控制consumer端数据的写入点。比如我们的处理消息消费的地方更新数据外还有另外一个地方更新数据,这个地方将version给更新了,那么就会导致producer和consumer版本不一致,最后导致怎么也同步不起来了。而producer方也要控制,比如如果我们人肉直接去表里修了数据可能就没有消息发出了。

如何保证消息不被重复消费?

  1. 重复消费原因:消费者消费超时,引起重新消费,从而重新入队
    生产者发送消息因为网络原因没有cinfirm导致重复发送

  2. 保证消息不被重复消费的关键是保证消息队列的幂等性,这个问题针对业务场景来答分以下几点:

1.比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

2.再比如,你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。

3.如果上面两种情况还不行,上大招。准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。

queue消息过多怎么办

在实际项目中,一个consumer肯定是不够的,queue中的消息过多。一个consumer明显会处理过慢,等待时间过长。这时候就需要多个consumer来缓解压力。

这会带来一个问题,当consumer过多的时候,消息就会分配的不均匀,导致某些concumer非常忙,有些特别闲。而且consumer也会有掉线的情况,
queue的消息分发是第n个消息给第n个consumer的,这就会造成最开始处理消息的consumer在处理完成后会有闲置的可能性,而后面的一直在忙,消息分发的不均匀造成了很大的资源浪费,所以我们需要的是把消息分发给闲置的consumber。
这个操作是在consumer中完成的,在声明频道之后就指定这个consumer在同一时间内只处理一个消息,在上个消息未交付之前不要给我再分发消息。这个操作是容易完成的

//公平分发、同一时间只处理一个消息。
channel.BasicQos(0, 1, false)

数据库

分页最好别让别人看到10万条以后的数据,要不然会很慢!就算用索引,为什么
update三个语句一个有主键,一个没有主键,还有一个普通索引,这几个锁有什么区别

hashmap为什么会不安全

线上环境OOM的定位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值