今天在学习队列的时候感觉不是很清晰,望山翻阅了一些资料然后自己也做了一些总结
什么是队列消息:
消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递。消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消息而不管是谁发布的。这样发布者和使用者都不用知道对方的存在。
其中包括 消息路由 以及三种主要使用的交换机类型,Direct,Fanout,Topic
消息路由示例
Send
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
/**
* 配置rabbitMQ的连接相关信息
*/
factory.setHost("192.168.157.129");//指定ip
factory.setPort(5672);//指定端口
factory.setUsername("root");//指定账号
factory.setPassword("root");//指定密码
Connection connection=null; //定义连接
Channel channel=null; //定义通道
connection=factory.newConnection(); //获取连接
channel=connection.createChannel(); //获取通道
/**
* 声明一个队列
* 参数1 : 队列名 取值任意
* 参数2 : 是否为持久化的队列
* 参数3 : 是否排外 如果排外这个队列只允许一个消费者监听
* 参数4 : 是否自动删除队列,如果为true则表示当队列中没有消息,也没有消费者连接时就会自动删除这个队列
* 参数5 : 为队列的一些属性,通常设置为null即可
* 注意:
* 1.声明队列时,如果这个队列已经存在,则放弃声明,如果队列不存在则会声明一个新的队列
* 2.队列名可以取任意值,但是要与消息接收时完全一致
* 3.这行代码是可有可无的,如果没有,则在发送消息前确定队列名是否已经在rabbitMQ中存在,
* 如果不存在则会出现问题,程序不会出现异常,但是在队列中看不到消息
*/
channel.queueDeclare("myQueue",true,false,false,null);
String message="我的RabbitMQ的测试消息";
/**
* 发送消息到MQ
* 参数1 交换机名称,这里为空字符串表示不使用交换机
* 参数2 队列或RoutingKey,当指定了交换机名称之后这个值就是RoutingKey
* 参数3 消息属性信息,通常空即可
* 参数4 具体的消息数据的字节数组
* 注意:对列名必须与接收时完全一致
*/
channel.basicPublish("","myQueue",null,message.getBytes("utf-8"));
System.out.println("消息发送成功"+message);
channel.close();
connection.close();
}```
对于端口号。在代码里面是5672,在网页上面是15672.这个地方我刚做的时候也弄了好久
Receive
```java
public static void main(String[] args)throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setUsername("root");
factory.setPassword("root");
factory.setHost("192.168.157.129");
factory.setPort(5672);
//建立到代理服务器到连接
Connection connection=factory.newConnection();
//获得通道
Channel channel=connection.createChannel();
//声明队列
channel.queueDeclare("myQueue",true,false,false,null);
//消费消息
boolean autoAck=true;
String consumeTag="";
/**
* 接收消息
* 参数1:当前消费者需要监听的队列名,队列名必须与发送者的对列名完全一致,否则接收不到消息
* 参数2:消息是否自动确认,true表示自动确认,接收完消息会自动从队列中移除
* 参数3:消费者接收的标签,当多个消费者同时监听一个队列时,用于区分不同的消费者,通常为空字符串即可
* 参数4:为消息接收的回调方法,这个方法中具体完成对消息的处理代码
* 注意 :使用了basicConsume方法以后,会启动一个线程在持续的监听队列,如果队列中有信息的数据进入则会自动接收消息
* 因此不能关闭连接和通道对象
*/
channel.basicConsume("myQueue",autoAck,consumeTag,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("消费者-- "+message);
}
});
//实现不间断的消费监听,就不能关闭通道连接。
// channel.close();
// connection.close();
}
AMQP中的消息路由只需要bangding和routingkey就可以匹配上它是一个基本的模式
运行发送的代码
Direct交换机类型
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
//定义连接对象
Connection connection=null;
Channel channel=null; //定义通道对象
connection=factory.newConnection();//实例化连接对象
channel=connection.createChannel(); //实例化通道对象
channel.queueDeclare("myQueueDirect",true,false,false,null);
/**
* 声明一个交换机
* 参数1:交换机的名称,取值任意
* 参数2:交换机类型 取值为 direct,fanout,topic,headers
* 参数3:是否为持久化的交换机
* 注意:
* 1.如果这个交换机已经存在了,则放弃声明,如果不存在,则声明交换机
* 2.这个代码是可有可无的,但是在使用前必须确保这个交换机被声明
*/
String exchangeName="directExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
/**
*j将队列绑定到交换机
* 参数1:队列的名称
* 参数2:交换机名称
* 参数3:消息的RoutingKey (也就是BindingKey)
* 注意:
* 在进行队列和交换机绑定的时候,必须确保交换机和队列已经成功声明
*/
channel.queueBind("myQueueDirect",exchangeName,"directRoutingKey");
String message="direct 的测试消息";
/**
* 发送消息到队列
* 参数1:交换机名称
* 参数2:消息的RoutingKey 如果这个消息的RoutingKey和某个队列与交换机绑定的RoutingKey一致
* 那么这个消息就会发送到指定的队列中
* 注意:
* 1:发送消息时,必须确保交换机已经创建,并且确保已经正确的绑定到某个队列上了
*/
channel.basicPublish(exchangeName,"directRoutingKey",null,message.getBytes("utf-8"));
System.out.println("消息发送成功: "+message);
channel.close();
connection.close();
Receive
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
Connection connection=null; //定义连接对象
Channel channel=null; //定义通道对象
connection=factory.newConnection(); //实例化连接对象
channel=connection.createChannel(); //实例化通道对象
/**
* 指定exchange的类型
* 参数1:交换机名称
* 参数2:交换机的类型 取值为 direct,queue,topic,headers
* 参数3:是否为持久化消息,true表示持久化,false表示非持久化
*/
String exchangeName="directExchange";
channel.exchangeDeclare(exchangeName,"direct",true);
channel.queueDeclare("myQueueDirect",true,false,false,null);
//1:队列名 2:交换机名
//将队列和交换机绑定
channel.queueBind("myQueueDirect","directExchange","directRoutingKey");
/**
* 监听某个队列并获取队列中的数据
* 注意:
* 当前被监听的队列必须已经存在,并正确的绑定到某个交换机中-
*/
channel.basicConsume("myQueueDirect",true,"",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("消费者-- "+message);
}
});
}
消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则只转发 routing key 标记为“dog”的消息,不会转发“dog.puppy”,也不会转发“dog.guard”等等。它是完全匹配、单播的模式。
Fnaout类型
Send
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
Connection connection=null;
Channel channel=null;
connection=factory.newConnection();
channel=connection.createChannel();
String exchangeName="fanoutExchange";
/**
* 指定交换机类型
* 由于使用的是Fanout类型交换机,因此消息的接收方肯定会有多个,因此不建议在发送消息时创建队列
* 以及绑定交换机,建议在消费者中创建队列并绑定交换机
* 但是发送消息时至少应该确保交换机存在
*/
channel.exchangeDeclare(exchangeName,"fanout",true);
String message="fanout的测试消息";
channel.basicPublish(exchangeName,"",null,message.getBytes("utf-8"));
System.out.println("消息成功发送");
}
Receive
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
Connection connection=null;
Channel channel=null;
connection=factory.newConnection();
channel=connection.createChannel();
//声明交换机
String exchangeName="fanoutExchange";
channel.exchangeDeclare(exchangeName,"fanout",true);
/**
* 声明队列
* 由于Fanout类型的交换机的消息类似于广播的模式,它不需要绑定RoutingKey
* 而又可能又很多的消费者来接收这个交换机中的数据,因此我们创建队列时要创建一个随机的队列名称
*
* 没有参数的queueDeclare() 方法会创建一个名子为随机的队列
* 这个队列的数据是非持久化,是排外的(同时最多只允许有一个消费者监听当前队列)
* 自动删除的 当没有任何消费者监听这个队列时就会自动删除
*
* getQueue() 方法用于获取这个随机的对列名
*/
String queueName=channel.queueDeclare().getQueue();
//将这个随机的队列绑定到交换机中,由于是fanout类型的交换机,因此不需要指定RoutingKey进行绑定
channel.queueBind(queueName, exchangeName,"");
channel.basicConsume(queueName,true,"",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("Receive01消费者--- "+message);
}
});
}
每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。
Topic类型
Send
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
Connection connection=factory.newConnection();
Channel channel=connection.createChannel();
String exchangeName="topicExchange";
channel.exchangeDeclare(exchangeName,"topic",true);
String message="topic的测试消息";
String routingKey="test.myRoutingKey";
channel.basicPublish(exchangeName,routingKey,null,message.getBytes("utf-8"));
System.out.println("消息发送成功");
channel.close();
connection.close();
}
Receive
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.157.129");
factory.setPort(5672);
factory.setUsername("root");
factory.setPassword("root");
Connection connection=factory.newConnection();
Channel channel=connection.createChannel();
/**
*Topic 类型的交换机也是一种一对多的交换机,它和Fanout都能实现一个消息发送给多个队列
* Fanout更适合使用在一个功能不同的进程来获取数据,例如手机App里的推送,一个App可能会有很多个用户安装
* 他们都会启动一个随机的队列来接收自己的数据
* Topic更适合不同的功能模块来接收同一个消息,例如商城下单之后需要发送消息到队列中,例如 RoutingKey为
* order.success ,物流系统监听订单 order.* 发票系统监听订单order.*
*
* Topic可以使用随机的对列名,也可以使用一个明确的队列名,但是如果应用在和订单有关的功能上,
* 建议有个明确的队列名,并且要求是持久化的队列
*/
//声明交换机
String exchangeName="topicExchange";
channel.exchangeDeclare(exchangeName,"topic",true);
//声明队列
String queueName=channel.queueDeclare().getQueue();
String routingKey="test.#";
//绑定队列
channel.queueBind(queueName,exchangeName,routingKey);
//接收消息
channel.basicConsume(queueName,true,"",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("test.#---"+message);
}
});
}
topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“”。#匹配0个或多个单词,“”匹配不多不少一个单词。
Topic的接收方也可以是多个。