Exchange
RabbitMQ的消息队列模型的核心概念是:生产者从不直接往队列里发送任何消息。实际上,多数情况下生产者甚至不知道消息是否会被发送到队列里。
与此相反,生产者只能将消息发送到一个交换器里。交换器做的事情很简单。一方面它接收生产者发送过来的消息,另一方面它将收到的消息推入队列里。交换器必须明确对于收到的消息它该怎么处理。这条消息是否应该附加到某个特定的队列后面?这条消息是否应该附加到多个队列后面?这条消息是否应该被丢弃?这些规则都由交换器类型(exchange type)来定义。
RabbitMQ常用的Exchange Type有fanout(广播交换器)、direct(直连交换器)、topic(主题交换器)、headers(头部交换器)这四种。
1.Publisher/Subscribe(发布/订阅) -fanout type
Sending messages to many consumers at once(同时向许多消费者发送消息)
一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息经过交换机,到达队列,实现一个消息被多个消费者获取的目的。需要注意的是,如果将消息发送到一个没有队列绑定的exchange上面,那么该消息将会丢失,这是因为在rabbitMQ中exchange不具备存储消息的能力,只有队列具备存储消息的能力。
//发布者
public class Publisher {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
String[] messages = new String[]{"First message.", "Second message..",
"Third message...", "Fourth message....", "Fifth message....."};
for(String message : messages){
//第二个参数为选择键
channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
System.out.println("[x] sent " + message);
}
channel.close();
connection.close();
}
}
//订阅者
public class Subscriber {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//声明一个队列,产生一个随机的队列名 如amq.gen-JzTY20BRgKO-HjmUJj0wLg
String queueName = channel.queueDeclare().getQueue();
System.out.println("queueName :" +queueName);
//第三个参数为绑定键, 将队列和交换机绑定
channel.queueBind(queueName,EXCHANGE_NAME,"");
System.out.println(" [*] waiting for messages.To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel){
//处理传送
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body,"UTF-8");
System.out.println(" [x] received " + message );
}
};
// 消费者监听队列
channel.basicConsume(queueName,true,consumer);
}
}
2.Routing(路由) - direct type
Receiving messages selectively (有选择地接收消息)
这种模式添加了一个路由键,生产者发布消息的时候添加路由键,消费者绑定队列到交换机时添加键值,这样就可以接收到需要接收的消息。与fanout type主要区别是交换机exchange和队列queue绑定的时候添加了路由键。
//发布者
public class RoutePublisher {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// direct类型
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
String[] messages = new String[]{"First message.", "Second message..",
"Third message...", "Fourth message....", "Fifth message....."};
String selectKey = "error";
for(String message : messages){
//第二个参数为路由键
channel.basicPublish(EXCHANGE_NAME,selectKey,null,message.getBytes());
System.out.println("[x] sent " + message);
}
channel.close();
connection.close();
}
}
// 订阅者
public class RouteSubscriber {
private static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
String queueName = channel.queueDeclare().getQueue();
System.out.println("queueName :" +queueName);
//第三个参数为绑定键,相同的selectKey 才能被接收
String selectKey = "error";
channel.queueBind(queueName,EXCHANGE_NAME,selectKey);
System.out.println(" [*] waiting for messages.To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel){
//处理传送
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body,"UTF-8");
System.out.println(" [x] received " + message );
}
};
channel.basicConsume(queueName,true,consumer);
}
}
3.Topics(通配) -topic type
Receiving messages based on a pattern (topics) 基于模式匹配
和路由匹配区别在于,路由键支持模糊匹配规则。符号“#”匹配一个或多个词,“*”仅匹配一个词
//生产者1
public class TopicPublisher {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// direct类型
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
String[] messages = new String[]{"First message.", "Second message..",
"Third message...", "Fourth message....", "Fifth message....."};
String selectKey = "item.insert";
for(String message : messages){
//第二个参数为路由键
channel.basicPublish(EXCHANGE_NAME,selectKey,null,message.getBytes());
System.out.println("[x] sent " + message);
}
String selectKey2 = "item.update";
for(String message : messages){
//第二个参数为路由键
channel.basicPublish(EXCHANGE_NAME,selectKey2,null,message.getBytes());
System.out.println("[x] sent " + message);
}
channel.close();
connection.close();
}
}
//消费者1
public class TopicSubscriber {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
String queueName = channel.queueDeclare().getQueue();
System.out.println("queueName :" +queueName);
//第三个参数为绑定键,相同的selectKey 才能被接收
String selectKey = "*.insert";
channel.queueBind(queueName,EXCHANGE_NAME,selectKey);
System.out.println(" [*] waiting for messages.To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel){
//处理传送
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body,"UTF-8");
System.out.println(" [x] received " + message );
}
};
channel.basicConsume(queueName,true,consumer);
}
}
//消费者2
public class TopicSubscriber2 {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.56.128");
factory.setUsername("admin");
factory.setPassword("123456");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
String queueName = channel.queueDeclare().getQueue();
System.out.println("queueName :" +queueName);
//第三个参数为绑定键,相同的selectKey 才能被接收
String selectKey = "*.update";
channel.queueBind(queueName,EXCHANGE_NAME,selectKey);
System.out.println(" [*] waiting for messages.To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel){
//处理传送
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body,"UTF-8");
System.out.println(" [x] received " + message );
}
};
channel.basicConsume(queueName,true,consumer);
}
}