目录
一、概念
1.RabbitMQ
2.AMQP
3.RabbitMQ与AMQP
二、RabbitMQ结构
1. vhost(虚拟主机) :
2.ConnectionFactory(连接工厂) :
3.Connection(连接) :
4.Channel(信道) :
5.Producet(生产者) :
6.Exchange(交换机):
7.RoutingKey(路由键) :
8.Queue(队列) :
9.Binding(绑定) :10.Consumer(消费者) :
三、交换机类型
1. Direct Exchange
2. Topic Exchange
3.Fanout Exchange
4. Headers Exchange
四、五种队列
1.简单队列
2.work模式
3.发布/订阅模式
4.路由模式
5.主题模式
6.注意
五、生产者与消费者的生命周期
六、代码示例
1.pom文件
2.连接工具类
3. 生产者
4.消费者
七、API介绍
1.声明交换机
2.声明队列
3.绑定队列
4.推送消息
5.消费
6.指定消费队列
八、消息持久化
1.队列与消息的持久化
2.交换机的持久化
一、概念
1.RabbitMQ
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。
2.AMQP
AMQP,即 Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
3.RabbitMQ与AMQP
RabbitMQ 是一个开源的 AMQP 实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
二、RabbitMQ结构
通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。
1. vhost(虚拟主机) :
RabbitMQ 通过虚拟主机(virtual host)来分发消息 ,拥有自己独立的权限控制, 不同vhost之间是隔离的、单独的 ,vhost是权限控制的基本单位,用户只能访问与之绑定的vhost ,默认vhost : “/” ,默认用户 “guest” ,默认密码 “guest” , 来访问默认的vhost .
2.ConnectionFactory(连接工厂) :
生产Connection的工厂 .
3.Connection(连接) :
是RabbitMQ的socket的长连接,它封装了socket的协议相关部分逻辑.
4.Channel(信道) :
是建立在Connection连接之上的一种轻量级的连接,我们大部分的业务操作是Channel这个接口中完成的 ,包括定义队列的声明queueDirect 、交换机声明exchangeDeclare 、队列的绑定queueBind、发布消息的basicPublish、消费消息的basicConsume等 . 如果把Connection比作一条光纤电缆的话,那么Channel信道就比作成光纤电缆中其中的一束光纤 . 一个Connection可以创建任意数量的Channel 信道 .
5.Producet(生产者) :
生产者用于发布消息 .
6.Exchange(交换机):
生产者会将消息发送到交换机,然后交换机通过路由路由规则将消息路由到匹配到队列中 .
7.RoutingKey(路由键) :
一个String,用于定义路由规则,在队列绑定的时候需要指定路由键,在生产者发布消息时需要指定路由键,当消息的路由键和队列绑定的路由键相匹配时,消息就会发送到该队列.
8.Queue(队列) :
用于存储消息容器,可以看成一个有序的数组,生产者生产的消息会发送到交换机中,最终交换机将消息存储到某个或某些队列中,队列可被消费者订阅,消费者从订阅的队列中获取消息 .
9.Binding(绑定) :
Binding并不是一个概念,而是一种操作,RabbitMQ中通过绑定,以路由键作为桥梁将Exchange与Queue关联起来(Exchange –> RoutingKey –>Queue) ,这样RabbitMQ就知道如何正确地将消息路由到指定的队列了,通过queueBind方法Exchange、RoutingKey、Queue绑定起来 .
10.Consumer(消费者) :
用于从队列中获取消息,消费者只需要关注队列即可,不需要关注交换机和路由键,消费者可以通过basicConsume(订阅模式可以从队列中一直持续的自动接收消息)或者basicGet(先订阅消息,然后获取单条消息,再然后取消订阅,也就是说basicGet一次只能获取一条消息,如果还想获取下一条还要再次调用basicGet来从队列中获取消息) .
三、交换机类型
交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout
1. Direct Exchange
如果路由键完全匹配的话,消息才会被投放到相应的队列
2. Topic Exchange
设置模糊的绑定方式,“*”操作符将“.”视为分隔符,匹配单个字符;“#”操作符没有分块的概念,它将任意“.”均视为关键字的匹配部分,能够匹配多个字符
3.Fanout Exchange
当发送一条消息到fanout交换器上时,它会把消息投放到所有附加在此交换器上的队列。
4. Headers Exchange
headers 交换器允许匹配 AMQP 消息的 header 而非路由键,除此之外,header 交换器和 direct 交换器完全一致,但是性能却差很多,因此基本上不会用到该交换器,这里也不详细介绍。
四、五种队列
1.简单队列
一个生产者对应一个消费者
2.work模式
竞争消费者模式。
Direct:direct 类型的行为是"先匹配, 再投送". 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去.
Topic:按规则转发消息(最灵活)
Headers:设置 header attribute 参数类型的交换机
Fanout:转发消息到所有绑定队列
一个生产者对应多个消费者,但是只能有一个消费者获得消息
3.发布/订阅模式
一个消费者将消息首先发送到交换器,交换器绑定到多个队列,然后被监听该队列的消费者所接收并消费。
ps:X表示交换器,在RabbitMQ中,交换器主要有四种类型:direct、fanout、topic、headers,这里的交换器是 fanout。下面我们会详细介绍这几种交换器。
4.路由模式
生产者将消息发送到direct交换器,在绑定队列和交换器的时候有一个路由key,生产者发送的消息会指定一个路由key,那么消息只会发送到相应key相同的队列,接着监听该队列的消费者消费消息
也就是让消费者有选择性的接收消息
5.主题模式
上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配
符号“#”表示匹配一个或多个词,符号“*”表示匹配一个词
6.注意
①消息的确认
消费者有自动确认消息和手动确认消息两种模式:
channel.basicConsume(QUEUE_NAME,true,queueingConsumer);
true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息已经成功消费
false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态,并且服务器会认为该消费者已经挂掉,不会再给其发送消息,直到该消费者反馈。
②消费者每次消费一条消息
channel.basicQos(1);
表示同一时刻服务器只会发送一条消息给消费者
③交换机未绑定队列
如果消息发送到没有队列绑定的交换器时,消息将丢失,因为交换器没有存储消息的能力,消息只能存储在队列中
五、生产者与消费者的生命周期
生产者:
①连接到MQ-->②获取信道-->③声明交换机-->④创建消息-->⑤发布消息-->⑥关闭信道-->⑦关闭连接
消费者:
①连接到MQ-->②获取信道-->③声明交换机-->④声明队列-->⑤把队列和交换机绑定-->⑥消费消息-->⑦关闭信道-->⑧关闭连接
六、代码示例
1.pom文件
2.连接工具类
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtil {
public static Connection getConnection(String host,int port,String vHost,String
userName,String passWord) throws Exception{
//1、定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2、设置服务器地址
factory.setHost(host);
//3、设置端口
factory.setPort(port);
//4、设置虚拟主机、用户名、密码
factory.setVirtualHost(vHost);
factory.setUsername(userName);
factory.setPassword(passWord);
//5、通过连接工厂获取连接
Connection connection = factory.newConnection();
return connection;
}
}
3. 生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.ys.utils.ConnectionUtil;
/**
* Create by YSOcean
*/
public class Producer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception{
//1、获取连接
Connection connection = ConnectionUtil.getConnection("192.168.146.251",5672,"/","guest","guest");
//2、声明信道
Channel channel = connection.createChannel();
//3、声明(创建)队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//4、定义消息内容
String message = "hello rabbitmq ";
//5、发布消息
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
System.out.println("[x] Sent'"+message+"'");
//6、关闭通道
channel.close();
//7、关闭连接
connection.close();
}
}
4.消费者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;
import com.ys.utils.ConnectionUtil;
/**
* Create by YSOcean
*/
public class Consumer1 {
private final static String QUEUE_NAME = "work_queue";
public static void main(String[] args) throws Exception{
//1、获取连接
Connection connection = ConnectionUtil.getConnection("192.168.146.251",5672,"/","guest","guest");
//2、声明通道
Channel channel = connection.createChannel();
//3、声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//同一时刻服务器只会发送一条消息给消费者
//channel.basicQos(1);
//4、定义队列的消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//5、监听队列,手动返回完成状态
channel.basicConsume(QUEUE_NAME,false,queueingConsumer);
//6、获取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
//消费者1接收一条消息后休眠10毫秒
Thread.sleep(10);
//返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
}
七、API介绍
1.声明交换机
channel.exchangeDeclare(exchange, type, durable, autoDelete, arguments);
exchange:交换机名
type:交换机类型
durable:持久化标志
autoDelete:自动删除
arguments:扩展参数
2.声明队列
channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments) ;
queue:队列名字
durable:队列持久化标志,true为持久化队列
exclusive:exclusive:排他队列,如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:其一,排他队列是基于连接可见的,同一连接的不同信道是可以同时访问同一个连接创建的排他队列的。其二,“首次”,如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同。其三,即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除的。这种队列适用于只限于一个客户端发送读取消息的应用场景。
autoDelete:自动删除,如果该队列没有任何订阅的消费者的话,该队列会被自动删除。这种队列适用于临时队列。
arguments:Map类型,关于队列及队列中消息的详细设置
3.绑定队列
channel.queueBind(queue, exchange, routingKey, arguments);
queue:队列名
exchange:交换机名
routingKey:选择键(路由键)
arguments:headers类型交换机绑定时指定的键值对
4.推送消息
channel.basicPublish(exchange, routingKey, mandatory, immediate, basicProperties, body);
exchange: 交换机名
routingKey: 路由键
mandatory:当mandatory标志位设置为true时,如果exchange根据自身类型和消息routeKey无法找到一个符合条件的queue,那么会调用basic.return方法将消息返还给生产者, channel.addReturnListener添加一个监听器,当broker执行basic.return方法时,会回调handleReturn方法,这样我们就可以处理变为死信的消息了;当mandatory设为false时,出现上述情形broker会直接将消息扔掉;通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则就将消息return给发送者。
immediate: rabbitMq3.0已经不在支持了,3.0以前这个标志告诉服务器如果该消息关联的queue上有消费者,则马上将消息投递给它,如果所有queue都没有消费者,直接把消息返还给生产者,不用将消息入队列等待消费者了。
basicProperties:消息的详细属性,例如优先级别、持久化、到期时间,headers类型的exchange要用到的是其中的headers字段。
body:消息实体,字节数组。
5.消费
QueueingConsumer:这是一个已经实现好了的Consumer,相比于自己实现Consumer接口,这是个比较安全快捷的方式。该类基于jdk的BlockingQueue实现,handleDelivery方法中将收到的消息封装成Delivery对象,并存放到BlockingQueue中,这相当于消费者本地存放了一个消息缓存队列。nextDelivery()方法底层调用的BlockingQueue的阻塞方法take()。
6.指定消费队列
channel.basicConsume(queue, autoAck, consumer);
queue:队列名。
autoAck:自动应答标志,true为自动应答。
consumer:消费者对象,可以自己实现Consumer接口,建议使用QueueingConsumer。
八、消息持久化
1.队列与消息的持久化
只持久化队列,在rabbitmq崩溃时也能保存队列, 但不能持久化消息, 消息持久化需要在持久化队列后, 发送消息时持久化消息
①队列持久化设置
channel.queueDeclare("queue.persistent.name", true, false, false, null);
关键的是第二个参数设置为true,即durable=true.
②消息持久化设置
channel.basicPublish("exchange.persistent", "persistent", MessageProperties.PERSISTENT_TEXT_PLAIN, "persistent_test_message".getBytes());
这里的关键是:MessageProperties.PERSISTENT_TEXT_PLAIN
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
builder.deliveryMode(2);
AMQP.BasicProperties properties = builder.build();
channel.basicPublish("exchange.persistent", "persistent",properties, "persistent_test_message".getBytes());
2.交换机的持久化
如果exchange不设置持久化,那么当broker服务重启之后,exchange将不复存在,那么既而发送方rabbitmq producer就无法正常发送消息
channel.exchangeDeclare(exchangeName, “direct/topic/header/fanout”, true);即在声明的时候讲durable字段设置为true即可。
参考文章:
https://www.cnblogs.com/ysocean/p/9251884.html
https://blog.csdn.net/u013256816/article/details/60875666/
springboot整合RabbitMQ参考:https://www.cnblogs.com/ityouknow/p/6120544.html
windows安装RabbitMQ参考:https://blog.csdn.net/qq_37653556/article/details/82730522?utm_source=copy
————————————————
版权声明:本文为CSDN博主「M_azed」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m_azed/article/details/90673736