一、基础入门
1.1、介绍
MQ全称Message Queue (消息队列), 是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。
其功能主要如图
1.2 MQ的优劣势
1、优势:
- 应用解耦
- 异步提速
- 削峰填谷
(1) 应用解耦举例
系统的耦合性越高,容错性就越低,可维护性就越低
如图,当库存系统挂掉,订单系统也会相应的失效。当要新增X系统时,又需要修改订单系统的详细代码。
使用MQ使得应用间解耦,提升容错性和可维护性
如图,当订单系统执行时,仅需发一条消息给MQ就行了,后面的就不需要管了,然后返回下蛋成功,后面的系统只需要去MQ拿订单消息去自己系统处理。提高系统的容错性。
(2)异步提速举例
用户点击完下单按钮后,需要等待920ms才能得到下单响应,影响用户体验。
提升用户体验和系统吞吐量( 单位时间内处理请求的数目)。
(3)削峰填谷举例
削峰
填谷
使用了MQ之后,限制消费消息的速度为1000,这样一来, 高峰期产生的数据势必会被积压在MQ中,高峰就被”削”掉了,但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000,直到消费完积压的消息,这就叫做“填谷”。
2、劣势:
- 系统可用性降低
- 系统复杂度提高
- 一致性问题
(1)系统可用性
系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。如何保证MQ的高可用,成为该系统的一个重点。
(2)系统复杂性
MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。如何保证消息没有被重复消费,怎么处理消息丢失情况?那么保证消息传递的顺序性,成了系统设计的一个重点
(3)一致性
A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。这个问题怎么处理成为难点重点。
3、使用MQ的条件
(1) 生产者不需要从消费者处获得反馈。引入消息队列之前的直接调用,其接口的返回值应该为空。
(2) 容许短暂的不一致性。
(3) 确实是用了有效果。即解耦、提速、削峰这些方面的收益,超过加入MQ,管理MQ这些成本。
1.3、常见MQ产品对比
目前业界有很多的MQ产品,例如RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、 MetaMq等,也有直接使用Redis充当消息队列的案例,而这些消息队列产品,各有侧重,在实际选型时,需要结合自身需求极MQ产品特征,综合考虑。
二、RabbitMQ基础
2.1、AMQP简介
RabbitMQ是基于AMQP(Advanced Message Queuing Protocol (高级消息队列协议)),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布。
Publisher 生产者,发布者。
Exchange是交换机,用于分发消息,通过Routes路由将消息分发给Queue。
Queue是存储消息的
Consumer 消费者
整体流程:
生产者发布消息到Exchange,Exchange通过不同的规则(路由)将消息发送到不同的Queue存储,然后Consumer 从Queue中监听拿走消息去消费。
2.2、RabbitMQ简介
2007年,Rabbit 技术公司基于AMQP标准开发的RabbitMQ 1.0发布。RabbitMQ 采用Erlang语言开发。Erlang语言由Ericson 设计,专门为开发高并发和分布式系统的一种语言,在电信领域使用广泛。
rabbitMQ基础架构
- Broker:中间/中介,接收和分发消息的应用,是rabbitMQ的服务层。RabbitMQ Server就是Message Broker
- Vrtual Host:虚拟机(逻辑分区的概念),每个虚拟机之间 相互隔离,一个虚拟机包含有多个Exchange和Queue。出于多租户和安全因素设计的,把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的分发依据。
2.3、工作模式
RabbitMQ提供了6种工作模式:简单模式、work queues、Publish/Subscribe 发布与订阅模式、Routing路由模式、Topics 主题模式、RPC 远程调用模式(远程调用,不太算MQ)。
(1)简单模式
在上图的模型中,有以下概念:
- P:生产者,也就是要发送消息的程序。
- C:消费者,消息的接收者,会-直等待消息到来。
- queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息。
代码示例
创建生产者
1.创建连接工厂
2.设置参数
3.创建连接Connection
4.创建Channel
5.创建队列
6.发送消息
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxx");//默认值 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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
channel.queueDeclare("rabbit1", false, false, false, null);
//6.发送消息
//public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, AMQP.BasicProperties props, byte[] body)
//exchange: 交换机名称,简单模式下交换机会使用默认的
//routingKey: 路由名称。
//props: 配置信息。
//body: 发送消息数据。
String body = "你好";
/*注意,如果使用简单模式,交换机置为空并且routingKey和队列名相同*/
channel.basicPublish("", "rabbit1", null, body.getBytes());
//释放资源
channel.close();
connection.close();
}
创建消费者
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxx");//默认值 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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
channel.queueDeclare("rabbit1", false, false, false, null);
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
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));
System.out.println("==================================================================");
}
};
channel.basicConsume("rabbit1", true, consumer);
}
(2)Work queues工作队列模式
- Work Queues:与入门程序的简单模式相比,多了一个或一些消费端, 多个消费端共同消费同一个队列中的消息。
- 应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
1.在一个队列中如果有多个消费者,那么消费者之间对于同一个消息的关系是竞争的关系。
2. Work Queues对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。例如:短信服务部署多个,只需要有一个节点成功发送即可。
代码示例
生产者
同上简单模式生产者
消费者
同简单模式消费者,复制一份就行。
(3)Pub/Sub订阅模式
在订阅模型中,多了一个Exchange角色,而且过程略有变化:
- P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X (交换机)
- C:消费者,消息的接收者,会- -直等待消息到来
- Queue:消息队列,接收消息、缓存消息
- Exchange:交换机(X) ,一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
- 常见 Exchange3种类型:
1、Fanout:广播,将消息交给所有绑定到交换机的队列。
2、Direct:定向,把消息交给符合指定routing key的队列。
3、Topic:通配符,把消息交给符合routing pattern (路由模式) 的队列。
Exchange (交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失。
代码示例
生产者
package org.example;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxxx");//默认值 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.创建交换机
//exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map<String, Object> arguments)
//exchange:交换机名称
//type:交换机类型
//DIRECT("direct"); 定向
//FANOUT("fanout"); 扇形(广播) 发送消息到每一个与之绑定队列。
//TOPIC("topic"); 通配符匹配
//HEADERS("headers");参数匹配
//durable:是否持久化。
//autoDelete:自动删除。
//internal: 内部使用,一般false。
//arguments:参数。
String exchangeName = "test_num1";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);
//6.创建队列
String queue1Name = "test_num1_queue1";
String queue2Name = "test_num1_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, exchangeName, "");
channel.queueBind(queue2Name, exchangeName, "");
//8.发送消息
String body = "私聊信息";
channel.basicPublish(exchangeName, "", null, body.getBytes());
//9.释放资源
channel.close();
connection.close();
}
}
消费者
消费者1
package org.example;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class MyConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxxx");//默认值 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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
String queenName1 = "test_num1_queue1";
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("==================================================================");
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));
System.out.println(queenName1 + ": '执行了'");
System.out.println("==================================================================");
}
};
channel.basicConsume(queenName1, true, consumer);
}
}
消费者2
就把queenName1 改成 test_num1_queue2 就行
(3)Routing路由模式
模式介绍
- 队列与交换机的绑定,不能是任意绑定了,而是要指定-个RoutingKey (路由key)。
- 消息的发送方在向 Exchange发送消息时,也必须指定消息的RoutingKey。
- Exchange 不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的Routing key完全一致,才会接收到消息。
代码示例
生产者
package org.example.routing;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxxx");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值guest
//3.创建连接Connection
Connection connection = factory.newConnection();
//4.创建Channel
Channel channel = connection.createChannel();
//5.创建交换机
//exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map<String, Object> arguments)
//exchange:交换机名称
//type:交换机类型
//DIRECT("direct"); 定向
//FANOUT("fanout"); 扇形(广播) 发送消息到每一个与之绑定队列。
//TOPIC("topic"); 通配符匹配
//HEADERS("headers");参数匹配
//durable:是否持久化。
//autoDelete:自动删除。
//internal: 内部使用,一般false。
//arguments:参数。
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);
//7.绑定队列和交换机
//queueBind(string queue, String exchange, string routingKey)
//1. queue: 队列名称
//2. exchange: 交换机名称
//3. routingKey: 路由键,绑定规则。 如果交换机的类型为fanout,routingKey设置为空
//queue1Name绑定error
channel.queueBind(queue1Name, exchangeName, "error");
//queue2Name绑定info warring error
channel.queueBind(queue2Name, exchangeName, "info");
channel.queueBind(queue2Name, exchangeName, "warring");
channel.queueBind(queue2Name, exchangeName, "error");
//8.发送消息
String body = "这是一条rabbitmq的info信息哦";
String body1 = "这是一条rabbitmq的error信息哦";
channel.basicPublish(exchangeName, "info", null, body.getBytes());//只有q2能收到消息
channel.basicPublish(exchangeName, "error", null, body1.getBytes());//q1q2能收到消息
//9.释放资源
channel.close();
connection.close();
}
}
消费者1
package org.example.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class MyConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxx");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
String queenName1 = "test_direct_queue1";
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("==================================================================");
// 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(queenName1 + ": '执行了'");
System.out.println("body:" + new String(body));
System.out.println("这是 error ");
System.out.println("==================================================================");
}
};
channel.basicConsume(queenName1, true, consumer);
}
}
消费者2
package org.example.routing;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class MyConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("xxxxxx");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
String queenName2 = "test_direct_queue2";
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("==================================================================");
// 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(queenName2 + ": '执行了'");
System.out.println("body:" + new String(body));
System.out.println("这是info error warring");
System.out.println("==================================================================");
}
};
channel.basicConsume(queenName2, true, consumer);
}
}
(4)Topics 通配符模式
模式介绍
通配符介绍
*
: 表示一个单词。
#
: 表示一个或者多个单词。
代码示例
生产者
package org.example.topic;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("47.115.217.235");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值guest
//3.创建连接Connection
Connection connection = factory.newConnection();
//4.创建Channel
Channel channel = connection.createChannel();
//5.创建交换机
//exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map<String, Object> arguments)
//exchange:交换机名称
//type:交换机类型
//DIRECT("direct"); 定向
//FANOUT("fanout"); 扇形(广播) 发送消息到每一个与之绑定队列。
//TOPIC("topic"); 通配符匹配
//HEADERS("headers");参数匹配
//durable:是否持久化。
//autoDelete:自动删除。
//internal: 内部使用,一般false。
//arguments:参数。
String exchangeName = "test_topic";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true, false, false, null);
//6.创建队列
String queue1Name = exchangeName+"_queue1";
String queue2Name = exchangeName+"_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设置为空
// routingKey 系统的名称.日志的级别
// 所有error级别的日志分发给queue1Name,所有order系统的日志分发给queue1Name
channel.queueBind(queue1Name, exchangeName, "#.error");
channel.queueBind(queue1Name, exchangeName, "order.*");
channel.queueBind(queue2Name, exchangeName, "*.*");
//8.发送消息
String body = "这是一条rabbitmq的error信息哦";
String body1 = "这是一条rabbitmq的order信息哦";
String body2 = "这是一条rabbitmq的info信息哦";
channel.basicPublish(exchangeName, "a.error", null, body.getBytes());
channel.basicPublish(exchangeName, "order.a", null, body1.getBytes());
channel.basicPublish(exchangeName, "info.a", null, body2.getBytes());
//9.释放资源
channel.close();
connection.close();
}
}
消费者1
package org.example.topic;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class MyConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("47.115.217.235");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
String queenName1 = "test_topic_queue1";
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("==================================================================");
// 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(queenName1 + ": '执行了'");
System.out.println("body:" + new String(body));
System.out.println("==================================================================");
}
};
channel.basicConsume(queenName1, true, consumer);
}
}
消费者2
package org.example.topic;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class MyConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
//1.创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//2.设置参数
factory.setHost("47.115.217.235");//默认值 localhost
factory.setPort(5672); //端口默认值5672
factory.setVirtualHost("/");//虚拟机默认值 /
factory.setUsername("mofeng");//用户名默认guest
factory.setPassword("263327");//密码默认值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)
queue:队列名称
durable:是否持久化,当mq重启之后,还在
exclusive:
是否独占。只能有一个消费者监听这队列
当Connection关闭时,是否册除队列
autoDelete:是否自动地除。 当没有Consumer时,自动删除掉
arguments: 参数。
*/
String queenName2 = "test_topic_queue2";
/*
public String basicConsume(String queue, boolean autoAck, Consumer callback)
queue: 队列名称
autoACk: 是否自动确认
callback: 回调对象
*/
Consumer consumer = new DefaultConsumer(channel) {
/**
* 回调方法,当收到消息后,会自动执行该方法
* 1.consumerTag: 标识
* 2.envelope: 获取一些信息, 交换机, 路由key...
* 3.properties: 配置信息
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("==================================================================");
// 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(queenName2 + ": '执行了'");
System.out.println("body:" + new String(body));
System.out.println("==================================================================");
}
};
channel.basicConsume(queenName2, true, consumer);
}
}
2.4、JMS
JMS 即Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件的API
JMS 是JavaEE规范中的一种,像JDBC。
很多消息中间件都实现了JMS规范,例如:ActiveMQ。 RabbitMQ 官方没有提供JMS的实现包,但是开源社区有。
2.5、安装与配置
docker run -it --rm --name=rabbitmq -p 5672:5672 -p 15672:15672 -v rabbitmq-home:/var/lib/rabbitmq -e RABBITMQ_DEFAULT_USER=xxxx -e RABBITMQ_DEFAULT_PASS=xxxx rabbitmq:3.11-management
RABBITMQ_DEFAULT_USER环境变量:指定RabbitMQ的用户名
RABBITMQ_DEFAULT_PASS环境变量:指定RabbitMQ的密码
版本号带management的,内部集成了一个管理插件,否则就没有。