一、RabbitMQ与springboot:Springamqp
1.引入依赖
<!--SpringAMQP,内含rabbitMQ--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
2.在publisher中编写测试方法,向simple.queue发送消息
二、工作模型:fanout、direct、topic
Exchange交换机:
接收publisher发送的消息
将消息按照规则路由到与之绑定的队列
不能缓存消息、路由失败,消息丢失
FanoutExchange会将消息路由到所有与之绑定的队列
1.SpringAMQP-FanoutExchange
(1)在consumer中常见的一个类,添加@Configuration注解,并且声明FanoutExchange、Queue和绑定关系对象Binding,代码如下
@Configuration
public class FanoutConfig {
//声明交换机fanoutExchange
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("hwt.fanout");
}
//声明第一个队列1
@Bean
public Queue fanoutQueue1(){
return new Queue("hwt.queue1");
}
//声明第二个队列2
@Bean
public Queue fanoutQueue2(){
return new Queue("hwt.queue2");
}
//将交换机和队列1进行绑定
@Bean
public Binding bindingQueue1(Queue fanoutQueue1,FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}
//将交换机和队列2进行绑定
@Bean
public Binding bindingQueue2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
}
(2)在consumer中写一个listener进行监听定义的交换机绑定的队列
@Component
public class SpringRabbitListener {
/*
Fanout交换机,会将消息发送到每个与它绑定的队列queue
*/
@RabbitListener(queues = "hwt.queue1")
public void listenFanoutQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到消息【"+msg+"】"+ LocalTime.now());
Thread.sleep(200);
}
@RabbitListener(queues = "hwt.queue2")
public void listenFanoutQueue2(String msg) throws InterruptedException {
System.out.println("消费者2接收到消息【"+msg+"】"+ LocalTime.now());
Thread.sleep(200);
}
}
(3)编写测试类进行测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
//fanout模式测试
@Test
public void testFanoutQueue(){
//交换机
String exchange = "hwt.fanout";
//发送信息
String message = "hello:everyone";
//参数:交换机、rountingKey、信息
rabbitTemplate.convertAndSend(exchange,"",message);
}
}
(4)启动consumer的应用,登录rabbitMQ可以看到所定义的交换机(hwt.fanout)以及进行绑定的queue
(5)执行测试,消息消费之后会在rabbitMQ中自动删除
2.SpringAMQP-DirectExchange
发布订阅-DirectExchange
direct交换机会将publisher发送的消息接收到后,根据规则路由到指定的队列Queue,因此也成为路由模式(route)。
Queue都与Exchange设置一个BindingKey
发布者发送消息时,指定消息的RoutingKey
DirectExhcange将消息路由到BindingKey与消息的RountingKey匹配一致的队列Queue。
(1)在consumer服务中声明Exchange、Queue
@Component
public class SpringRabbitListener {
//绑定Queue、exchange、routingKey
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "hwt.direct",type = ExchangeTypes.DIRECT),
key = {"blue","red"}
))
public void listenDirectQueue1(String msg){
System.out.println("DirectQueue1接收到消息:"+msg+" time:"+LocalTime.now());
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue("direct.queue2"),
exchange = @Exchange(name = "hwt.direct",type = ExchangeTypes.DIRECT),
key = {"red","yellow"}
))
public void listenDirectQueue2(String msg){
System.out.println("DirectQueue2接收到消息:"+msg+" time:"+LocalTime.now());
}
}
(2)启动consumer所在的服务,可以看到Exchange以及绑定的Queue以及RoutingKey
(3)编写测试类,指定rountingKey
@Test
public void testDirectExchange(){
//direct交换机
String exchange = "hwt.direct";
//发送信息
String message = "direct:hello,everyone";
rabbitTemplate.convertAndSend(exchange,"yellow",message);
}
DirectExchange与FanoutExchange的区别:
FanoutExcahnge将消息路由到每一个与它绑定的队列
DirectExchange会根据RoutingKey判断路由给哪个队列
如果多个队列具有相同的RoutingKey,则与FanoutExchange相同
3.SpringAMQP-TopicExchange
TopicExchange与DirectExchange类似,区别在于RountingKey必须是多个单词的列表,用"."分割
Queue与Exchange设置的BindingKey可以使用通配符:
#:代指0个或者多个单词
*:代指一个单词
(1)在consumer中声明Exchange、Queue、RoutingKey
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.queue1"),
exchange = @Exchange(value = "hwt.topic",type = ExchangeTypes.TOPIC),
key = {"china.#"}
))
public void listenTopicQueue1(String msg){
System.out.println("TopicQueue1接收到消息:"+msg+" time:"+LocalTime.now());
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue("topic.queue2"),
exchange = @Exchange(value = "hwt.topic",type = ExchangeTypes.TOPIC),
key = {"#.news"}
))
public void listenTopicQueue2(String msg){
System.out.println("TopicQueue2接收到消息:"+msg+" time:"+LocalTime.now());
}
(2)启动consumer所在的服务,可以看到Exchange以及绑定的Queue以及RoutingKey
(3)编写测试类,指定rountingKey
@Test
public void testTopicExchange(){
String exchange = "hwt.topic";
String message = "topic:hello,china";
rabbitTemplate.convertAndSend(exchange,"china.weather",message);
}
三、消息转换器
Spring对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的,而默认实现是SimpleMessageConverter,基于JDK的ObjectOutputStream完成序列化。
如果要修改只需要定义一个MessageConverter类型的Bean即可,例如Json方式序列化
(1)在publisher中引入依赖
<!-- jackson序列化-->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
(2)在publisher中声明MessageConverter
@SpringBootApplication
public class CustomApplication {
public static void main(String[] args) {
SpringApplication.run(CustomApplication.class,args);
}
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
}
(3)然后定义一个消费者,监听object.queue的队列并消费消息
@RabbitListener(queues = "object.queue")
public void listenObjectQueue(Map<String,Object> msg){
System.out.println("收到消息: ["+msg+"]");
}
(4)编写测试
@Test
public void testObjectQueue(){
Map<String,Object> message = new HashMap<>();
message.put("name","吴一凡");
message.put("work","缝纫机员工");
rabbitTemplate.convertAndSend("object.queue",message);
}
四、MQ常见问题
消息可靠性问题:
如何确保发送的消息至少被消费一次
消息延迟问题:
如何实现消息的延迟投递
消息堆积问题:
如何解决数百万消息堆积,无法及时消费问题
高可用:
如何避免单点的MQ故障而导致的不可用问题
1.消息可靠性问题
消息从生产者发送到exchange,再到queue,再到消费者,有哪些导致消息丢失的可靠性?
> 发送时丢失
生产者publisher发送的消息未到达交换机exchange
生产者到达exchange但未到达queue
> MQ宕机,queue将消息丢失
> consumer接收到消息但未消费就宕机了
生产者确认机制
RabbitMQ提供了publisher confirm机制来避免消息发送到MQ的过程中丢失。消息发送到MQ后,会返回一个结果给发送者,表示消费是否处理成功,结果有两种请求:
> publisher confirm 发送者确认
消息成功投递到交换机,返回ack
消息未投递到交换机,返回nack
> publisher return,发送者回执
消息投递到交换机了,但是没有路由到队列。发回ack及路由失败原因。
注意:消息确认机制发送消息时,需要给每一个消息设置一个全局唯一id,以区分不同消息,避免ack冲突。
(1)在publisher应用的application.yml中添加配置
spring:
rabbitmq:
publisher-confirm-type: correlated #异步回调 confirmCallback,同步调用 simple
publisher-returns: true #开启publish-return功能,基于callback机制,需要定义ReturnCallback
template:
mandatory: true #开启消息路由失败时的策略,调用ReturnCallback,false:直接丢弃消息
(2)配置ReturnCallback
/*
生产者确认:开启publish-return功能,每个RabbitTemplate只能配置一个ReturnCallback,因此需要在项目启动过程中配置
*/
@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
@Override
@Deprecated
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取RabbitTemplate
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
//设置ReturnCallback
rabbitTemplate.setReturnCallback((message, replayCode, replayTest, exchange, routingKey) -> {
log.info("消息发送失败:应答码{},原因{},交换机{},路由键{},消息{}",
replayCode,replayTest,exchange,routingKey,message.toString());
});
}
}
(3)发送消息,指定消息ID,消息ConfirmCallback
//发送消息,指定消息ID,消息confirmCallback
@Test
public void TestConfirmCallback(){
//1.准备消息
String message = "生产者确认机制:I am coming";
//2.准备CorrelationData
CorrelationData correlationData = new CorrelationData();
//2.1消息ID
correlationData.setId(UUID.randomUUID().toString());
//2.2准备ConfirmCallback
correlationData.getFuture().addCallback(result -> {
if (result.isAck()) {
//消息成功ACK
log.debug("消息发送成功,ID{}", correlationData.getId());
} else {
//消息失败NACK
log.error("消息发送失败,ID{}", correlationData.getId(),result.getReason());
}
}, ex -> log.error("消息发送异常,ID{}",correlationData.getId(),ex.getMessage()));
//3.发送消息
rabbitTemplate.convertAndSend("amq.topic","callback.test",message,correlationData);
}
消息持久化
MQ默认是内存存储模式,开启持久化功能可以确保缓存在MQ中的消息不丢失
(1)消息持久化
/*
交换机持久化 (默认持久)
*/
@Bean
public DirectExchange durableExchange(){
//三个参数:1.交换机名称,2.是否持久化,3.当没有Queue绑定时是否自动删除
return new DirectExchange("durable.Exchange",true,false);
}
(2)队列持久化
/*
队列持久化 (默认持久)
*/
@Bean
public Queue simpleQueue(){
//使用QueueBuilder构建队列,durable是持久化的
return QueueBuilder.durable("durable.queue").build();
}
(3)消息持久化,SpringAMQP中的消息默认是持久的,可以通过MessageProperties中的DeliveryMode来指定
//1.准备消息
String msg= "durable.message:I am coming";
Message message = MessageBuilder
.withBody(msg.getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();
消费者确认机制
RabbitMQ支持消费者确认机制,即消费者收到消息后可以向MQ发送回执ack,MQ收到回执ack后才删除该消息,而SpringAMQ允许配置三种确认模式
manual:手动ack,需要在处理业务逻辑之后,手动调用api发送ack。
auto:自动ack,由spring监测Listener代码是否出现异常,无异常返回ack,抛出异常返回nack。
none:关闭ack,MQ假定消费者收到消息后会成功处理,因此消息投递后立即删除
配置修改application.yml即可:
spring:
rabbitmq:
listener:
simple:
prefetch: 1
acknowledge-mode: auto #自动ack,none,关闭ack,#manual,手动ack
消费者失败重试机制
当消费者出现异常后,消息会重新入队requeue,再重新发送到消费者,然后再次异常,再次requeue,无限循环,导致MQ消息处理飙升,带来不必要的压力。
我们可以利用spring中的retry机制,在消费者出现异常后在本地重试,而不是无限制的requeue到MQ队列
配置如下:
spring:
rabbitmq:
listener:
simple:
prefetch: 1
retry:
enabled: true #开启消费者失败重试
initial-interval: 1000 #初始的重试等待时长为1秒
multiplier: 3 #下次重试的的等待时长倍数,下次等待时长 = multiplier * initial-interval
max-attempts: 3 #最大重试次数
stateless: true #无状态,如果业务中包含事务,改为false,有状态
消费者失败消息处理策略
在开启重试模式之后,重试次数耗尽,如果消息依然失败,则需要有MessageRecoverer接口处理,它包含三种不同的实现:
> RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息(默认)。
> ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队。
> RepublishMessageRecoverer:重试耗尽后,将失败消息投递至指定的交换机。
RepublishMessageRecoverer处理策略示例:
(1)在consumer中常见的一个类,添加@Configuration注解,声明异常消息的处理类
@Configuration
public class ErrorMessageConfig {
//异常交换机
@Bean
public DirectExchange errorMessageExchange(){
return new DirectExchange("error.direct");
}
//异常交换机需要绑定的队列
@Bean
public Queue errorQueue(){
return new Queue("error.queue");
}
//交换机与队列绑定
@Bean
public Binding errorMessageBinging(){
return BindingBuilder.bind(errorQueue()).to(errorMessageExchange()).with("error");
}
//失败消息重新投至交换机,因为交换机只能转发消息,不能存储消息,所以前面声明了异常队列error.queue,由队列进行存储
@Bean
public MessageRecoverer republishMessageRecover(RabbitTemplate rabbitTemplate){
return new RepublishMessageRecoverer(rabbitTemplate,"error.direct","error");
}
}
(2)启动consumer应用,可以看到异常处理交换机以及绑定的队列
(3)异常处理策略测试
@RabbitListener(queues = "simple.queue")
public void listenSimpleQueue(String msg){
log.debug("消费者接收到durable.queue的消息:【"+msg+"】");
System.out.println(1/0);
log.info("消费者处理消息成功");
}
发送消息
查看控制台
查看error.queue队列
消息可靠性总结:
如何确保消息可靠性:
(1).开启生产者确认机制,确保生产者的消息可以到达队列
(2).开启持久化功能,确保消息未消费前在队列不会消失
(3).开启消费者确认机制auto,由spring确认消息处理完成后返回ack
(4).开启消费者失败重试机制,并且设置Messagerecoverer,多次重试失败后,将消息投递至异常交换机,交由人工处理
2.消息延迟性问题
当队列中消息满足以下的情况之一,就会成为死信(dead letter)。
> 消费者使用basic.reject或者basic.nack声明消费失败,并且消息的requeue参数设置为false。
> 消息是一个过期消息,超时无人消费。
> 要投递的队列消息堆满了,最早的消息可能成为死信。
如果该队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到
这个交换机,而这个交换机就被称为死信交换机(Dead Letter Exchange 简称:DLX)。
(1)声明死信交换机和队列
/*
TTL time-to-live
监听延迟i消息(死信)
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "dl.queue",durable = "true"),
exchange = @Exchange(name = "dl.direct"),
key = "ddl"
))
public void listenerDlQueue(String msg){
log.info("接收到dl.queue的延迟消息:{}",msg);
}
(2)给队列设置超时时间,并给队列指定私信交换机
@Configuration
public class TTLMessageConfig {
@Bean
public DirectExchange ttlExchange(){
return new DirectExchange("ttl.direct");
}
@Bean
public Queue ttlQueue(){
return QueueBuilder
.durable("ttl.queue") //指定队列名称以及持久化
.ttl(10000) //队列超时时间,10秒
.deadLetterExchange("dl.direct") //配置死信交换机
.deadLetterRoutingKey("ddl") //指定死信 RoutingKey
.build();
}
@Bean
public Binding simpleBinding(){
return BindingBuilder.bind(ttlQueue()).to(ttlExchange()).with("ttl");
}
}
(3)发送消息时,也可以给消息设置超时时间
@Test
public void testTTLMessage(){
//创建消息
Message message = MessageBuilder.withBody("ttl message".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.setExpiration("5000")
.build();
//消息Id,需要封装到CorrelationData
CorrelationData correlationData = new CorrelationData();
correlationData.setId(UUID.randomUUID().toString());
//发送消息
rabbitTemplate.convertAndSend("ttl.direct","ttl",message);
log.info("消息发送成功");
}
延迟队列
利用TTL,结合死信交换机,我们实现了消息发出后,消费者延迟收到的效果,这种消息模式就称为延迟模式(Delay Queue)。
延迟队列使用场景包括:
> 延迟发送短信
> 用户下单,15min内未支付,自动取消订单。
> 预约工作会议,20min后自动通知所有参会人员
(1)安装DelayExchange插件
下载的插件放到plugins目录下后
执行:rabbitmq-plugins enable rabbitmq_delayed_message_exchange
(2)声明延迟交换机
可以使用RabbitMQ管理平台声明:
DelayExchange交换机本质还是官方的三种交换机,只是增加了延迟功能。因此使用时,只需要声明一个交换机,交换机的类型可以是任何类型,然后设定delayed属性为true即可。
基于注解方式
/*
延迟队列 DelayExchange
*/
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "delay.queue",durable = "true"),
exchange = @Exchange(name = "delay.direct",delayed = "true"),
key = "delay"
))
public void listenerDelayQueue(String msg){
log.info("接收到delay.queue的延迟消息:{}",msg);
}
基于代码方式
@Bean
public DirectExchange delayExchange(){
return ExchangeBuilder
.directExchange("delay.direct")//指定交换机类和名称
.delayed()//设置delayed属性为true
.durable(true)//持久化
.build();
}
@Bean
public Queue delayQueue(){
return new Queue("delay.queue");
}
@Bean
public Binding delayBinding(){
return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay");
}
然后我们向delayed为true的交换机发送消息,一定要给消息添加一个head:x-delay,值为延迟的时间,单位为毫秒
/*
延迟消息测试
*/
@Test
public void testDelayMessage(){
Message message = MessageBuilder
.withBody("hello,this is a delay message".getBytes(StandardCharsets.UTF_8))
.setHeader("x-delay", 10000)//延迟时间,毫秒
.build();
CorrelationData correlationData = new CorrelationData();
correlationData.setId(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("delay.direct","delay",message,correlationData);
log.debug("发送消息成功");
}
忽略延迟消息的错误提示
@Slf4j
@Configuration
public class CommonConfig implements ApplicationContextAware {
@Override
@Deprecated
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取RabbitTemplate
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
//设置ReturnCallback
rabbitTemplate.setReturnCallback((message, replayCode, replayTest, exchange, routingKey) -> {
//判断是否是延迟消息
if(message.getMessageProperties().getReceivedDelay()>0){
//是延迟消息,忽略这个提示
return;
}
log.info("消息发送失败:应答码{},原因{},交换机{},路由键{},消息{}",
replayCode,replayTest,exchange,routingKey,message.toString());
});
}
}
2.消息堆积问题
当生产者发送消息的速度大于消费者处理消息的速度,就会导致队列中的消息堆积,直到队列存储达到上限。最早收到的消息就会成为死信,会被丢弃,这就是消息堆积问题。
解决消息堆积有三种思路:
(1)增加更多消费者,提高消费速度。
(2)在消费者内开启线程池增加消息处理速度。
(3)扩大队列容积,提高堆积上线。
惰性队列
RabbitMQ从3.6起,就增加了Lazy Queues的概念,即惰性队列。
惰性队列有如下特征:
(1)接收到消息后直接存入磁盘而不是内存
(2)消费者要消费时,才会从磁盘中读取加入到内存
(3)支持数百万条数据存储
需要设置一个队列为惰性队列,只需要在声明队列时,指定x-queue-mode属性为lazy即可。可以通过命令将一个运行中的队列修改为惰性队列。
rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"lazy"}' -apply-to queues
(1)用SpringAMQP声明惰性队列分两种方式
> Bean方式
/*
惰性队列
*/
@Bean
public Queue lazyQueue(){
return QueueBuilder
.durable("lazy.queue")
.lazy()
.build();
}
> 注解方式
/*
注解声明:惰性队列
*/
@RabbitListener(queuesToDeclare = @Queue(
name = "lazy.queue",
durable = "true",
arguments = @Argument(name = "x-queue-mode",value = "lazy")
))
public void listenLazyQueue(String msg){
log.info("接收到惰性队列消息:{}",msg);
}
(2)进行惰性队列测试
//测试惰性队列,发送10000条消息
@Test
public void testLazyQueue(){
Message message = MessageBuilder
.withBody("hello,this is a message belong to lazy.queue".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();
for (int i =0;i<10000;i++){
rabbitTemplate.convertAndSend("lazy.queue",message);
}
}
//测试正常队列,发送10000条消息
@Test
public void testNormalQueue(){
Message message = MessageBuilder
.withBody("hello,this is a message belong to normal.queue".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.PERSISTENT)
.build();
for (int i =0;i<10000;i++){
rabbitTemplate.convertAndSend("normal.queue",message);
}
}
(2)查看MQ页面
惰性队列测试结果:
正常队列测试结果:
消息堆积问题解决方案:
队列上绑定多个消费者,提高消费速度。
给消费者开启线程池,提高消费速度
使用惰性队列,可以在MQ中存储更多消息
惰性队列的优点:
基于磁盘存储,消息上限高
没有间歇性的page-out,性能比较稳定
惰性队列的缺点:
基于磁盘存储,消息时效性会降低
性能受限于磁盘IO
五、RabbitMQ集群
RabbbitMQ是基于Erlang语言编写,而Erlang语言又是一个面向并发的语言,天然支持集群方式,RabbitMQ有两种集群模式:
普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。
镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。
镜像集群虽然支持主从,但主从同步并不是强一致的,某些情况下可能导致数据丢失的风险,因此RabbitMQ在3.8版本以后,退出了新功能:仲裁队列,来代替镜像集群,底层采用Raft协议确保主从数据的一致性。
1.普通集群
普通集群,也叫标准集群(classic cluster)具有以下特征:
(1)会在集群的各节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。
(2)当访问集群节点时,如果队列不在该节点,将会从数据所在节点传递到当前节点把那个返回。
(3)队列所在节点宕机,队列中的消息就会丢失
需要修改配置config,集群中的各节点配置文件相同,然后共享cookie(待补充)
2.镜像集群
镜像集群,本身是主从模式,具有以下特征:
(1)交换机,队列、队列中的消息会在镜像集群的各个节点间同步备份。
(2)创建备份的节点被称为该队列的主节点,备份到的其他节点被称为该队列的镜像节点。
(3)一个队列的主节点,可能是另一个队列的镜像节点。
(4)所有操作都是主节点完成,然后同步到镜像节点。
(5)主节点宕机后,镜像节点会成为新的主。
镜像模式的的配置有三种模式:
Exactly模式:
2.仲裁队列
仲裁队列,仲裁队列是3.8版本以后才有的功能,用来代替镜像队列,具备下列特征。
(1)与镜像队列一样都是主从模式,支持主从数据同步。
(2)使用非常简单,没有复杂的配置。
(3)主从同步基于Raft协议,强一致。
1.创建仲裁队列的两种方式
(1)在任意控制台添加一个队列,一定要选择队列类型为Quorum类型。
(2)SpringAMQP创建仲裁队列
@Configuration
public class QuorumConfig {
@Bean
public Queue quorumQueue(){
return QueueBuilder.durable("quorum.queue")//持久化
.quorum()//仲裁队列
.build();
}
}
2.SpringAMQP连接集群,只需要在yaml中配置即可