简介
RabbitMQ提供了6种工作模式:简单模式、work queues、Publish/Subscribe发布与订阅模式、Routing路由模式、Topics主题模式、RPC远程调用模式(远程调用,不太算MQ;暂不作介绍)。
官网对应模式介绍:https://www.rabbitmq.com/getstarted.html
RabbitMQ 基础架构:
RabbitMQ相关概念:
- Broker:接收和分发消息的应用,RabbitMQ Server就是 Message Broker
- Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个vhost,每个用户在自己的 vhost 创建 exchange/queue 等
- Connection:publisher/consumer 和 broker 之间的 TCP 连接
- Channel:如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection的开销将是巨大的,效率也较低。Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的 channel 进行通讯,AMQP method 包含了channel id 帮助客户端和message broker 识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection 极大减少了操作系统建立 TCP connection 的开销
- Exchange:message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)
- Queue:消息最终被送到这里等待 consumer 取走
Binding:exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据
六种模式入门:
简单模式
上图的模型中,有以下概念:P:生产者,也就是要发送消息的程序
- C:消费者:消息的接收者,会一直等待消息到来
- queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息
maven坐标
<dependencies>
<!--rabbitmq java 客户端-->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.6.0</version>
</dependency>
</dependencies>
Java代码
//生产者
public class Producer_HelloWord {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.创建工厂
ConnectionFactory factory = new ConnectionFactory();
// 2.设置参数
factory.setHost("192.168.31.128"); //默认ip localhost
factory.setPort(5672); //默认端口 5672
factory.setVirtualHost("/"); //虚拟机 默认是/
factory.setUsername("guest"); //用户名 默认guest
factory.setPassword("guest"); //密码 默认guest
// 3.创建连接 Connection
Connection connection = factory.newConnection();
// 4.创建Channel
Channel channel = connection.createChannel();
// 5.创建消息队列queue
/*
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
参数:
1. queue:队列名称
2. durable:是否持久化,当mq重启之后,还在
3. exclusive:
* 是否独占。只能有一个消费者监听这队列
* 当Connection关闭时,是否删除队列
*
4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
5. arguments:参数。
*/
//如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
channel.queueDeclare("hello_world",true,false,false,null);
String body = "hello RabbitMQ~~";
// 6.发送消息
channel.basicPublish("","hello_world",null,body.getBytes());
// 7.关闭
channel.close();
connection.close();
}
}
-------------------------------------------------------------------------------------------------------------------
//消费者
public class Consumer_HelloWord {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2. 设置参数
factory.setHost("192.168.31.128"); //默认ip localhost
factory.setPort(5672); //默认端口 5672
factory.setVirtualHost("/"); //虚拟机 默认是/
factory.setUsername("guest"); //用户名 默认guest
factory.setPassword("guest"); //密码 默认guest
//3. 创建连接 Connection
Connection connection = factory.newConnection();
//4. 创建Channel
Channel channel = connection.createChannel();
//5. 创建队列Queue
/*
queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
参数:
1. queue:队列名称
2. durable:是否持久化,当mq重启之后,还在
3. exclusive:
* 是否独占。只能有一个消费者监听这队列
* 当Connection关闭时,是否删除队列
*
4. autoDelete:是否自动删除。当没有Consumer时,自动删除掉
5. arguments:参数。
*/
//如果没有一个名字叫hello_world的队列,则会创建该队列,如果有则不会创建
channel.queueDeclare("hello_world", true, false, false, null);
/*
basicConsume(String queue, boolean autoAck, Consumer callback)
参数:
1. queue:队列名称
2. autoAck:是否自动确认
3. callback:回调对象
*/
// 接收消息
Consumer consumer = new DefaultConsumer(channel) {
/*
回调方法,当收到消息后,会自动执行该方法
1. consumerTag:标识
2. envelope:获取一些信息,交换机,路由key...
3. properties:配置信息
4. body:数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("consumerTag:" + consumerTag);
System.out.println("Exchange:" + envelope.getExchange());
System.out.println("RoutingKey:" + envelope.getRoutingKey());
System.out.println("properties:" + properties);
System.out.println("body:" + new String(body));
}
};
channel.basicConsume("hello_world", true, consumer);
//关闭资源?不要
}
}
工作队列
- Work Queues:与入门程序的简单模式相比,多了一个或一些消费端,多个消费端共同消费同一个队列中的消息。
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
订阅模式
在订阅模型中,多了一个 Exchange 角色,而且过程略有变化:P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- C:消费者,消息的接收者,会一直等待消息到来
- Queue:消息队列,接收消息、缓存消息
- Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下3种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列
- Direct:定向,把消息交给符合指定routing key 的队列
- Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失!
Java代码
//生产者
public static void main(String[] args) throws IOException, TimeoutException {
// 1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2.设置参数
factory.setHost("192.168.31.128"); //默认ip localhost
factory.setPort(5672); //默认端口 5672
factory.setVirtualHost("/"); //虚拟机 默认是/
factory.setUsername("guest"); //用户名 默认guest
factory.setPassword("guest"); //密码 默认guest
// 3.创建连接
Connection connection = factory.newConnection();
// 4.创建Channel
Channel channel = connection.createChannel();
// 5.创建交换机
/*
exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
参数:
1. exchange:交换机名称
2. type:交换机类型
DIRECT("direct"),:定向
FANOUT("fanout"),:扇形(广播),发送消息到每一个与之绑定队列。
TOPIC("topic"),通配符的方式
HEADERS("headers");参数匹配
3. durable:是否持久化
4. autoDelete:自动删除
5. internal:内部使用。 一般false
6. arguments:参数
*/
String exchange="test_fanout";
channel.exchangeDeclare(exchange, BuiltinExchangeType.FANOUT,true,false,false,null);
// 6.创建队列
String queue1Name="test_fanout_queue1";
String queue2Name="test_fanout_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
// 7.绑定队列和交换机
/*
queueBind(String queue, String exchange, String routingKey)
参数:
1. queue:队列名称
2. exchange:交换机名称
3. routingKey:路由键,绑定规则
如果交换机的类型为fanout ,routingKey设置为""
*/
channel.queueBind(queue1Name,exchange,"");
channel.queueBind(queue2Name,exchange,"");
String body = "日志信息:小张查询了近15天的销量";
// 8.发消息
channel.basicPublish(exchange,"",null,body.getBytes());
// 9.释放资源
channel.close();
connection.close();
}
----------------------------------------------------------------------------------------
//消费者
public static void main(String[] args) throws IOException, TimeoutException {
// 1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2.设置参数
factory.setHost("192.168.31.128"); //默认ip localhost
factory.setPort(5672); //默认端口 5672
factory.setVirtualHost("/"); //虚拟机 默认是/
factory.setUsername("guest"); //用户名 默认guest
factory.setPassword("guest"); //密码 默认guest
// 3.创建连接
Connection connection = factory.newConnection();
// 4.创建Channel
Channel channel = connection.createChannel();
// 5.创建队列
channel.queueDeclare("test_fanout_queue2",true,false,false,null);
// 6.接收消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:" + new String(body));
System.out.println("我是负责存入数据库的");
}
};
channel.basicConsume("test_fanout_queue2",true,consumer);
}
Rotting路由模式
- P:生产者,向 Exchange 发送消息,发送消息时,会指定一个routing key
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与 routing key 完全匹配的队列
- C1:消费者,其所在队列指定了需要 routing key 为 error 的消息
- C2:消费者,其所在队列指定了需要 routing key 为 info、error、warning 的消息
```java
//生产者
public static void main(String[] args) throws IOException, TimeoutException {
// 1.创建工厂
// 2.设置参数ConnectionFactory factory = new ConnectionFactory();
// 3.创建连接//2. 设置参数
factory.setHost("172.16.98.133");//ip 默认值 localhost
factory.setPort(5672); //端口 默认值 5672
factory.setVirtualHost("/itcast");//虚拟机 默认值/
factory.setUsername("heima");//用户名 默认 guest
factory.setPassword("heima");//密码 默认值 guest
// 4.创建channelConnection connection = factory.newConnection();
// 5.创建交换机Channel channel = connection.createChannel();
// 7.绑定交换机String exchangeName = "test_direct";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,true,false,false,null);
//6. 创建队列
String queue1Name = "test_direct_queue1";
String queue2Name = "test_direct_queue2";
channel.queueDeclare(queue1Name,true,false,false,null);
channel.queueDeclare(queue2Name,true,false,false,null);
// 8.发送信息channel.queueBind(queue1Name,exchangeName,"error");
channel.queueBind(queue1Name,exchangeName,"warning");
channel.queueBind(queue1Name,exchangeName,"info");
channel.queueBind(queue1Name,exchangeName,"error");
String body = "日志信息:张三调用了delete方法...出错误了。。。日志级别:error...";
// 9.释放资源channel.basicPublish(exchangeName,"warning",null,body.getBytes());
}channel.close();
connection.close();
//消费者
public static void main(String[] args) throws IOException, TimeoutException {
// 1.创建工厂 ConnectionFactory factory = new ConnectionFactory(); // 2.设置参数 factory.setHost(“192.168.31.128”);//ip 默认值 localhost factory.setPort(5672); //端口 默认值 5672 factory.setVirtualHost(“/“);//虚拟机 默认值/ factory.setUsername(“guest”);//用户名 默认 guest factory.setPassword(“guest”);//密码 默认值 guest // 3. 创建连接 Connection Connection connection = factory.newConnection(); // 4.创建channel Channel channel = connection.createChannel(); // 5.接收消息 String queue1Name = “test_direct_queue1”; String queue2Name = “test_direct_queue2”;
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("body:"+new String(body));
System.out.println("将日志信息打印到控制台.....");
}
};
channel.basicConsume(queue2Name,true,consumer);
}
Topic通配符模式
- 红色 Queue:绑定的是 usa.# ,因此凡是以 usa. 开头的 routing key 都会被匹配到
- 黄色 Queue:绑定的是 #.news ,因此凡是以 .news 结尾的 routing key 都会被匹配
°通配符*代表一个单词
°通配符#代表0~多个单词