rabbitmq主要有三大类交换机:fanout,direct,topic,他们从名字上分别是扇区交换机、直连交换机和主题交换机。其实还有headers一类的交换机,这里不去深究。
fanout交换机也叫无路由交换机,就是它直接与交换机exchange发生关联,不用routingKey。
direct和topic都加入了routingKey的概念,发送数据的时候,它只跟交换机和路由发生关系,不关心最终的队列queue。就是生产者这里只需要将消息绑定到exchange和routingKey上,如果是springboot与rabbitmq关联,我们可以从api上来看看:
@Override
public void convertAndSend(String exchange, String routingKey, final Object object) throws AmqpException {
convertAndSend(exchange, routingKey, object, (CorrelationData) null);
}
这是一个很直观的方法,方法体中跟rabbitmq有关的只是exchange,routingKey,object。
这里也引出了本文的主要内容:direct类型和topic类型到底有什么区别呢?区别就是:direct类型从字面上看就是直连的意思,从生产者到消费者这条线上,routingKey只能一一对应,而topic主题类型生产者到消费者routingKey可以多对一。可以用如下直观的图形表示:
如果这段描述还不是很清晰,请看下面一个示例:
springboot整合rabbitmq
maven依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- tool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
rabbitmq连接配置信息:
spring:
rabbitmq:
addresses: 192.168.226.100:5672
username: huali
password: huali
virtual-host: /mec
publisher-confirms: true
connectionTimeout: 5000
template:
madatory: true
listener:
simple:
acknowledge-mode: manual
concurrency: 1
max-concurrency: 10
retry:
enabled: true
RabbitTemplate配置
package com.huali.mec.parse.config;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AMQPConfig {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
return template;
}
}
监听器,这里是消费者:
package com.huali.mec.parse.listener;
import java.io.IOException;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
@Component
public class UpMessageListener {
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue1",durable = "true"),
exchange = @Exchange(value="MEC1",type = ExchangeTypes.TOPIC),
key = "test.#")})
public void topicHandler(Message message,Channel channel) throws IOException{
System.out.println("receive topic message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue2",durable = "true"),
exchange = @Exchange(value="MEC2",type = ExchangeTypes.DIRECT),
key = "test.aaa")})
public void directHandler(Message message,Channel channel) throws IOException{
System.out.println("receive direct message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
/*
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue2",durable = "true"),
exchange = @Exchange(value="MEC2",type = ExchangeTypes.DIRECT),
key = "test.bbb")})
public void directHandler2(Message message,Channel channel) throws IOException{
System.out.println("receive direct message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}*/
}
这里是生产者,注释的部分是用来发送direct类型消息的,这样可以看清楚他们的区别。
package com.huali.mec.parse.service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PublishService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
rabbitTemplate.convertAndSend("MEC1", "test.aaa", msg);
rabbitTemplate.convertAndSend("MEC1", "test.bbb", msg);
//rabbitTemplate.convertAndSend("MEC2", "test.aaa", msg);
//rabbitTemplate.convertAndSend("MEC2", "test.bbb", msg);
}
}
启动类,启动的时候循环发送10次消息。
package com.huali.mec.parse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.huali.mec.parse.service.PublishService;
@SpringBootApplication
public class ParseApp implements CommandLineRunner{
public static void main( String[] args ){
SpringApplication.run(ParseApp.class, args);
}
@Autowired
private PublishService publishService;
@Override
public void run(String... args) throws Exception {
for(int i=0;i<10;i++) {
publishService.send("hello,"+i);
}
}
}
运行这段示例,只需要准备好一个rabbitmq服务器,设定好本文所需vhost即可。队列和交换机都不用创建并绑定,程序启动会自动创建并绑定路由关系。
这里运行分三步:
1、直接运行本示例代码,可以查看topic交换机的输出:控制台打印20次消息,因为消费者绑定了routingKey是test.#就是会消费来自test.aaa和test.bbb的所有消息。
receive topic message -> hello,0
receive topic message -> hello,0
receive topic message -> hello,1
receive topic message -> hello,1
receive topic message -> hello,2
receive topic message -> hello,2
receive topic message -> hello,4
receive topic message -> hello,3
receive topic message -> hello,3
receive topic message -> hello,4
receive topic message -> hello,5
receive topic message -> hello,5
receive topic message -> hello,6
receive topic message -> hello,6
receive topic message -> hello,7
receive topic message -> hello,7
receive topic message -> hello,8
receive topic message -> hello,8
receive topic message -> hello,9
receive topic message -> hello,9
2、将PublishService中发送topic交换机的代码注释,打开发送direct交换机的代码,如下所示:
@Component
public class PublishService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
//rabbitTemplate.convertAndSend("MEC1", "test.aaa", msg);
//rabbitTemplate.convertAndSend("MEC1", "test.bbb", msg);
rabbitTemplate.convertAndSend("MEC2", "test.aaa", msg);
rabbitTemplate.convertAndSend("MEC2", "test.bbb", msg);
}
}
同样运行启动程序,打印如下所示:
receive direct message -> hello,0
receive direct message -> hello,1
receive direct message -> hello,2
receive direct message -> hello,3
receive direct message -> hello,4
receive direct message -> hello,5
receive direct message -> hello,7
receive direct message -> hello,6
receive direct message -> hello,8
receive direct message -> hello,9
虽然发送了两组routingKey=test.aaa和routingKey=test.bbb的消息,但是只打印了test.aaa这个routingKey的消息。因为我们的监听器那里只绑定了一次routingKey。
3、将代码UpMessageListener.java中的另一个绑定routingKey的代码注释打开,运行:
public class UpMessageListener {
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue1",durable = "true"),
exchange = @Exchange(value="MEC1",type = ExchangeTypes.TOPIC),
key = "test.#")})
public void topicHandler(Message message,Channel channel) throws IOException{
System.out.println("receive topic message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue2",durable = "true"),
exchange = @Exchange(value="MEC2",type = ExchangeTypes.DIRECT),
key = "test.aaa")})
public void directHandler(Message message,Channel channel) throws IOException{
System.out.println("receive direct message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
@RabbitListener(bindings = {@QueueBinding
(value = @Queue(value = "queue2",durable = "true"),
exchange = @Exchange(value="MEC2",type = ExchangeTypes.DIRECT),
key = "test.bbb")})
public void directHandler2(Message message,Channel channel) throws IOException{
System.out.println("receive direct message -> "+new String(message.getBody()));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
}
这次运行就打印了20次消息:
receive direct message -> hello,0
receive direct message -> hello,1
receive direct message -> hello,0
receive direct message -> hello,2
receive direct message -> hello,3
receive direct message -> hello,1
receive direct message -> hello,4
receive direct message -> hello,3
receive direct message -> hello,2
receive direct message -> hello,5
receive direct message -> hello,7
receive direct message -> hello,4
receive direct message -> hello,5
receive direct message -> hello,6
receive direct message -> hello,6
receive direct message -> hello,7
receive direct message -> hello,8
receive direct message -> hello,8
receive direct message -> hello,9
receive direct message -> hello,9
至此,示例结束,再回过头来想想direct和topic类型交换机的区别,其实在生产者端几乎完全没有区别,主要在于消费者端这里,消费者要去消费routingKey,direct只能一一对应消费单个的routingKey,而topic可以使用通配符*和#来匹配多个routingKey。至于他们绑定到queue上,是一个还是多个,他们之间又没有区别。
他们两者与fanout的区别是一个有routingKey,一个没有。