目录
一、概念
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.交换机的持久化
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。
AMQP,即 Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ 是一个开源的 AMQP 实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。
RabbitMQ 通过虚拟主机(virtual host)来分发消息 ,拥有自己独立的权限控制, 不同vhost之间是隔离的、单独的 ,vhost是权限控制的基本单位,用户只能访问与之绑定的vhost ,默认vhost : “/” ,默认用户 “guest” ,默认密码 “guest” , 来访问默认的vhost .
生产Connection的工厂 .
是RabbitMQ的socket的长连接,它封装了socket的协议相关部分逻辑.
是建立在Connection连接之上的一种轻量级的连接,我们大部分的业务操作是Channel这个接口中完成的 ,包括定义队列的声明queueDirect 、交换机声明exchangeDeclare 、队列的绑定queueBind、发布消息的basicPublish、消费消息的basicConsume等 . 如果把Connection比作一条光纤电缆的话,那么Channel信道就比作成光纤电缆中其中的一束光纤 . 一个Connection可以创建任意数量的Channel 信道 .
生产者用于发布消息 .
生产者会将消息发送到交换机,然后交换机通过路由路由规则将消息路由到匹配到队列中 .
一个String,用于定义路由规则,在队列绑定的时候需要指定路由键,在生产者发布消息时需要指定路由键,当消息的路由键和队列绑定的路由键相匹配时,消息就会发送到该队列.
用于存储消息容器,可以看成一个有序的数组,生产者生产的消息会发送到交换机中,最终交换机将消息存储到某个或某些队列中,队列可被消费者订阅,消费者从订阅的队列中获取消息 .
Binding并不是一个概念,而是一种操作,RabbitMQ中通过绑定,以路由键作为桥梁将Exchange与Queue关联起来(Exchange –> RoutingKey –>Queue) ,这样RabbitMQ就知道如何正确地将消息路由到指定的队列了,通过queueBind方法Exchange、RoutingKey、Queue绑定起来 .
用于从队列中获取消息,消费者只需要关注队列即可,不需要关注交换机和路由键,消费者可以通过basicConsume(订阅模式可以从队列中一直持续的自动接收消息)或者basicGet(先订阅消息,然后获取单条消息,再然后取消订阅,也就是说basicGet一次只能获取一条消息,如果还想获取下一条还要再次调用basicGet来从队列中获取消息) .
交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout
如果路由键完全匹配的话,消息才会被投放到相应的队列
设置模糊的绑定方式,“*”操作符将“.”视为分隔符,匹配单个字符;“#”操作符没有分块的概念,它将任意“.”均视为关键字的匹配部分,能够匹配多个字符
当发送一条消息到fanout交换器上时,它会把消息投放到所有附加在此交换器上的队列。
headers 交换器允许匹配 AMQP 消息的 header 而非路由键,除此之外,header 交换器和 direct 交换器完全一致,但是性能却差很多,因此基本上不会用到该交换器,这里也不详细介绍。
一个生产者对应一个消费者
竞争消费者模式。
一个生产者对应多个消费者,但是只能有一个消费者获得消息
一个消费者将消息首先发送到交换器,交换器绑定到多个队列,然后被监听该队列的消费者所接收并消费。
ps:X表示交换器,在RabbitMQ中,交换器主要有四种类型:direct、fanout、topic、headers,这里的交换器是 fanout。下面我们会详细介绍这几种交换器。
生产者将消息发送到direct交换器,在绑定队列和交换器的时候有一个路由key,生产者发送的消息会指定一个路由key,那么消息只会发送到相应key相同的队列,接着监听该队列的消费者消费消息
也就是让消费者有选择性的接收消息
上面的路由模式是根据路由key进行完整的匹配(完全相等才发送消息),这里的通配符模式通俗的来讲就是模糊匹配
符号“#”表示匹配一个或多个词,符号“*”表示匹配一个词
①消息的确认
消费者有自动确认消息和手动确认消息两种模式:
channel.basicConsume(QUEUE_NAME,true,queueingConsumer);
true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息已经成功消费
false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态,并且服务器会认为该消费者已经挂掉,不会再给其发送消息,直到该消费者反馈。
②消费者每次消费一条消息
channel.basicQos(1);
表示同一时刻服务器只会发送一条消息给消费者
③交换机未绑定队列
如果消息发送到没有队列绑定的交换器时,消息将丢失,因为交换器没有存储消息的能力,消息只能存储在队列中
生产者:
①连接到MQ-->②获取信道-->③声明交换机-->④创建消息-->⑤发布消息-->⑥关闭信道-->⑦关闭连接
消费者:
①连接到MQ-->②获取信道-->③声明交换机-->④声明队列-->⑤把队列和交换机绑定-->⑥消费消息-->⑦关闭信道-->⑧关闭连接
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;
}
}
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();
}
}
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);
}
}
}
channel.exchangeDeclare(exchange, type, durable, autoDelete, arguments);
exchange:交换机名
type:交换机类型
durable:持久化标志
autoDelete:自动删除
arguments:扩展参数
channel.queueDeclare(queue, durable, exclusive, autoDelete, arguments) ;
queue:队列名字
durable:队列持久化标志,true为持久化队列
exclusive:exclusive:排他队列,如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:其一,排他队列是基于连接可见的,同一连接的不同信道是可以同时访问同一个连接创建的排他队列的。其二,“首次”,如果一个连接已经声明了一个排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同。其三,即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除的。这种队列适用于只限于一个客户端发送读取消息的应用场景。
autoDelete:自动删除,如果该队列没有任何订阅的消费者的话,该队列会被自动删除。这种队列适用于临时队列。
arguments:Map类型,关于队列及队列中消息的详细设置
channel.queueBind(queue, exchange, routingKey, arguments);
queue:队列名
exchange:交换机名
routingKey:选择键(路由键)
arguments:headers类型交换机绑定时指定的键值对
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:消息实体,字节数组。
QueueingConsumer:这是一个已经实现好了的Consumer,相比于自己实现Consumer接口,这是个比较安全快捷的方式。该类基于jdk的BlockingQueue实现,handleDelivery方法中将收到的消息封装成Delivery对象,并存放到BlockingQueue中,这相当于消费者本地存放了一个消息缓存队列。nextDelivery()方法底层调用的BlockingQueue的阻塞方法take()。
channel.basicConsume(queue, autoAck, consumer);
queue:队列名。
autoAck:自动应答标志,true为自动应答。
consumer:消费者对象,可以自己实现Consumer接口,建议使用QueueingConsumer。
只持久化队列,在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());
如果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