1、简单模式HelloWorld: 一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)
消息生产者
import com.rabbitmq.client.Channel;import com.rabbitmq.client.Connection;import com.rabbitmq.client.ConnectionFactory;/* 消息生产者:发送消息到队列 */public class Producer { //队列名 public static final String QUEUE_NAME = "simple_queue"; public static void main(String[] args) throws Exception { //1. 创建连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); //设置连接参数 connectionFactory.setHost("localhost"); //主机地址 connectionFactory.setPort(5672); //连接端口 connectionFactory.setVirtualHost("/pomelo");//虚拟主机 connectionFactory.setUsername("pomelo"); //用户名 connectionFactory.setPassword("pomelo"); //密码 //2. 创建连接 Connection connection = connectionFactory.newConnection(); //3. 创建频道 Channel channel = connection.createChannel(); /** * 4. 声明(创建)队列 * 参数1:队列名称 * 参数2:是否定义持久化队列 * 参数3:是否独占本次连接 * 参数4:是否在不使用的时候自动删除队列 * 参数5:队列其它参数 */ channel.queueDeclare(QUEUE_NAME,true,false,false,null); //5. 发送消息 String message = "你好,小兔子"; /** * 参数1:交换机名称,如果没有指定则使用默认Default Exchage * 参数2:路由key,简单模式可以传递队列名称 * 参数3:消息其它属性 * 参数4:消息内容 */ channel.basicPublish("", QUEUE_NAME, null, message.getBytes()); System.out.println("已发送消息:" + message); // 6. 关闭资源 channel.close(); connection.close(); }}
//消息消费者public class Consumer { //设置队列名 public static final String QUEUE_NAME = "simple_queue"; public static void main(String[] args) throws Exception { //1. 创建连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); //设置连接参数,如果没有指定的话,会有默认值 connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/pomelo"); connectionFactory.setUsername("pomelo"); connectionFactory.setPassword("pomelo"); //2. 创建连接 Connection connection = connectionFactory.getConnection(); //3. 创建频道 Channel channel = connection.createChannel(); //4. 声明队列 channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null); //5. 创建消息消费者 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override /** * consumerTag 消费者标签,在channel.basicConsume时候可以指定 * envelope 消息包的内容,可从中获取消息id,消息routingkey,交换机,消息和重传标志(收到消息失败后是否需要重新发送) * properties 属性信息 * body 消息 */ public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //路由key System.out.println("路由key: "+envelope.getRoutingKey()); //交换机 System.out.println("交换机: "+envelope.getExchange()); //消息id System.out.println("消息id: "+envelope.getDeliveryTag()); //接受到的消息 System.out.println("收到的消息: "+new String(body,"utf-8")); } }; //监听消息 /** * 参数1:队列名称 * 参数2:是否自动确认,设置为true为表示消息接收到自动向mq回复接收到了, * mq接收到回复会删除消息,设置为false则需要手动确认 * 参数3:消息接收到后回调 */ channel.basicConsume(Producer.QUEUE_NAME, true, consumer); //不关闭资源,应该一直监听消息 }}2、工作队列模式Work Queue: 一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认的交换机)在同一个队列中可以有多个消费者监听消息,消费者之间对于消息是竞争关系工作队列模式:一个消息只能被一个消费者接收,其它消费者不能接收到
/* 消息生产者 : 发送30个消息到队列 */public class Producer { public static final String QUEUE_NAME = "simple_queue"; public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //2. 创建频道 Channel channel = connection.createChannel(); //3. 声明队列 channel.queueDeclare(QUEUE_NAME,true,false,false,null); //4. 发送消息 for(int i = 0 ; i < 30 ; i++){ String message = "你好,小兔子! "+i; channel.basicPublish("",QUEUE_NAME,null,message.getBytes()); System.out.println("已发送消息: "+message); } //5. 关闭资源 channel.close(); connection.close(); }}
//消息消费者1public class Consumer1 { static final String QUEUE_NAME = "simple_queue"; public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //ConnectionUtil工具类创建连接工厂 //2. 创建频道 Channel channel = connection.createChannel(); //3. 声明队列 channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null); //4. 创建消息消费者 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //路由key System.out.println("路由key: "+envelope.getRoutingKey()); //交换机 System.out.println("交换机: "+envelope.getExchange()); //消息id System.out.println("消息id: "+envelope.getDeliveryTag()); //接受到的消息 System.out.println("收到的消息: "+new String(body,"utf-8")); } }; channel.basicConsume(Producer.QUEUE_NAME, true, consumer); //不关闭资源,应该一直监听消息 }}//消息消费者2public class Consumer2 { static final String QUEUE_NAME = "simple_queue"; public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //2. 创建频道 Channel channel = connection.createChannel(); //3. 声明队列 channel.queueDeclare(Producer.QUEUE_NAME,true,false,false,null); //4. 创建消息消费者 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { //路由key System.out.println("路由key: "+envelope.getRoutingKey()); //交换机 System.out.println("交换机: "+envelope.getExchange()); //消息id System.out.println("消息id: "+envelope.getDeliveryTag()); //接受到的消息 System.out.println("收到的消息: "+new String(body,"utf-8")); } }; channel.basicConsume(Producer.QUEUE_NAME, true, consumer); //不关闭资源,应该一直监听消息 }}前面2个案例中,只有3个角色: P:生产者,也就是要发送消息的程序 C:消费者:消息的接受者,会一直等待消息到来。 queue:消息队列,图中红色部分而在订阅模式中,多了一个exchange角色,而且过程略有变化: P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机) C:消费者,消息的接受者,会一直等待消息到来 Q : 消息队列,接收消息、缓存消息 X : 交换机,接收数据并决定如何将数据转发投递到不同的队列,消息的投递决定于交换机的类型Exchange有常见以下3种类型: Fanout:广播,将消息交给所有绑定到交换机的队列 Direct:定向,把消息交给符合指定routing key 的队列 Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!3、发布订阅模式Publish/subscribe,需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列
/* 消息生产者:发布订阅模式 发布订阅模式Publish/subscribe 1.需要设置类型为fanout的交换机 2.并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列 */public class Producer { // 交换机名称 public static final String FANOUT_EXCHANGE = "fanout_exchange"; // 队列名称1 public static final String FANOUT_QUEUE_1 = "fanout_queue_1"; // 队列名称2 public static final String FANOUT_QUEUE_2 = "fanout_queue_2"; public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); /** * 3.声明交换机 * 参数1:交换机名称 * 参数2:交换机类型:fanout、direct、topic、headers */ channel.exchangeDeclare(FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT); /** * 4.声明(创建)队列 * 参数1:队列名称 * 参数2:是否定义持久化队列 * 参数3:是否独占本次连接 * 参数4:是否在不使用的时候自动删除队列 * 参数5:队列其它参数 */ channel.queueDeclare(FANOUT_QUEUE_1,true,false,false,null); channel.queueDeclare(FANOUT_QUEUE_2,true,false,false,null); // 5.队列绑定交换机 channel.queueBind(FANOUT_QUEUE_1,FANOUT_EXCHANGE,""); channel.queueBind(FANOUT_QUEUE_2,FANOUT_EXCHANGE,""); // 6. 发送多个消息 for (int i=1; i<=10; i++) { // 要发送的信息 String message = "你好;小兔子!"+i; /** * 参数1:交换机名称,如果没有指定则使用默认Default Exchage * 参数2:路由key,简单模式可以传递队列名称 * 参数3:消息其它属性 * 参数4:消息内容 */ channel.basicPublish(FANOUT_EXCHANGE, "", null, message.getBytes()); System.out.println("已发送消息:" + message); } // 7. 关闭资源 channel.close(); connection.close(); }}
/** * 消息消费者1: 消费队列1中的消息 */public class Consumer1 { public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); // 3.创建交换机 channel.exchangeDeclare(Producer.FANOUT_EXCHANGE,BuiltinExchangeType.FANOUT); // 4.声明(创建)队列 channel.queueDeclare(Producer.FANOUT_QUEUE_1, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.FANOUT_QUEUE_1,Producer.FANOUT_EXCHANGE,""); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.FANOUT_QUEUE_1, true, consumer); }}
** * 消息消费者2: 消费队列2中的消息 */public class Consumer2 { public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); // 3.创建交换机 channel.exchangeDeclare(Producer.FANOUT_EXCHANGE,BuiltinExchangeType.FANOUT); // 4.声明(创建)队列 channel.queueDeclare(Producer.FANOUT_QUEUE_2, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.FANOUT_QUEUE_2,Producer.FANOUT_EXCHANGE,""); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.FANOUT_QUEUE_2, true, consumer); }}
发布与订阅:一个消息可以被多个消费者接收,使用了 fanout(广播)类型的交换机4、路由模式Routing 需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列
路由模式特点:
-
队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey (路由key)
-
消息的发送方在 向Exchange发送消息时,也必须指定消息的 RoutingKey 。
-
Exchange不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的
Routingkey 与消息的 Routing key 完全一致,才会接收到消息
/* 路由消息生产者:消息发送到交换机 */public class Producer { //交换机名 public static final String DIRECT_EXCHANGE = "direct_exchange"; //队列名 public static final String DIRECT_QUEUE_INSERT = "direct_queue_insert"; public static final String DIRECT_QUEUE_UPDATE = "direct_queue_update"; public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //2. 创建频道 Channel channel = connection.createChannel(); 3.声明交换机 channel.exchangeDeclare(DIRECT_EXCHANGE, BuiltinExchangeType.DIRECT); //4.声明(创建)队列 channel.queueDeclare(DIRECT_QUEUE_INSERT,true,false,false,null); channel.queueDeclare(DIRECT_QUEUE_UPDATE,true,false,false,null); // 5.队列绑定交换机 channel.queueBind(DIRECT_QUEUE_INSERT,DIRECT_EXCHANGE,"insert"); channel.queueBind(DIRECT_QUEUE_UPDATE,DIRECT_EXCHANGE,"update"); // 6.发送消息 String message = "新增了商品,路由模式;routing key 为 insert "; channel.basicPublish(DIRECT_EXCHANGE,"insert",null,message.getBytes()); System.out.println("已发送消息:" + message); message = "修改了商品,路由模式;routing key 为 update "; channel.basicPublish(DIRECT_EXCHANGE,"update",null,message.getBytes()); System.out.println("已发送消息:" + message); // 7.关闭资源 channel.close(); connection.close(); }}
/** * 消息消费者: 消费队列中的消息 */public class Consumer1 { public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); // 3.创建交换机 channel.exchangeDeclare(Producer.DIRECT_EXCHANGE,BuiltinExchangeType.DIRECT); // 4.声明(创建)队列 channel.queueDeclare(Producer.DIRECT_QUEUE_INSERT, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.DIRECT_QUEUE_INSERT,Producer.DIRECT_EXCHANGE,"insert"); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.DIRECT_QUEUE_INSERT, true, consumer); }}
/** * 消息消费者2: 消费队列中的消息 */public class Consumer2 { public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); // 3.创建交换机 channel.exchangeDeclare(Producer.DIRECT_EXCHANGE,BuiltinExchangeType.DIRECT); // 4.声明(创建)队列 channel.queueDeclare(Producer.DIRECT_QUEUE_UPDATE, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.DIRECT_QUEUE_UPDATE,Producer.DIRECT_EXCHANGE,"update"); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.DIRECT_QUEUE_UPDATE, true, consumer); }}小结:路由模式:可以根据消息的路由key传递到不同的队列,然后实现消息的定向投递到不同消费者。
5、通配符模式Topic需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列
案例分析
-
生产者:发送包含有item.insert,item.update,item.delete的3种路由key的消息
-
-
消费者2:一个队列绑定到交换机的routing key是:item.update、item.delete
//Topic通配符模型消息生产者:消息发送到交换机public class Producer { // 交换机名称 public static final String TOPIC_EXCHANGE = "topic_exchange"; // 队列名称 public static final String TOPIC_QUEUE_1 = "topic_queue_1"; // 队列名称 public static final String TOPIC_QUEUE_2 = "topic_queue_2"; public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //2. 创建频道 Channel channel = connection.createChannel(); //3. 声明交换机 参数1(交换机名) 参数2(交换机类型) channel.exchangeDeclare(TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC); //4.发送消息 String message = "新增了商品,Topic模式,路由key为item.insert"; channel.basicPublish(TOPIC_EXCHANGE,"item.insert",null,message.getBytes()); System.out.println("已发送消息:" + message); message = "修改了商品,Topic模式,路由key为item.update"; channel.basicPublish(TOPIC_EXCHANGE,"item.update",null,message.getBytes()); System.out.println("已发送消息:" + message); message = "删除了商品,Topic模式,路由key为item.delete"; channel.basicPublish(TOPIC_EXCHANGE,"item.delete",null,message.getBytes()); System.out.println("已发送消息:" + message); // 7.关闭资源 channel.close(); connection.close(); }}
/** * 消息消费者: 消费队列中的消息 */public class Consumer2 { public static void main(String[] args) throws Exception { // 1.创建连接 Connection connection = ConnectionUtil.getConnection(); // 2.创建频道 Channel channel = connection.createChannel(); // 3.创建交换机 channel.exchangeDeclare(Producer.TOPIC_EXCHANGE,BuiltinExchangeType.TOPIC); // 4.声明(创建)队列 channel.queueDeclare(Producer.TOPIC_QUEUE_2, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.TOPIC_QUEUE_2, Producer.TOPIC_EXCHANGE,"item.update"); channel.queueBind(Producer.TOPIC_QUEUE_2, Producer.TOPIC_EXCHANGE,"item.delete"); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.TOPIC_QUEUE_2, true, consumer); }}
public class Consumer1 { public static void main(String[] args) throws Exception { //1. 创建连接 Connection connection = ConnectionUtil.getConnection(); //2. 创建频道 Channel channel = connection.createChannel(); //3. 声明交换机 参数1(交换机名) 参数2(交换机类型) channel.exchangeDeclare(Producer.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC); //4. 声明队列 channel.queueDeclare(Producer.TOPIC_QUEUE_1, true, false, false, null); // 5.队列绑定到交换机 channel.queueBind(Producer.TOPIC_QUEUE_1, Producer.TOPIC_EXCHANGE,"item.*"); // 6.创建消费者;并设置消息处理 DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("路由key为:" + envelope.getRoutingKey()); System.out.println("交换机为:" + envelope.getExchange()); System.out.println("消息id为:" + envelope.getDeliveryTag()); System.out.println("接收到的消息为:" + new String(body, "utf-8")); } }; // 7.监听消息 channel.basicConsume(Producer.TOPIC_QUEUE_1, true, consumer); }}小结 Topic通配符模式:可以根据routing key将消息传递到队列中;其中routing key可以使用 * 和 #,使用了通配符则对应路由key的配置更加灵活。
-
不使用Exchange交换机(默认交换机)
-
simple简单模式:一个生产者发送消息到队列中由一个消费者接收。
-
work工作队列模式:一个生产者发送消息到队列中可由多个消费者接收;多个消费者之间消息是竞争接收。
-
-
使用Exchange交换机;订阅模式(广播fanout,定向direct,通配符topic)
-
发布与订阅模式:使用了fanout类型的交换机,可以将一个消息发送到所有与交换机绑定的队列并被消费者接收。
-
路由模式:使用了direct类型的交换机,可以将一个消息发送到routing key相关的队列并被消费者接收。
-
通配符模式:使用了topic类型的交换机,可以将一个消息发送到routing key(*,#)相关的队列并被消费者接收。
-