消息队列的使用

使用场景:

        异步,削峰,解耦

缺点:

1.增加复杂性

        要维护,考虑消息丢失,重复消费,顺序消费

2.数据一致性 

        分布式事务:把下单,优惠券,积分。。。都放在一个事务里面一样,要成功一起成功,要失败一起失败。

3.可用性

        现在突然接入一个中间件在那放着,万一挂了怎么办?我下个单MQ挂 了,优惠券不扣了,积分不减了,可用性变差

消息重复:

        一般消息队列的使用,我们都是有重试机制的,就是说我下游的业务发生异常了,我会 抛出异常并且要求你重新发一次。 

我这个活动这里发生错误,你要求重发肯定没问题。但是大家仔细想一下问题在哪里? 是的,不止你一个人监听这个消息啊,还有别的服务也在监听,他们也会失败啊,他一失败他也要求重发,但是你这里其实是成功的,重发了,你的钱不就加了两次了?

就好比上面的这样,我们的积分系统处理失败了,他这个系统肯定要求你重新发送一次这个消息对吧, 积分的系统重新接收并且处理成功了,但是别人的活动,优惠券等等服务也监听了这个消息呀,那不就 可能出现活动系统给他加GMV加两次,优惠券扣两次这种情况么?真实的情况其实重试是很正常的,服务的网络抖动,开发人员代码Bug,还有数据问题等都可能处理失 败要求重发的。

一般我们叫这样的处理叫接口幂等。

幂等(idempotent、idempotence)是一个数学与计算机学概念,常⻅于抽象代数中。 在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

    幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函
    数不会影响系统状态,也不用担心重复执行会对系统造成改变。

例如,“setTrue()”函数就是一个幂等函数,无论多次执行,其结果都是一样的.更复杂的操作幂 等保证是利用唯一交易号(流水号)实现.

通俗了讲就是你同样的参数调用我这个接口,调用多少次结果都是一个,你加GMV同一个订单号你加一 次是多少钱,你加N次都还是多少钱。

但是如果不做幂等,你一个订单调用多次钱不就加多次嘛,同理你退款调用多次钱也就减多次了。

解决方案:

 

       分场景去考虑,看是强校验还是弱校验,比如跟金钱相关的场景那就很关键呀,就做强校验,别不是很重要的场景做弱校验。

强校验

比如你监听到用户支付成功的消息,你监听到了去加GMV是不是要调用加钱的接口,那加钱接口下面再 调用一个加流水的接口,两个放在一个事务,成功一起成功失败一起失败。

每次消息过来都要拿着订单号+业务场景这样的唯一标识(比如天猫双十一活动)去流水表查,看看有 没有这条流水,有就直接return不要走下面的流程了,没有就执行后面的逻辑。

之所以用流水表,是因为涉及到金钱这样的活动,有啥问题后面也可以去流水表对账,还有就是帮助开 发人员定位问题。

弱校验:

这个简单,一些不重要的场景,比如给谁发短信啥的,我就把这个id+场景唯一标识作为Redis的key, 放到缓存里面失效时间看你场景,一定时间内的这个消息就去Redis判断。

用KV就算消息丢了可能这样的场景也没关系,反正丢条无关痛痒的通知短信嘛(你敢说你没验证码短信丢失的情况?)。

还有很多公司的弱校验用token啊什么的,反正花样很多,但是重要的场景一定要强校验,真正查问题 的时候没有在磁盘持久化的数据,心里还是空空的,就像你和女朋友分开的时候的心里状态一样。

消息顺序消费

        

同个业务场景下不同几个操作的消息同时过去,本身顺序是对的,但是你发出去的时候同时发 出去了,消费的时候却乱掉了,这样就有问题了。

我之前做电商活动也是有这样的例子,我们都知道数据量大的时候数据同步压力还是很大的,有时候数 据量大的表需要同步几个亿的数据。(并不是主从同步,主从延迟大的话会有问题,可能是从数据库或 者主数据库同步到备库)

这种情况我们都是怼到队列里面去,然后慢慢消费的,那问题就来了呀,我们在数据库同时对一个Id的 数据进行了增、改、删三个操作,但是你消息发过去消费的时候变成了改,删、增,这样数据就不对 了。

本来一条数据应该删掉了,结果在你那却还在,这不是出大问题!两者的结果是不是完全不一样了

解决办法:

生产者消费者一般需要保证顺序消息的话,可能就是一个业务场景下的,比如订单的创建、支付、发
货、收货。
那这些东⻄是不是一个订单号呢?一个订单的肯定是一个订单号的说,那简单了呀。

一个topic下有多个队列,为了保证发送有序,RocketMQ提供了MessageQueueSelector队列选择机 制,他有三种实现:

我们可使用Hash取模法,让同一个订单发送到同一个队列中,再使用同步发送,只有同个订单的创建 消息发送成功,再发送支付消息。这样,我们保证了发送有序。

RocketMQ的topic内的队列机制,可以保证存储满足FIFO(First Input First Output 简单说就是指先进先 出),剩下的只需要消费者顺序消费即可。

RocketMQ仅保证顺序发送,顺序消费由消费者业务保证!!! 这里很好理解,一个订单你发送的时候放到一个队列里面去,你同一个的订单号Hash一下是不是还是一样的结果,那肯定是一个消费者消费,那顺序是不是就保证了?一个队列有序出去,一个消费者消费不就好了,我想说 的是消费者是多线程的,你消息是有序的给他的,你能保证他是有序的处理的?还是一个消费成功了再 发下一个稳妥。

分布式事务:

        下单流程可能涉及到10多个环节,你下单付钱都成功了,但是你优惠券扣减失败 了,积分新增失败了,前者公司会被薅羊毛,后者用户会不开心,但是这些都在不同的服务怎么保证大 家都成功呢?

我接触和了解到的分布式事务大概分为:

2pc(两段式提交)

3pc(三段式提交)

TCC(Try、Confirm、Cancel)

最大努力通知

XA
本地消息表(ebay研发出的)

半消息/最终一致性(RocketMQ)

2pc(两段式提交)

弊端:
例如⻓时间锁定数据库资源,导致系统的响应不快,并发上不去。 网络抖动出现脑裂情况,导致事物参与者,不能很好地执行协调者的指令,导致数据不一致。

单点故障:例如事物协调者,在某一时刻宕机,虽然可以通过选举机制产生新的Leader,但是这过程 中,必然出现问题,而TCC,只有强悍的技术团队,才能支持开发,成本太高。

2pc(两段式提交)可以说是分布式事务的最开始的样子了,像极了媒婆,就是通过消息中间件协调多 个系统,在两个系统操作事务的时候都锁定资源但是不提交事务,等两者都准备好了,告诉消息中间 件,然后再分别提交事务。

但是我不知道大家看到问题所在没有?
是的你可能已经发现了,如果A系统事务提交成功了,但是B系统在提交的时候网络波动或者各种原因提 交失败了,其实还是会失败的。

最终一致性

整个流程中,我们能保证是:
  业务主动方本地事务提交失败,业务被动方不会收到消息的投递。
  只要业务主动方本地事务执行成功,那么消息服务一定会投递消息给下游的业务被动方,并最终保
  证业务被动方一定能成功消费该消息(消费成功或失败,即最终一定会有一个最终态)。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值