RabbitMq发布/订阅

一个消息发给多个消费者,这种模式称之为发布/订阅(类似观察者模式)

为了验证这种模式,我们准备构建一个简单的日志系统。这个系统包含两类程序,一类程序发动日志,另一类程序接收和处理日志。在我们的日志系统中,每一个运行的接收者程序都会收到日志。然后我们实现,一个接收者将接收到的数据写到硬盘上,与此同时,另一个接收者把接收到的消息展现在屏幕上。

     本质上来说,就是发布的日志消息会转发给所有的接收者。

1、转发器(Exchanges)

此处引入转发器Exchanges,并使用Fanout模式,设置如下:

channel.exchangeDeclare("logs","fanout");fanout类型转发器特别简单,把所有它介绍到的消息,广播到所有它所知道的队列

2、匿名转发器(nameless exchange)

在不指定时候Rabbitmq有一个默认的转发器,它的标识符为””,如下:

channel.basicPublish("", QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());

第一个参数为转发器的名称,我们设置为”” : 如果存在routingKey(第二个参数),消息由routingKey决定发送到哪个队列。

现在我们可以指定消息发送到的转发器:

channel.basicPublish( "logs","", null, message.getBytes());

3、临时队列(Temporary queues)

之前每个队列都有一个特定的名称,能够为队列命名对我们来说是很关键的,我们需要指定消费者为某个队列。当我们希望在生产者和消费者间共享队列时,为队列命名是很重要的。

不过,对于我们的日志系统我们并不关心队列的名称。我们想要接收到所有的消息,而且我们也只对当前正在传递的数据的感兴趣。为了满足我们的需求,需要做两件事:
第一, 无论什么时间连接到Rabbit我们都需要一个新的空的队列。为了实现,我们可以使用随机数创建队列,或者更好的,让服务器给我们提供一个随机的名称。
第二, 一旦消费者与Rabbit断开,消费者所接收的那个队列应该被自动删除。
Java中我们可以使用queueDeclare()方法,不传递任何参数,来创建一个非持久的、唯一的、自动删除的队列且队列名称由服务器随机产生。
String queueName = channel.queueDeclare().getQueue();

一般情况这个名称与amq.gen-JzTY20BRgKO-HjmUJj0wLg 类似。

4、绑定(Bindings)

我们已经创建了一个fanout转发器和队列,我们现在需要通过binding告诉转发器把消息发送给我们的队列。

channel.queueBind(queueName, “logs”, ””)参数1:队列名称 ;参数2:转发器名称

5、完整的例子

日志发送端:

@Test
	public void sendMessage() throws IOException, TimeoutException{
		// 创建连接和频道
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
		// 声明转发器和类型
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout" );
		
		String message = new Date().toLocaleString()+" : log something";
		// 往转发器上发送消息
		channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
 
		System.out.println(" [x] Sent '" + message + "'");
 
		channel.close();
		connection.close();
	}

接收端1:写入本地日志文件:

@Test
	public void receiveMessage() throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{
		// 创建连接和频道
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
 
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
		// 创建一个非持久的、唯一的且自动删除的队列
		String queueName = channel.queueDeclare().getQueue();
		// 为转发器指定队列,设置binding
		channel.queueBind(queueName, EXCHANGE_NAME, "");
 
		System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
 
		QueueingConsumer consumer = new QueueingConsumer(channel);
		// 指定接收者,第二个参数为自动应答,无需手动应答
		channel.basicConsume(queueName, true, consumer);
 
		while (true){
			QueueingConsumer.Delivery delivery = consumer.nextDelivery();
			String message = new String(delivery.getBody());
			
			print2File(message);
		}

	}
	
	private static void print2File(String msg){
		try{
			String dir = ReceiveLogsToSave.class.getClassLoader().getResource("").getPath();
			String logFileName = new SimpleDateFormat("yyyy-MM-dd")
					.format(new Date());
			File file = new File(dir, logFileName+".txt");
			FileOutputStream fos = new FileOutputStream(file, true);
			fos.write((msg + "\r\n").getBytes());
			fos.flush();
			fos.close();
		} catch (FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		}
	}

接收端2:打印到控制台:

@Test
	public void receiveMessage() throws IOException, TimeoutException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{
		// 创建连接和频道
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
 
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
		// 创建一个非持久的、唯一的且自动删除的队列
		String queueName = channel.queueDeclare().getQueue();
		// 为转发器指定队列,设置binding
		channel.queueBind(queueName, EXCHANGE_NAME, "");
 
		System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
 
		QueueingConsumer consumer = new QueueingConsumer(channel);
		// 指定接收者,第二个参数为自动应答,无需手动应答
		channel.basicConsume(queueName, true, consumer);
 
		while (true){
			QueueingConsumer.Delivery delivery = consumer.nextDelivery();
			String message = new String(delivery.getBody());
			System.out.println(" [x] Received '" + message + "'");
 
		}
	}

效果如下:

接收端1:


接收端2:


这个例子实现了我们文章开头所描述的日志系统,利用了转发器的类型:fanout。

本篇说明了,生产者将消息发送至转发器,转发器决定将消息发送至哪些队列,消费者绑定队列获取消息。

转自http://blog.csdn.net/lmj623565791/article/details/37657225





猜你喜欢

转载自blog.csdn.net/BingoXing/article/details/81013819