RabbitMQ核心组成部分
核心概念:
Server:又称Broker ,接受客户端的连接,实现AMQP实体服务。 安装rabbitmq-server
Connection:连接,应用程序与Broker的网络连接 TCP/IP/ 三次握手和四次挥手
Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户端可以建立对各Channel,每个Channel代表一个会话任务。
Message :消息:服务与应用程序之间传送的数据,由Properties和body组成,Properties可是对消息进行修饰,比如消息的优先级,延迟等高级特性,Body则就是消息体的内容。
Virtual Host 虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机理由可以有若干个Exhange和Queueu,同一个虚拟主机里面不能有相同名字的Exchange
Exchange:交换机,接受消息,根据路由键发送消息到绑定的队列。(==不具备消息存储的能力==)
Bindings:Exchange和Queue之间的虚拟连接,binding中可以保护多个routing key.
Routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息。
Queue:队列:也成为Message Queue,消息队列,保存消息并将它们转发给消费者。
simple模式
get message一定要选择Nack 如果选择ack会当成真实的消费 把队列的这条消息消费掉
RabbitMQ的运行流程
fanout模式
相比simple模式多了exchange
创建一个交换机fanout-demo type选择fanout
在bindings中绑定三个queue
通过交换机向三个queue发送消息
最后三条队列收到消息
routing模式
相比fanout模式多了一层路由key fanout中指定路由key没有意义
创建一个direct交换机
绑定三个队列 并给他们路由key
给路由key为vx的队列发送消息
可以看到只有2和3收到了消息
topic模式
在routing模式上新增了模糊匹配路由key
创建一个topic exchange type选择 topic
绑定队列
com.# #代表占位0个,1个或多个或多级 例如com com.xxxxxxx com.xxx.xxx
com.*代表占位至少一个并且只有一级 例如com.xxx
例如发送的路由key为com.course.order
那么队列1,2,3都能收到消息 切记#后面可以为空
如果发送的是com.course.order.user
那么只有队列1,3能收到消息 因为*只能有1级并且不能为空 所以队列2,4收不到消息
headers模式
type选择headers
给队列1和2绑定并给予参数
给参数x=1的队列发送消息
队列1收到消息
代码模板
Producer
public class Producer {
public static void main(String[] args) {
//1.创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//2.设置连接属性
connectionFactory.setHost("8.130.18.190");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection=null;
Channel channel=null;
try{
//3.从连接工厂中获取连接
connection=connectionFactory.newConnection("生产者");
//4.从连接中获取通道channel
channel=connection.createChannel();
//5.准备发送消息的内容
String message="ALL DIRECT";
String exchangeName="direct-all"; //声明一个不存在的交换机
String exchangeType="direct";
channel.exchangeDeclare(exchangeName,exchangeType,true);//创建一个交换机
//声明队列
/**
* @params1: queue 队列的名称
* @params2: durable 队列是否持久化
* @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
* @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
* @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
*/
channel.queueDeclare("queue04",true,false,false,null);
channel.queueDeclare("queue05",true,false,false,null);
channel.queueDeclare("queue06",true,false,false,null);
//绑定关系
channel.queueBind("queue04",exchangeName,"qq");
channel.queueBind("queue05",exchangeName,"tieba");
channel.queueBind("queue06",exchangeName,"qq");
// 6: 发送消息给中间件rabbitmq-server
// @params1: 交换机exchange
// @params2: 队列名称/routingkey
// @params3: 属性配置
// @params4: 发送消息的内容
String routingkey="qq";
channel.basicPublish(exchangeName,routingkey,null,message.getBytes());
System.out.println("发送成功");
}catch (Exception e){
e.printStackTrace();
System.out.println("发送出现错误");
}finally {
// 7: 释放连接关闭通道
if (channel!=null&&channel.isOpen()){
try{
channel.close();
}catch (Exception e){
e.printStackTrace();
}
try{
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
Consumer
public class Consumer {
private static Runnable runnable = () -> {
//1.创建连接工厂
ConnectionFactory connectionFactory=new ConnectionFactory();
//2.设置连接属性
connectionFactory.setHost("8.130.18.190");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
//获取队列的名称
final String queueName = Thread.currentThread().getName();
Connection connection = null;
Channel channel = null;
try {
// 3: 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 4: 从连接中获取通道channel
channel = connection.createChannel();
// 5: 申明队列queue存储消息
/*
* 如果队列不存在,则会创建
* Rabbitmq不允许创建两个相同的队列名称,否则会报错。
*
* @params1: queue 队列的名称
* @params2: durable 队列是否持久化
* @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
* @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
* @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
* */
// 这里如果queue已经被创建过一次了,可以不需要定义
//channel.queueDeclare("queue1", false, false, false, null);
// 6: 定义接受消息的回调
Channel finalChannel = channel;
finalChannel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String s, Delivery delivery) throws IOException {
System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String s) throws IOException {
}
});
System.out.println(queueName + ":开始接受消息");
System.in.read();
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("发送消息出现异常...");
} finally {
// 7: 释放连接关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
public static void main(String[] args) {
// 启动三个线程去执行
new Thread(runnable, "queue01").start();
new Thread(runnable, "queue02").start();
new Thread(runnable, "queue03").start();
new Thread(runnable, "queue04").start();
new Thread(runnable, "queue05").start();
new Thread(runnable, "queue06").start();
}
}
Work轮询模式
主要有两种模式:
1、轮询模式的分发:一个消费者一条,按均分配;
2、公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;
默认是轮询分发
#改用公平分发
finalChannel.basicQos(1); //表示每次取1条消息 建议不要设置太大 几十即可
finalChannel.basicConsume("queue01", false, new DeliverCallback() //@params2:autoAck改为false
#改为手动应答
finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
Linux中启动Docker容器报错:Error response from daemon: driver failed programming external connectivity
请使用以下命令重启docker
sudo systemctl restart docker