环境java11+SpringCloud+Springboot
spring:
#基础配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
#发送端配置
publisher-confirms: true
publisher-returns: true
template:
mandatory: false
#接收端配置
listener:
simple:
#手动应答
acknowledge-mode: manual
#每次接受一条
prefetch: 1
direct:
acknowledge-mode: manual
prefetch: 1
创建一些bean,类似的省略
@Autowired
private RabbitTemplate rabbitTemplate;
/*---------------------------注入6个队列--------------------*/
@Lazy
@Resource(name="queue1")
private Queue queue1;
/*---------------------------注入3种交换机--------------------*/
//无key
@Lazy
@Resource(name="fanoutExchange")
private FanoutExchange fanoutExchange;
//key模糊匹配
@Lazy
@Resource(name="topicExchange")
private TopicExchange topicExchange;
//key直接匹配
@Lazy
@Resource(name="directExchange")
private DirectExchange directExchange;
/*---------------------------bean的创建部分--------------------*/
/**
* 创建6个队列交给spring管理
*/
@Bean(autowire = Autowire.BY_NAME, name = "queue1")
private Queue queue1() {
// 第一个参数是队列名字, 第二个参数是指是否持久化
return new Queue(QUEUE1, false);
}
@Bean(autowire = Autowire.BY_NAME, name = "queue6")
private Queue queue6() {
return new Queue(QUEUE6, false);
}
//Topic交换器
@Bean(autowire = Autowire.BY_NAME, name = "topicExchange")
public TopicExchange topicExchange() {
return new TopicExchange(TOPIC_EXCHANGE);
}
//绑定,把队列交给交换器管理
@Bean(autowire = Autowire.BY_NAME, name = "bind1")
public Binding bind1() {
return BindingBuilder.bind(queue1).to(topicExchange).with("asdf.zs");
}
@Bean(autowire = Autowire.BY_NAME, name = "bind3")
public Binding topicBinding3() {
return BindingBuilder.bind(queue3).to(directExchange).with("hh.zs");
}
/**
* fanout交换机
*/
@Bean(autowire = Autowire.BY_NAME, name = "fanoutExchange")
public FanoutExchange fanoutExchange() {
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean(autowire = Autowire.BY_NAME, name = "bind4")
public Binding topicBinding4() {
return BindingBuilder.bind(queue4).to(fanoutExchange);
}
/**
* direct交换机
*/
@Bean(autowire = Autowire.BY_NAME, name = "directExchange")
public DirectExchange directExchange(){
return new DirectExchange(DIRECT_EXCHANGE);
}
Springboot启动时,对rabbitTemlate的配置
@PostConstruct
public void setApplicationContext(){
// 消息发送失败返回到队列中, yml需要配置 publisher-returns: true
rabbitTemplate.setMandatory(true);
// 消息返回, yml需要配置 publisher-returns: true
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
String correlationId = message.getMessageProperties().getCorrelationId();
System.out.println(correlationId);
});
// 消息确认, yml需要配置 publisher-confirms: true
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
System.out.println("消息发送到成功");
} else {
System.out.println("消息发送到exchange失败,原因: {}"+cause);
}
});
}
生产者和消费者一对一,一条消息被消费一次
生产者
/**
* 简单传输,单个生产者+单个队列+单个消费者
*/
public void simpleSend(String doc){
rabbitTemplate.convertAndSend(RabbitMQConfig.QUEUE1,doc);
}
消费者
@RabbitListener(queues = RabbitMQConfig.QUEUE1)
public void receiverQueue2(String json, Channel channel, Message message) {
try {
//告诉服务器收到这条消息已经被消费了
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
System.out.println("监听A"+json);
} catch (IOException e) {
e.printStackTrace();
System.out.println("receiver fail");
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
生产者和消费者一对多,
生产者一样,消费者有多个监听同一个队列,在接受端配置了每次从消息队列接收一个,处理完了响应,告诉队列可以消息已经被消费可以被销毁了,然后再接受下一个,多个消费者可以均匀分配,不会让一个一直工作另一个一直闲置。
用到FanoutExchange交换机,生产者和消费者一对多,一条消息被每个队列消费一次
/**
* 发布订阅
*/
public void sendFanoutExchangeQueue(String json) {
rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE,null,json);
}
我绑定4和5,所以这两个队列收到消息
@Bean(autowire = Autowire.BY_NAME, name = "bind4")
public Binding topicBinding4() {
return BindingBuilder.bind(queue4).to(fanoutExchange);
}
@Bean(autowire = Autowire.BY_NAME, name = "bind5")
public Binding topicBinding5() {
return BindingBuilder.bind(queue5).to(fanoutExchange);
}
用到DirectExchange交换机,生产者和消费者一对多,一条消息被指定每个队列消费一
次
/**
* 路由模式
*/
public void sendDirectExchangeQueue(String json) {
rabbitTemplate.convertAndSend(RabbitMQConfig.DIRECT_EXCHANGE,"ni",json);
}
发送的key必须和绑定的key完全一样才能匹配到
@Bean(autowire = Autowire.BY_NAME, name = "bind4")
public Binding topicBinding4() {
return BindingBuilder.bind(queue4).to(directExchange).with("hao");
}
@Bean(autowire = Autowire.BY_NAME, name = "bind5")
public Binding topicBinding5() {
return BindingBuilder.bind(queue5).to(directExchange).with("nihao");
}
@Bean(autowire = Autowire.BY_NAME, name = "bind6")
public Binding topicBinding6() {
return BindingBuilder.bind(queue6).to(directExchange).with("ni");
}
用到了TopicExchange交换机,生产者和消费者一对多,一条消息被符合规则的每个队列消费一次
先设置这三个绑定
//三个绑定,吧队列交给交换器管理
@Bean(autowire = Autowire.BY_NAME, name = "bind1")
public Binding bind1() {
return BindingBuilder.bind(queue1).to(topicExchange).with("*.*.zs");
}
@Bean(autowire = Autowire.BY_NAME, name = "bind2")
public Binding topicBinding2() {
return BindingBuilder.bind(queue2).to(topicExchange).with("as.*.*");
}
@Bean(autowire = Autowire.BY_NAME, name = "bind3")
public Binding topicBinding3() {
return BindingBuilder.bind(queue3).to(topicExchange).with("#.zs");
}
结果是三个都匹配到了,单词之间用.分割,*匹配一个单词,#匹配多个单词。
/**
* 话题
*/
public void sendTopicExchangeQueue(String json) {
rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE,"as.tt.zs",json);
}
在试三个不一样的
"as.*.zs"
"*.tt.*"
"#.*.zs"
"as.tt.zs"
和前面理解的一样,#支持0个,*可以在中间或两边
测试的时候每次启动生产者都会新建绑定,改了绑定的key再启动,旧的也没删除,影响测试结果,可以在rabbitMQ的界面点开相应的交换器查看,确认失败的消息再下次启动生产者会重新进入发送队列,配置了work模式,消费端没确认,队列会阻塞,因为work模式受到确认才给他下一个消息。