rabbitmq--发布-订阅模型

上两篇中我们介绍了rabbitmq中的收发模型和工作队列模型,它们都一个共同的特点——exchange是透明的,似乎不存在,收、发双方都是通过queue名称产生关联。

 

本篇中我们来着重介绍下rabbitmq中的exchange ,rabbitmq中涉及exchange(非透明)的收发模型我们称之为发布-订阅模型。在发布-订阅模型中,消息生产者和消费者不再是直接面对queue(队列名称),而是直面exchange,都需要经过exchange来进行消息的发送和接收。 

发布-订阅模型又可以分为以下几种:

fanout: 广播交换机,广播模型,不关心queue名称,不关心路由key

direct:  直接交换机,直接路由模型,根据路由key字符串精确匹配, 不关心queue名称,关心路由key

topic:主题交换机,主题路由模型,根据路由key字符串模糊(通配符)匹配, 不关心queue名称,关心路由key

headers:头交换机,头路由模型(使用较少),不关心queue名称,不关心路由key,关心消息头中key-value对。

 

本篇,我们来介绍下第一种发布订阅模型——fanout广播模型。

广播模型的特点是:所有发往同一个fanout交换机的消息都会被所有监听这个交换机的消费者接收到,而不关心具体的路由key。广播交换机就如同一个广播地址,只要有消费者监听这个广播地址,则一定都会收到来自这个广播地址收到的所有消息。如图:

示例代码如下 

日志生产者:

package com.tingcream.rabbitmq.broadcast;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class LogProducer {
	
	private static String EXCHANGE_NAME="logs";
	
	public static void main(String[] args) throws Exception {
		
		
		 ConnectionFactory factory = new ConnectionFactory();
         
			//主机  端口  vhost 用户名 密码
			factory.setHost("192.168.9.102");
			factory.setUsername("rabbitmq");
			factory.setPassword("rabbitmq123");
			factory.setPort(AMQP.PROTOCOL.PORT);
			factory.setVirtualHost("/");
         
        Connection connection=factory.newConnection();
        Channel channel=connection.createChannel();
        
        //    声明一个fanout 广播交换机,名称为logs  ,  声明的fanout队列     autoDelete为true, exclude 为true
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); //amq.gen-xxxxxxxxxx   AD EXCL      
       //  channel.exchangeDeclare(exchange, type, durable, autoDelete, arguments)
         
        for (int i=0;i<10;i++){
            String message="你好 World "+i;
             // channel.basicPublish(exchange, routingKey, props, body);
             //发布消息时指定routingKey为""
             channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
             System.out.println("LogProducer Send :" + message );
        }
        channel.close();
        connection.close();
    }

}

日志生产者代码中声明了一个名叫logs的fanout类型交换机,并使用这个交换机来发送消息,并且发送消息时设置路由key为"",因为在广播的场景中根本就不关心路由key。

日志消费者A:

package com.tingcream.rabbitmq.broadcast;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class LogReceiverA {
	private static    String EXCHANGE_NAME="logs";
	
	public static void main(String[] args) throws Exception {
		
		ConnectionFactory factory = new ConnectionFactory();
        
		//主机  端口  vhost 用户名 密码
		factory.setHost("192.168.9.102");
		factory.setUsername("rabbitmq");
		factory.setPassword("rabbitmq123");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setVirtualHost("/");
	    Connection connection=factory.newConnection();
	    Channel channel=connection.createChannel();
	    
	    //重要 与生产者使用同一个交换机
	    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
	    
	    //获取一个随机的队列名称   
	    String queueName = channel.queueDeclare().getQueue();
	    
	    //关联 exchange 和 queue ,因为是广播无需指定routekey,routingKey设置为空字符串
	   // channel.queueBind(queue, exchange, routingKey)
	    channel.queueBind(queueName, EXCHANGE_NAME, ""); 
	    System.out.println("LogReceiverA Waiting for messages");
	    
	    
	      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( "LogReceiverA接收到消息:" + message );   
	        }
	        
	      };
	      channel.basicConsume(queueName, true, consumer);
 
		
	}

}

日志消费者B:

package com.tingcream.rabbitmq.broadcast;

import java.io.IOException;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class LogReceiverB {
	private static    String EXCHANGE_NAME="logs";
	
	public static void main(String[] args) throws Exception {
		
		ConnectionFactory factory = new ConnectionFactory();
        
		//主机  端口  vhost 用户名 密码
		factory.setHost("192.168.9.102");
		factory.setUsername("rabbitmq");
		factory.setPassword("rabbitmq123");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setVirtualHost("/");
	    Connection connection=factory.newConnection();
	    Channel channel=connection.createChannel();
	    
	    //重要 与生产者使用同一个交换机
	    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
	    //获取一个随机的队列名称
	    String queueName = channel.queueDeclare().getQueue();
	    
	    //关联 exchange 和 queue ,因为是广播无需指定routekey,routingKey设置为空字符串
	   // channel.queueBind(queue, exchange, routingKey)
	     channel.queueBind(queueName, EXCHANGE_NAME, ""); 
	     System.out.println("LogReceiverB Waiting for messages");
	    
	      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( "LogReceiverA接收到消息:" + message );   
	        }
	        
	      };
	      //true 自动回复ack
	      channel.basicConsume(queueName, true, consumer);
 
		
	}

}

注意:

日志消费者A、B代码中使用了随机生成的queue名称(在本地随机产生一个名称),然后绑定这个queue名称到logs交换机上了。这是因为所有发布-订阅模型对queue名称都不关心,只要queue名称唯一、不重复即可。绑定queue到交换机是一种惯例的做法,实际上就是从交换机上取出一条消息并关联上一个指定的queue,然后我们就可以对这个queue进行监听等等,当然本例中queue的名称是随机生成。

 

先启动消费者A、B, 再启动生产者,运行下,登录管理后台。

登录管理后台后,我们发现exchange标签项中最后一行多出了一个名叫logs交换机,它的类型为fanout(广播),这就是我们代码中自定义的一个交换机。其上的7个交换机都是rabbitmq中内置的交换机。(最上面一个我们已经很熟悉了,收-发模型和工作队列模型就是采用的这种exchange)

 

queue标签项中多出了两条随机生成的队列名称。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值