消息队列 (3) RabbtMQ 发布/订阅 、Routing Key 模式、Topic 通配符模式
第三种模式:发布/订阅 一对多 每个消费者监听各自的队列 消息来了每个一个消费者 都可以收到
在订阅模式中,多了一个交换机 Exchange角色,而且过程略有变化。
P:生产者,发送消息的程序,但是不再发送到队列中,而是发给交换机。
Queue:消息队列,接收消息、缓存消息。
Exchange:交换机,一方面接收生产者发送来的消息。另一方面知道如何处理消息,例如交给特别的队列,或者全部的队列,或者将消息丢弃。到底如何操作取决于Exchange是哪种类型:
1.Fanout:广播,将消息分发给所有绑定到交换机的队列。
2.Direct:定向,把消息交给符合特定routing key 的队列。
3.Topic:通配符,吧消息交给符合routing pattern (路由模式)的队列。
Exchange 只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息就会丢失!
一、编写代码:
发布方: fanout 分发消息
package com.david; import com.rabbitmq.client.BuiltinExchangeType; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.io.IOException; import java.util.concurrent.TimeoutException; public class Publish { private static String EXCHANGE_NAME = "pubsub"; public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); //设置参数 factory.setHost("10.211.55.4"); //id factory.setVirtualHost("local"); //虚拟机 factory.setPort(5672); //端口 默认5672 factory.setUsername("admin"); factory.setPassword("admin"); //创建connect Connection connection = factory.newConnection(); //创建管道 Channel channel = connection.createChannel(); //创建交换机 fanout是分发 所有人都收到 //channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); //创建队列 String queue1_name="test_fanout_queue1"; String queue2_name="test_fanout_queue2"; channel.queueDeclare(queue1_name,true,false,false,null); channel.queueDeclare(queue2_name,true,false,false,null); //绑定交换机 channel.queueBind(queue1_name,EXCHANGE_NAME,""); channel.queueBind(queue2_name,EXCHANGE_NAME,""); for(int i = 0;i<10;i++){ String message = "hello rabbitmq!" + i; channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes()); System.out.println("Producer Send : " + message); } channel.close(); connection.close(); } }
消费者代码
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; public class Sub1 { private static String QUEUE_NAME = "test_fanout_queue1"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("10.211.55.4"); factory.setUsername("admin"); factory.setPassword("admin"); factory.setPort(5672); factory.setVirtualHost("local"); Connection connection = factory.newConnection(); final Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,true,false,false,null); System.out.println("work1 ready"); //每次从队列获取的数量 channel.basicQos(1); Consumer comsumer = 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("work1 收到了 :" + message); try { doWork(); }catch (Exception ex){ channel.abort(); }finally { System.out.println("work1 完成了"); channel.basicAck(envelope.getDeliveryTag(),false); } } }; boolean autoAck = false; channel.basicConsume(QUEUE_NAME,autoAck,comsumer); } public static void doWork() throws InterruptedException { Thread.sleep(1000); } }
上面完成了一个发布/订阅模式的消息队列 可以看到 当发布一个消息的时候 发送到交换机中 由交换机发送给每一个消费者 没有竞争 都得到了
第四种模式:Routing 路由模式
上面我们采用了广播的模式进行消息的发送,现在我们采用路由的方式对不同的消息进行过滤。
新建发送端
1 public class Producer_Routing { 2 3 private static String EXCHANGE_NAME = "RoutingKey"; 4 public static void main(String[] args) throws IOException, TimeoutException { 5 //创建连接工厂 6 ConnectionFactory factory = new ConnectionFactory(); 7 //设置参数 8 factory.setHost("10.211.55.4"); //id 9 factory.setVirtualHost("local"); //虚拟机 10 factory.setPort(5672); //端口 默认5672 11 factory.setUsername("admin"); 12 factory.setPassword("admin"); 13 //创建connect 14 Connection connection = factory.newConnection(); 15 //创建管道 16 Channel channel = connection.createChannel(); 17 18 //创建交换机 direct 19 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); 20 21 //创建队列 22 String queue1_name="test_direct_queue1"; 23 String queue2_name="test_direct_queue2"; 24 channel.queueDeclare(queue1_name,true,false,false,null); 25 channel.queueDeclare(queue2_name,true,false,false,null); 26 27 //队列1绑定 28 channel.queueBind(queue1_name,EXCHANGE_NAME,"error"); 29 //队列2绑定 30 channel.queueBind(queue2_name,EXCHANGE_NAME,"info"); 31 channel.queueBind(queue2_name,EXCHANGE_NAME,"warning"); 32 channel.queueBind(queue2_name,EXCHANGE_NAME,"error"); 33 34 for(int i = 0;i<10;i++){ 35 String type = ""; 36 if(i%2==0){ 37 type = "info"; 38 }else{ 39 type = "error"; 40 } 41 String message = type + i; 42 channel.basicPublish(EXCHANGE_NAME,type,null,message.getBytes()); 43 System.out.println("Producer Send : " + message); 44 } 45 46 channel.close(); 47 connection.close(); 48 49 } 50 }
新建消费者1 用于接受error
public class Customer_RoutingKey1 { private static String QUEUE_NAME = "test_direct_queue1"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("10.211.55.4"); factory.setUsername("admin"); factory.setPassword("admin"); factory.setPort(5672); factory.setVirtualHost("local"); Connection connection = factory.newConnection(); final Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,true,false,false,null); System.out.println("routing1 ready"); Consumer comsumer = 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("routing1 收到了 :" + message); } }; channel.basicConsume(QUEUE_NAME,true,comsumer); } }
新建消费者2 用于接受info
public class Customer_RoutingKey2 { private static String QUEUE_NAME = "test_direct_queue2"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("10.211.55.4"); factory.setUsername("admin"); factory.setPassword("admin"); factory.setPort(5672); factory.setVirtualHost("local"); Connection connection = factory.newConnection(); final Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME,true,false,false,null); System.out.println("routing2 ready"); Consumer comsumer = 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("routing2 收到了 :" + message); } }; channel.basicConsume(QUEUE_NAME,true,comsumer); } }
执行后 只有routingkey1 灰收到 1 3 5 7 9 routingkey2 可以收到全部
第五种模式:通配符模式 Topics
这种应该属于模糊匹配
*:可以替代一个词
#:可以替代0或者多个词
发送端:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.io.IOException; import java.util.concurrent.TimeoutException; public class TopicSend { private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] args) throws IOException, TimeoutException { Connection connection = null; Channel channel = null; try { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); connection = factory.newConnection(); channel = connection.createChannel(); //声明一个匹配模式的交换机 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); //待发送的消息 String[] routingKeys = new String[]{ "quick.orange.rabbit", "lazy.orange.elephant", "quick.orange.fox", "lazy.brown.fox", "quick.brown.fox", "quick.orange.male.rabbit", "lazy.orange.male.rabbit" }; //发送消息 for (String severity : routingKeys) { String message = "From " + severity + " routingKey' s message!"; channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes()); System.out.println("TopicSend Sent '" + severity + "':'" + message + "'"); } } catch (Exception e) { e.printStackTrace(); if (connection != null) { channel.close(); connection.close(); } } finally { if (connection != null) { channel.close(); connection.close(); } } } }
消费者1 匹配*.orange.*的
import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; public class ReceiveLogsTopic1 { private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); //声明一个匹配模式的交换机 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); String queueName = channel.queueDeclare().getQueue(); //路由关键字 String[] routingKeys = new String[]{"*.orange.*"}; //绑定路由 for (String routingKey : routingKeys) { channel.queueBind(queueName, EXCHANGE_NAME, routingKey); System.out.println("ReceiveLogsTopic1 exchange:" + EXCHANGE_NAME + ", queue:" + queueName + ", BindRoutingKey:" + routingKey); } System.out.println("ReceiveLogsTopic1 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("ReceiveLogsTopic1 Received '" + envelope.getRoutingKey() + "':'" + message + "'"); } }; channel.basicConsume(queueName, true, consumer); } }
消费者2 匹配*.*.rabit的 和lazy.# lazy开头的
import com.rabbitmq.client.*; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.concurrent.TimeoutException; public class ReceiveLogsTopic2 { private static final String EXCHANGE_NAME = "topic_logs"; public static void main(String[] argv) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); // 声明一个匹配模式的交换器 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); String queueName = channel.queueDeclare().getQueue(); // 路由关键字 String[] routingKeys = new String[]{"*.*.rabbit", "lazy.#"}; // 绑定路由关键字 for (String bindingKey : routingKeys) { channel.queueBind(queueName, EXCHANGE_NAME, bindingKey); System.out.println("ReceiveLogsTopic2 exchange:"+EXCHANGE_NAME+", queue:"+queueName+", BindRoutingKey:" + bindingKey); } System.out.println("ReceiveLogsTopic2 Waiting for messages"); Consumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws UnsupportedEncodingException { String message = new String(body, "UTF-8"); System.out.println("ReceiveLogsTopic2 Received '" + envelope.getRoutingKey() + "':'" + message + "'"); } }; channel.basicConsume(queueName, true, consumer); } }