RabbitMQ消息中间件
消息队列在使用过程中,面临着很多实际问题需要思考:
消息从发送,到消费者接收,会经理多个过程:
其中的每一步都可能导致消息丢失,常见的丢失原因包括:
-
-
生产者发送的消息未送达exchange
-
消息到达exchange后未到达queue
-
-
MQ宕机,queue将消息丢失
-
针对这些问题,RabbitMQ分别给出了解决方案:
-
生产者确认机制
-
mq持久化
-
消费者确认机制
-
失败重试机制
RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程中丢失。消息发送到MQ以后,会返回一个结果给发送者,表示消息是否处理成功。结果有两种请求:
publisher-confirm,发送者确认
消息成功投递到交换机,返回ack
消息未投递到交换机,返回nack
publisher-return,发送者回执
消息投递到交换机了,但是没有路由到队列。返回ACK,及路由失败的原因。
首先,修改publisher服务中的application.yml文件,添加下面的内容:
spring:
rabbitmq:
publisher-confirm-type: correlated
publisher-returns: true
template:
mandatory: true
说明:
- `publish-confirm-type`:开启publisher-confirm,这里支持两种类型:
- `simple`:同步等待confirm结果,直到超时
- `correlated`:异步回调,定义ConfirmCallback,MQ返回结果时会回调这个ConfirmCallback
- `publish-returns`:开启publish-return功能,同样是基于callback机制,不过是定义ReturnCallback
- `template.mandatory`:定义消息路由失败时的策略。true,则调用ReturnCallback;false:则直接丢弃消息
测试交换机和队列:首先建好交换机名字,比如:amq.topic,然后再按下图操作点进去。
在弹出的页面里面绑定队列名称,如下图:其中simple.# 代表以simple开头的队列都会匹配到。
看生产者代码发布消息的时候是怎么操作的:
但是我们发送消息的时候是需要确认机制的,因此我们还要有一个参数,作为消息的确认机制,这个参数就是:异步回调,这里我们以“correlationData”来命名,
correlationData这个对象获取future对象添加callback,callback里面有两个回调函数,一个是成功的回调,一个是失败的回调,如下图:
由于它们都是接口里面的方法,因此可以采用匿名内部类的方式实现,同时这些接口也是函数式接口,因此可以采用lambda表达式简化。接着定义confirm机制和失败处理机制
测试该接口,控制台如下输出:
打开交换机监控页面:点击交换机,点开amq.topic
重新绑定一次,但是如果再绑定一次,其实它会报错,会提示access refused,这种情况说明是该用户下没有绑定该虚拟主机上的队列的权限,我们需要将虚拟主机添加给该用户:
接下来我们看效果:
所以当我们操作没有虚拟机所在权限的交换机或者队列时,就会报错误,比如我们将默认虚拟主机/这个给撤销了:我们把上图的再去掉clear就可以了,然后回到交换机页面,点击/所在的某个交换机名称,然后给它分配队列,我们会看到如下图:
现在如果我们想恢复,直接将刚才clear那个再加回来就好了。接下来我们重新绑定一次就不会报权限拒绝的错误了。
那为什么idea控制台还报错呢?
那是因为我们上面所演示的都是qw这个用户的,然后我们看下idea里面yam文件实际配置:
那其实我在rabbit网页端分配的是这个:分配的是下图的最后一个,也就是说用下面这个amq.topic交换机绑定队列的话其实跟没设置一样,因为yam配置的是/,而不是默认虚拟主机这个的交换机(所以无论怎么绑定你会发现很奇怪,怎么明明绑定好了,控制台还路由不到),根据yaml配置文件,我们应该设置默认交换机这个,也即下图的第一个箭头标记的这个
我们点开这个amq.topic,绑定好队列和队列标识就好了:
再重新执行test测试下,控制台不报错了:
我们再测试下消息没有到达交换机的情况: 我们将交换机名称写错就可以测试了:
我们再测下将路由表示写错:
总结: