RabbitMQ 工作原理
对于 RabbitMQ 来说, 除了这三个基本模块以外, 还添加了一个模块, 即交换机(Exchange). 它使得生产者和消息队列之间产生了隔离, 生产者将消息发送给交换机,而交换机则根据调度策略把相应的消息转发给对应的消息队列. 那么 RabitMQ 的工作流程如下所示:
说一下交换机: 交换机的主要作用是接收相应的消息并且绑定到指定的队列. 交换机有四种类型, 分别为Direct, topic, headers, Fanout.
Direct 是 RabbitMQ 默认的交换机模式,也是最简单的模式.即创建消息队列的时候,指定一个 BindingKey. 当发送者发送消息的时候, 指定对应的 Key. 当 Key 和消息队列的 BindingKey 一致的时候,消息将会被发送到该消息队列中.
topic 转发信息主要是依据通配符, 队列和交换机的绑定主要是依据一种模式(通配符+字符串), 而当发送消息的时候, 只有指定的 Key 和该模式相匹配的时候, 消息才会被发送到该消息队列中.
headers 也是根据一个规则进行匹配, 在消息队列和交换机绑定的时候会指定一组键值对规则, 而发送消息的时候也会指定一组键值对规则, 当两组键值对规则相匹配的时候, 消息会被发送到匹配的消息队列中.
Fanout 是路由广播的形式, 将会把消息发给绑定它的全部队列, 即便设置了 key, 也会被
1.生产者
package cn.zhm.util.simple;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
public class SendWork {
/**
* @Description: java类作用描述
* @Author: zhaohaiming
* @CreateDate: 2019/8/14 0:18
* @Version: 1.0
*/
private static final String QUEUE_NAME = "test_work_name";
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
for (int i = 0 ;i <50;i++){
//发送信息内容
String msg = "hello work!!"+i;
System.out.println("SendWork send msg信息:"+msg);
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
Thread.sleep(300);
}
}catch (Exception e){
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
}
}
消费者
package cn.zhm.util.simple;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
public class ReceptionWork {
/**
* @Description: 消费者接收信息
* @Author: zhaohaiming
* @CreateDate: 2019/8/11 21:58
* @Version: 1.0
*/
private static final String QUEUE_NAME = "test_work_name";
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//获取消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String smg = new String(body,"utf-8");
System.out.println("消费者接收消息:"+smg);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("is down");
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,consumer);
}catch (Exception e){
e.printStackTrace();
}finally {
// channel.close();
// connection.close();
}
}
}
//监听队列
channel.basicConsume(QUEUE_NAME,true,consumer);
ture :(自动确认模式) 一旦rabbitmq将消息分发给消费者,就会从内容中删除,
这种情况下,如果杀死正在执行的消费者,就会丢失正在处理的消息
flase :(手动模式),如果 有一个消费者挂掉,就会交付给其他消费者。rabbitMq支持消应答,消费者发送一个消息应答,告诉rabitMq这个消息我已经处理完,你可以删除,,然后rabbitmq就删除内存中的消息
默认是flase
消息的持久
//创建队列声明
boolean durable = false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
已经设置durable=false 不能再修改成为ture,尽管代码是正确的,他也不会运行成功,因为我们已经定义了一个 test_work_name这个queue是未持久化rabbitmq不准许重新定义(不同参数)一个已经存在的队列。
订阅模型
解读
1.一个生产者,多个消费者
2.每一个消费者都的自己的队列
3.生产者没有直接把消息发送到队列,而是发到了交换机转发器到exchanger
4.每个队列都是要在绑定到交换器上
5.生产者发送的消息经过交换机,到达队列 一个消息对应多个消费者消费
注册 -》 邮件 ——》短信
生产者
package cn.zhm.util.su;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
/**
* @Description: 订阅 生产者
* @Author: zhaohaiming
* @CreateDate: 2019/8/15 0:42
* @Version: 1.0
*/
private static final String EXCHANGE_NAME = "exchange_test";
public static void main(String[] args) throws IOException, TimeoutException {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//fanout 分发 广播形式
String message = "Hello wordle exchang....";
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
}catch (Exception e){
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
}
}
消息到那去了 ?? 丢失了 因为交换机没有存储的能力,在rabbitmq里面只有队列有存储能力。
因为这个时候还没有队列绑定到这个交换机所以数据丢失了
消费者1
package cn.zhm.util.su;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Reception {
/**
* @Description: 订阅 消费者
* @Author: zhaohaiming
* @CreateDate: 2019/8/15 0:59
* @Version: 1.0
*/
private static final String QUEUE_NAME = "test_sub_name_info";
private static final String EXCHANGE_NAME = "exchange_test";
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
boolean durable = false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
//绑定交换机
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//获取消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String smg = new String(body,"utf-8");
System.out.println("test_sub_name_info_消费者接收消息:"+smg);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("test_sub_name_info is down");
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,consumer);
}catch (Exception e){
e.printStackTrace();
}finally {
// channel.close();
// connection.close();
}
}
}
运行结果:
test_sub_name_info_消费者接收消息:Hello wordle exchang....
test_sub_name_info is down
消费者2
package cn.zhm.util.su;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
public class ReceptionEmail {
/**
* @Description: 订阅 消费者
* @Author: zhaohaiming
* @CreateDate: 2019/8/15 0:59
* @Version: 1.0
*/
private static final String QUEUE_NAME = "test_sub_name_email";
private static final String EXCHANGE_NAME = "exchange_test";
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
boolean durable = false;
channel.queueDeclare(QUEUE_NAME,durable,false,false,null);
//绑定交换机
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//获取消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String smg = new String(body,"utf-8");
System.out.println("test_sub_name_email_消费者接收消息:"+smg);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("test_sub_name_email is down");
}
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,consumer);
}catch (Exception e){
e.printStackTrace();
}finally {
// channel.close();
// connection.close();
}
}
}
运行结果:
test_sub_name_email_消费者接收消息:Hello wordle exchang....
test_sub_name_email is down
6.Exchange(交换机,转发器)
一方面是接收生产的消息,另一方面是向队列推送消息
匿名转发: “”
Fanout(不处理路由键)
Direct (处理路由键)
路由模式
模型
生产者:
package routing;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
/**
* @description: 路由模式发送者
* @author: zhaohaiming
* @Version V1.0.0
* @create: 2019-08-17 00:11
**/
public class Send {
/**
* @Description: java类作用描述
* @Author: zhaohaiming
* @CreateDate: 2019/8/17 0:13
* @Version: 1.0
*/
private static final String QUEUE_NAME = "test_rout_name";
private static final String EXCHANF_NAME = "exchan_name";
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//Exchang
channel.exchangeDeclare(EXCHANF_NAME, "direct");//处理路由键
String smg = "send diret megges!";
String rountKey = "error";//路由key
//发送信息
channel.basicPublish(EXCHANF_NAME,rountKey,null,smg.getBytes());
System.out.println("send===>"+smg);
} catch (Exception e) {
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
}
}
消费者1
package routing;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @description: 路由模式消费者
* @author: zhaohaiming
* @Version V1.0.0
* @create: 2019-08-17 00:48
**/
public class Reception {
/**
* @Description: java类作用描述
* @Author: zhaohaiming
* @CreateDate: 2019/8/17 0:49
* @Version: 1.0
*/
private static final String QUEUE_NAME = "queue_rout_name";
private static final String EXCHANF_NAME = "exchan_name";
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//交换机与路由绑定
channel.queueBind(QUEUE_NAME,EXCHANF_NAME,"error");
channel.basicQos(1);
//获取消息
final Channel finalChannel = channel;
DefaultConsumer consumer = new DefaultConsumer(finalChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String smg = new String(body,"utf-8");
System.out.println("消费者接收消息:"+smg);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1111is down");
finalChannel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//监听队列
boolean auteAck = false ; //自动回答消息
channel.basicConsume(QUEUE_NAME,auteAck,consumer);
}catch (Exception e){
e.printStackTrace();
}
}
}
消费者2
package routing;
import cn.zhm.util.GetRabbitConnectUtil;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @description: 路由模式消费者
* @author: zhaohaiming
* @Version V1.0.0
* @create: 2019-08-17 00:48
**/
public class Reception2 {
/**
* @Description: java类作用描述
* @Author: zhaohaiming
* @CreateDate: 2019/8/17 0:49
* @Version: 1.0
*/
private static final String QUEUE_NAME = "queue_rout_name2";
private static final String EXCHANF_NAME = "exchan_name";
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection connection = null;
Channel channel = null;
try {
//获取一个连接
connection = GetRabbitConnectUtil.getMQConnection();
//从连接中获取一个通道
channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//交换机与路由绑定
channel.queueBind(QUEUE_NAME,EXCHANF_NAME,"error");
channel.queueBind(QUEUE_NAME,EXCHANF_NAME,"info");
channel.queueBind(QUEUE_NAME,EXCHANF_NAME,"warning");
channel.basicQos(1);
//获取消息
final Channel finalChannel = channel;
DefaultConsumer consumer = new DefaultConsumer(finalChannel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// super.handleDelivery(consumerTag, envelope, properties, body);
String smg = new String(body,"utf-8");
System.out.println("消费者接收消息:"+smg);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("22222is down");
finalChannel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//监听队列
boolean auteAck = false ; //自动回答消息
channel.basicConsume(QUEUE_NAME,auteAck,consumer);
}catch (Exception e){
e.printStackTrace();
}
}
}
运行结果
当发送是 error 时 消费1 和消费2都能接收信息
发送者发送信息:
send===>send diret megges!
消费1 收到消息:
消费者接收消息:send diret megges!
1111is down
消费2 收到消息:
消费者接收消息:send diret megges!
22222is down
当发送是 info 时 只有消费2能接收信息,消费1收不消息