RabbitMQ-learning
第一种模式=直连
-
-
C:消费者:消息的接受者,会一直等待消息到来。
-
producer:
package com.quan.rabbitmq.producer; 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 RMQProducer { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare("hello",true,false,false,null); channel.basicPublish("","hello",null,"hello rabbit".getBytes()); channel.close(); connection.close(); } }
consumer:
package com.quan.rabbitmq.consumer; import com.rabbitmq.client.*; import java.io.IOException; import java.util.concurrent.TimeoutException; public class RMQConsumer { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建通道 Channel channel = connection.createChannel(); /** * 参数1:声明通道对应的队列 * 参数2:指定是否持久化 * 参数3:指定是否独占对象 * 参数4:指定是否自动删除队列 * 参数5:对队列的额外设置 */ channel.queueDeclare("hello",true,false,false,null); channel.basicConsume("hello",true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println(new String(body)); } }); } }
第二种模式=任务模型(
)
-
-
C1:消费者-1,领取任务并且完成任务,假设完成速度较慢
-
P:
/** * 直连模型: * 生产者发送消息 * 消费者,等待消息到来消费 * 消息队列:可以缓存消息,生产者向其中投递消息,消费者从其中取出消息。 * */ public class RMQProducer2 { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); channel.queueDeclare("hello",true,false,false,null); for(int i =0 ;i<20;i++){ channel.basicPublish("","hello",null,(i+"=====>hello rabbit").getBytes()); } channel.close(); connection.close(); } }
C1
public class RMQConsumer21 { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建通道 Channel channel = connection.createChannel(); /** * 参数1:声明通道对应的队列 * 参数2:指定是否持久化 * 参数3:指定是否独占对象 * 参数4:指定是否自动删除队列 * 参数5:对队列的额外设置 */ channel.queueDeclare("hello",true,false,false,null); channel.basicConsume("hello",true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("我是consumer1"+new String(body)); } }); } }
C2
public class RMQConsumer22 { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建通道 Channel channel = connection.createChannel(); /** * 参数1:声明通道对应的队列 * 参数2:指定是否持久化 * 参数3:指定是否独占对象 * 参数4:指定是否自动删除队列 * 参数5:对队列的额外设置 */ channel.queueDeclare("hello",true,false,false,null); channel.basicConsume("hello",true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是consumer2"+new String(body)); } }); } }
re
当两个消费者都在监听通道中的消息的时候:
我们一旦发消息:
我是consumer2 0=====>hello rabbit 我是consumer2 2=====>hello rabbit 我是consumer2 4=====>hello rabbit 我是consumer2 6=====>hello rabbit 我是consumer2 8=====>hello rabbit 我是consumer2 10=====>hello rabbit 我是consumer2 12=====>hello rabbit 我是consumer2 14=====>hello rabbit 我是consumer2 16=====>hello rabbit 我是consumer2 18=====>hello rabbit @@@@@@@@@@@@@@ 我是consumer1 1=====>hello rabbit 我是consumer1 3=====>hello rabbit 我是consumer1 5=====>hello rabbit 我是consumer1 7=====>hello rabbit 我是consumer1 9=====>hello rabbit 我是consumer1 11=====>hello rabbit 我是consumer1 13=====>hello rabbit 我是consumer1 15=====>hello rabbit 我是consumer1 17=====>hello rabbit 我是consumer1 19=====>hello rabbit
默认情况下,RabbitMQ将按顺序将每个消息发送给下一个使用者。平均而言,每个消费者都会收到相同数量的消息。这种分发消息的方式称为循环。
消息确认机制:
public class RMQConsumer21 { public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("localhost"); connectionFactory.setPort(5672); connectionFactory.setUsername("quan"); connectionFactory.setPassword("admin"); connectionFactory.setVirtualHost("quan"); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建通道 final Channel channel = connection.createChannel(); channel.queueDeclare("hello",true,false,false,null); channel.basicQos(1);//一次只接受一条为确认的消息 //第二个参数:关闭自动确认消息 channel.basicConsume("hello",false,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("我是consumer1 "+new String(body)); channel.basicAck(envelope.getDeliveryTag(),false);//手动确认消息 } }); } }
re:
我是consumer1 0=====>hello rabbit @@@@@@@@@@@ 我是consumer2 1=====>hello rabbit 我是consumer2 2=====>hello rabbit 我是consumer2 3=====>hello rabbit 我是consumer2 4=====>hello rabbit 我是consumer2 5=====>hello rabbit 我是consumer2 6=====>hello rabbit 我是consumer2 7=====>hello rabbit 我是consumer2 8=====>hello rabbit 我是consumer2 9=====>hello rabbit 我是consumer2 10=====>hello rabbit 我是consumer2 11=====>hello rabbit 我是consumer2 12=====>hello rabbit 我是consumer2 13=====>hello rabbit 我是consumer2 14=====>hello rabbit 我是consumer2 15=====>hello rabbit 我是consumer2 16=====>hello rabbit 我是consumer2 17=====>hello rabbit 我是consumer2 18=====>hello rabbit 我是consumer2 19=====>hello rabbit
第三种模式-广播
fanout 扇出===广播
消息发送流程:
- 可以有多个消费者 - 每个消费者有自己的queue(队列) - 每个队列都要绑定到Exchange(交换机) - 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。 - 交换机把消息发送给绑定过的所有队列 - 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
p;更新部分:
Connection connection = connectionFactory.newConnection(); Channel channel = connection.createChannel(); channel.exchangeDeclare("logs","fanout"); for(int i =0 ;i<20;i++){ //第一个参数:交换机名字 //第二个参数:队列名字 //第三个参数: //第四个参数:消息,是byte类型 channel.basicPublish("logs","",null,(i+"=====>hello rabbit").getBytes()); }
c1 c2 c3:这三个都是差不多的配置:
//绑定交换机 channel.exchangeDeclare("logs","fanout"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange channel.queueBind(queue,"logs",""); //处理消息 channel.basicConsume(queue,true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("我是consumer1 "+new String(body)); } });
re:
所有的消息,每个消费者都可以消费得到,
第四种模式-
在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。
这时就要用到Direct类型的Exchange。
流程:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个`RoutingKey`(路由key) - 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 `RoutingKey`。 - Exchange不再把消息交给每一个绑定的队列,而是根据消息的`Routing Key`进行判断,只有队列的`Routingkey`与消息的 `Routing key`完全一致,才会接收到消息
- P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key。 - X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与routing key完全匹配的队列 - C1:消费者,其所在队列指定了需要routing key 为 error 的消息 - C2:消费者,其所在队列指定了需要routing key 为 info、error、warning 的消息
情景:
c1 :error,info,debug
c2:error
c3:info
p:会每种key发一条消息:
p:改变rkey的值:
//声明交换机:参数1:交换机名称 //参数2:交换机类型: channel.exchangeDeclare("logs1","direct"); String rkey = "debug"; //第一个参数:交换机名字 //第二个参数:队列名字/路由key //第三个参数: //第四个参数:消息,是byte类型 channel.basicPublish("logs1",rkey,null,(rkey+"消息=====>hello rabbit").getBytes()); channel.close(); connection.close(); }
c1:
//绑定交换机/参数2 交换机类型 channel.exchangeDeclare("logs1","direct"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange //第一个参数:队列,第2个参数:交换机名字,第3个参数:路由key channel.queueBind(queue,"logs1","error"); channel.queueBind(queue,"logs1","info"); channel.queueBind(queue,"logs1","debug"); //处理消息 channel.basicConsume(queue,true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("我是consumer1 "+new String(body)); } }); }
c2:
//绑定交换机 channel.exchangeDeclare("logs1","direct"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange //第一个参数:队列,第2个参数:交换机名字,第3个参数:路由key channel.queueBind(queue,"logs1","error"); //处理消息
C3:
//创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建通道 Channel channel = connection.createChannel(); //绑定交换机 channel.exchangeDeclare("logs1","direct"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange //第一个参数:队列,第2个参数:交换机名字,第3个参数:路由key channel.queueBind(queue,"logs1","info"); //处理消息
p每个rkey发送一次消息后的结果:
我是consumer1 error消息=====>hello rabbit 我是consumer1 info消息=====>hello rabbit 我是consumer1 debug消息=====>hello rabbit 我是consumer2 error消息=====>hello rabbit 我是consumer3 info消息=====>hello rabbit
可以知道,这种类型的交换机是可以按需发送的。
Routing的订阅模式--topic:
p:
//声明交换机:参数1:交换机名称 //参数2:交换机类型: channel.exchangeDeclare("topic1","topic"); String rkey = "user.debug.all"; //第一个参数:交换机名字 //第二个参数:队列名字/路由key //第三个参数: //第四个参数:消息,是byte类型 channel.basicPublish("topic1",rkey,null,(rkey+"消息=====>hello rabbit").getBytes());
c2:
//绑定交换机 channel.exchangeDeclare("topic1","topic"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange //第一个参数:队列,第2个参数:交换机名字,第3个参数:路由key channel.queueBind(queue,"topic1","user.#");
user.#可以匹配多个后面的单词:
c1:
//绑定交换机/参数2 交换机类型 channel.exchangeDeclare("topic1","topic"); //创建临时队列 String queue = channel.queueDeclare().getQueue(); //将临时队列绑定交换机exchange //第一个参数:队列,第2个参数:交换机名字,第3个参数:路由key channel.queueBind(queue,"topic1","user.*");
user.*只能接一个单词:
p发送了一次key为user.debug 和一次user.debug.all:
我是consumer1 user.debug消息=====>hello rabbit 我是consumer2 user.debug消息=====>hello rabbit 我是consumer2 user.debug.all消息=====>hello rabbit