一.RabbitMQ简介
1.rabbitmq的工作模式:
(1)Broker
RabbitMQ服务器,帮助我们存储、转发消息的代理/中介。
(2)Connection
生产者发送消息/消费者接收消息,都需要跟Broker之间建立一个TCP的长连接
(3)channel
所有的生产者发送消息和消费者接收消息,都直接创建和释放TCP长连接的话,对于Broker来说会造成很大的性能损耗。因此引入了Channel(通道)的概念,它是一个虚拟的连接,这样我们就可以在保持的TCP长连接里去创建和释放Channel,大大减少了资源消耗。
(4)Queue
queue(队列)真正用来存储消息的,是一个独立运行的进程,有自己的数据库(Mnesia)。
消费者获取消息有两种模式,一种是Push,只要生产者发到服务器,就马上推送给消费者。另一种是Pull,消息存放在服务端,只有消费者主动获取才能拿到消息。消费者不需要循环不断地从队列获取消息,可以基于事件机制,实现消费者对队列的监听。由于队列有FIFO的特性,只有确定前一条消息被消费者接收之后,才会把这条消息从数据库删除,继续投递下一条消息。
(5)Exchange
在 RabbitMQ 里消息不会直接发送到队列的。而是通过交换机(Exchange)用来实现消息的灵活路由。交换机是一个绑定列表,用来查找匹配的绑定关系。队列使用绑定键(Binding Key)跟交换机建立绑定关系。生产者发送的消息需要携带路由键,交换机收到消息时会根据它保存的绑定列表,决定将消息路由到哪些与它绑定的队列上。
ps:交换机与队列、队列与消费者都是多对多的关系。
(6)Vhost
虚拟主机VHOST。VHOST 可以提高硬件资源的利用率之外,实现资源的隔离和权限的控制。不同的 VHOST中可以有同名的Exchange和Queue,它们是完全透明的。可以使自己的业务系统不跟别人混用一个系统。
2.路由方式
(1)直连 Direct
队列与直连类型的交换机绑定,需指定一个精确的绑定键。生产者发送消息时会携带一个路由键。只有当路由键与其中的某个绑定键完全匹配时,这条消息才会从交换机路由到满足路由关系的此队列上。
channel.basicPublish(“MY_DIRECT_EXCHANGE”,”spring”,”msg1”); 只有第一个队列能收到消息。
(2)主题 Topic
队列与主题类型的交换机绑定时,可以在绑定键中使用通配符。两个通配符:# 0个或者多个单词,* 不多不少一个单词单词,单词用英文的点“.”隔开的字符。例如abc.def是两个单词。
第一个队列支持路由键以junior 开头的消息路由,后面可以有单词,也可以没有。
第二个队列支持路由键以netty开头,并且后面是一个单词的消息路由。
第三个队列支持路由键以jvm结尾,并且前面是一个单词的消息路由
channel.basicPublish("MY_TOPIC_EXCHANGE","junior.fjd.klj","msg 2"); 只有第一个队列能收到消息。
channel.basicPublish("MY_TOPIC_EXCHANGE","junior.jvm", "msg 3"); 第一个队列和第三个队列能收到消息
(3)广播 Fanout
不需要指定绑定键。因此生产者发送消息到广播类型的交换机上,也不需要携带路由键。消息达到交换机时,所有与之绑定了的队列,都会收到相同的消息的副本。
channel.basicPublish("MY_FANOUT_EXCHANGE","","msg 4"); 三个队列都会收到 msg 4。
一.简单使用
1.创建Maven工程,pom.xml引入依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
2.生产者
public class MyProducer {
private final static String EXCHANGE_NAME = "SIMPLE_EXCHANGE";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
// 连接 IP
factory.setHost("127.0.0.1");
// 连接端口
factory.setPort(5672);
// 虚拟机
factory.setVirtualHost("/");
// 用户
factory.setUsername("guest");
factory.setPassword("guest");
// 建立连接
Connection conn = factory.newConnection();
// 创建消息通道
Channel channel = conn.createChannel();
// 发送消息
String msg = "HelloWorld";
channel.basicPublish(EXCHANGE_NAME, "test.best", null, msg.getBytes());
channel.close();
conn.close();
}
}
3.消费者
public class MyConsumer {
private final static String EXCHANGE_NAME = "SIMPLE_EXCHANGE";
private final static String QUEUE_NAME = "SIMPLE_QUEUE";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest");
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
// 声明交换机(参数1.交换机名
// 2.交换机类型:direct,topic,fanout.
// 3.是否持久化,代表交换机在服务器重启后是否还存在
// 4.是否自动删除,设置为TRUE则表是自动删除,自删除的前提是至少有一个队列或者交
// 换器与这交换器绑定,之后所有与这个交换器绑定的队列或者交换器都与此解绑.
// 5.其它一些结构化参数比如:alternate-exchange)
channel.exchangeDeclare(EXCHANGE_NAME,"direct",false, false, null);
// 声明队列(参数1 队列名
// 2是否持久化,代表队列在服务器重启后是否还存在
// 3是否排他性队列。排他性队列只能在声明它的 Connection中使用(可以在同一个
// Connection 的不同的 channel 中使用),连接断开时自动删除。
// 4.是否自动删除。如果为 true,至少有一个消费者连接到这个队列,之后所有与这个队列
// 连接的消费者都断开时,队列会自动删除。
// 5.队列的其他属性
// )
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" Waiting for message....");
// 绑定队列和交换机
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"test.best");
// 创建消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
System.out.println("Received message : '" + msg + "'");
System.out.println("consumerTag : " + consumerTag );
System.out.println("deliveryTag : " + envelope.getDeliveryTag());
}
};
// 开始获取消息
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
5.打开控制台localhost:15672.可观察队列内消息的动态变化