RabbitMQ 是基于 AMQP 协议使用 Erlang 语言开发的一款消息队列产品。
AMQP协议类似http协议,是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。
AMQP协议 Advanced Message Queuing Protocol(高级消息队列协议)。
我们知道RabbitMQ基于 AMQP 协议实现,因此下面将使用amqp client进行代码演示学习。
两个工程都导入jar包,如下:
<dependencies>
<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.12.0</version>
</dependency>
</dependencies>
简单模式:有一个生产者,一个消费者,一个消息队列。生产者将消息发送给队列,消息队列可以缓存消息,消费者可从中取出消息。
代码如下:
简单模式生产者代码
/**
* @author JIN
* @description 简单模式 一个生产者 一个消费者 一个消息队列
* @createTime 2021-06-10 10:18
**/
public class HelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
// 设置一系列内容
// ip
connectionFactory.setHost("127.0.0.1");
// 端口
connectionFactory.setPort(5672);
// 名字
connectionFactory.setUsername("jin");
// 密码
connectionFactory.setPassword("jin");
// 虚拟主机
connectionFactory.setVirtualHost("/jin");
// 建立连接
Connection connection = connectionFactory.newConnection();
// 获取channel
Channel channel = connection.createChannel();
// queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments)
// 参数意思:
// queue 队列名字
// durable 是否持久化
// exclusive 是否独占,true为 独占,只有一个消费者监听这个队列,且当connection关闭时,自动删除此队列
// autoDelete 是否自动删除,当没有消费者consumer监听队列时,队列自动删除
// arguments 其他参数
// 下面一句代码---用于创建队列
channel.queueDeclare("HelloWorldQueue",true, false,true,null);
// basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
// exchange 交换机,这里是简单模式,没有交换机,因此只需要写空 ""
// routingkey 路由键,在rabbitmq简单模式中 消息队列名字默认就是路由键
// props 其他额外参数 可以填null
// body 具体消息内容
// 下面代码 是生产者向 rabbitmq 发送消息
channel.basicPublish("","HelloWorldQueue",null,"HelloWorld的测试".getBytes());
//关闭资源
channel.close();
connection.close();
}
}
简单模式消费者代码
/**
* @author JIN
* @description 简单模式 消费者
* @createTime 2021-06-10 10:33
**/
public class HelloWorld {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 下面一句代码---用于创建队列,由于生产者已经生产,下面可有可无
// 深入解析:
// 1. 若一开始你队列为空,你先执行生产者代码,生产者必须有创建队列代码,消费者可有可无
// 2. 若一开始你队列为空,你先执行消费者代码,消费者必须有创建队列代码,生产者可有可无
channel.queueDeclare("HelloWorldQueue",true, false,true,null);
// basicConsume(String queue, boolean autoAck, Consumer callback)
// 参数说明:
// 1. queue 监听的队列
// 2. autoAck 是否自动确认,当消费者收到一个消息,自动给rabbitmq发送已经收到的确认信息
// 3. callback 回调函数,当消费者收到消息,执行对应操作
// 下面代码 是消费者从队列获取消息
channel.basicConsume("HelloWorldQueue",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 参数说明
// consumerTag 消息tag,每个消息都有自己的tag
// envelope 可以获得路由键信息,交换机信息等
// envelope.getExchange();
// envelope.getRoutingKey();
// properties 其他参数配置
// body 收到的信息
System.out.println(new String(body));
}
});
//因为消费者需要一直监听队列是否有消息,因此无需关闭资源
}
}
工作队列模式:相对简单模式来说,就是多了一些消费者,多个消费者共同消费同一个队列中的消息(是竞争关系),应用场景通常是对于任务过重或者任务较多情况使用工作队列模式可以提高任务处理的速度。
消费者代码
因为为了演示多个消费者相互竞争同一个队列的消息,先将2个消费者启动,然后再让生产者发送消息。
/**
* @author JIN
* @description 消费者一号
* @createTime 2021-06-10 10:51
**/
public class WorkQueueConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("WorkQueue",true, false,true,null);
channel.basicConsume("WorkQueue",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者一号: " + new String(body));
}
});
}
}
/**
* @author JIN
* @description 消费者二号
* @createTime 2021-06-10 10:51
**/
public class WorkQueueConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("WorkQueue",true, false,true,null);
channel.basicConsume("WorkQueue",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者二号: " + new String(body));
}
});
}
}
生产者代码
/**
* @author JIN
* @description workqueue模式,一个队列,一个生产者,多个消费者
* @createTime 2021-06-10 10:48
**/
public class WorkQueue {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("WorkQueue",true, false,true,null);
for (int i = 1; i <= 10; i++) {
// 为了演示 多个消费者竞争同一个队列 消息,因此发送多个消息
channel.basicPublish("","WorkQueue",null,("消息编号:" + i + " , WorkQueue的测试").getBytes());
}
channel.close();
connection.close();
}
}
广播模式就是将消息交给所有绑定到交换机的队列。
生产者代码
/**
* @author JIN
* @description 广播模式,一个交换机,一个生产者,两个队列 两个消费者
* @createTime 2021-06-10 12:14
**/
public class Fanout {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建2个队列
channel.queueDeclare("Fanout-Queue-01", true,false,true,null);
channel.queueDeclare("Fanout-Queue-02", true,false,true,null);
// 创建交换机
// exchangeDeclare(String exchange, BuiltinExchangeType type)
// 参数说明:
// 1. exchange 交换机
// 2. type 交换机类型
// BuiltinExchangeType.FANOUT 广播
// BuiltinExchangeType.DIRECT 定向
// BuiltinExchangeType.TOPIC 通配符
channel.exchangeDeclare("Fanout-Exchange", BuiltinExchangeType.FANOUT);
// 交换机与队列进行绑定
// queueBind(String queue, String exchange, String routingKey)
// 参数说明:
// 1. queue 队列名字
// 2. exchange 交换机名字
// 3. 绑定的路由键,由于这里是广播,全部队列都收到消息,即填"" 即可
channel.queueBind("Fanout-Queue-01","Fanout-Exchange","");
channel.queueBind("Fanout-Queue-02","Fanout-Exchange","");
for (int i = 0; i < 5; i++) {
// 发送消息
// 由于是广播,无需route-key
channel.basicPublish("Fanout-Exchange","",null,"Fanout测试".getBytes());
}
channel.close();
connection.close();
}
}
消费者代码
public class FanoutConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建2个队列
// 由于生产者写了,这里可有可无
channel.queueDeclare("Fanout-Queue-01;", true,false,true,null);
channel.queueDeclare("Fanout-Queue-02;", true,false,true,null);
channel.basicConsume("Fanout-Queue-01",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者一号: " + new String(body));
}
});
}
}
public class FanoutConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建2个队列
// 由于生产者写了,这里可有可无
channel.queueDeclare("Fanout-Queue-01;", true,false,true,null);
channel.queueDeclare("Fanout-Queue-02;", true,false,true,null);
channel.basicConsume("Fanout-Queue-02",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者二号: " + new String(body));
}
});
}
}
结果图:
从上面截图可以知道,每个消费者都消费了5次,并没有想work queue模式一样处于竞争关系,是因为work queue 模式是一个队列对应两个消费者,而Pub/Sub模式是,一个消费者对应一个队列,自己监听自己的队列,而队列是否有消息得看交换机是否转发过去,而对于同一个消息,交换机可以转发到符合路由规则的队列。
定向模式:
生产者代码
/**
* @author JIN
* @description
* @createTime 2021-06-10 12:46
**/
public class Direct {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 创建2个队列
channel.queueDeclare("Direct-Queue-01", true,false,true,null);
channel.queueDeclare("Direct-Queue-02", true,false,true,null);
// 创建交换机
channel.exchangeDeclare("Direct-Exchange", BuiltinExchangeType.DIRECT);
// 绑定交换机与队列
// 队列1 将会收到 路由建为 DIRECT.error 的消息
channel.queueBind("Direct-Queue-01","Direct-Exchange","DIRECT.error");
// 队列2 将会收到 路由建为 DIRECT.add 的消息
channel.queueBind("Direct-Queue-02","Direct-Exchange","DIRECT.add");
// 发送了三条消息
channel.basicPublish("Direct-Exchange","DIRECT.error",null,"我是error消息".getBytes());
channel.basicPublish("Direct-Exchange","DIRECT.add",null,"我是add消息".getBytes());
channel.basicPublish("Direct-Exchange","DIRECT.info",null,"我是info消息".getBytes());
channel.close();
connection.close();
}
}
消费者代码
/**
* @author JIN
* @description 消费者一号 接受队列1的消息,即只接受 routing key 为 error的消息
* @createTime 2021-06-10 12:51
**/
public class DirectConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Direct-Queue-01",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者一号: " + new String(body));
}
});
}
}
/**
* @author JIN
* @description 消费者二号 接受队列2的消息,即只接受 routing key 为 add的消息
* @createTime 2021-06-10 12:51
**/
public class DirectConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Direct-Queue-02",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者二号: " + new String(body));
}
});
}
}
结果截图
我们在生产者的时候发送了三条消息,未出现routing key为 DIRECT.info的消息,因为没有符合的队列,被交换机舍去了。
如果现在有个需求,要求把系统各个模块的error错误信息放在同一个队列,那么按照上面定向模式,则需要建立大量的交换机跟队列的路由键,比如:order.error,login.error…等等,实在是很麻烦,因此诞生出了通配符模式。
生产者代码
public class Topic {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("Topic-Queue-01",true,false,true,null);
channel.queueDeclare("Topic-Queue-02",true,false,true,null);
channel.queueDeclare("Topic-Queue-03",true,false,true,null);
channel.queueDeclare("Topic-Queue-04",true,false,true,null);
// 第三个参数 是否持久化
channel.exchangeDeclare("Topic-Exchange", BuiltinExchangeType.TOPIC,true);
// 创建这么多队列,只是为了* # 的演示
channel.queueBind("Topic-Queue-01","Topic-Exchange","*.add");
channel.queueBind("Topic-Queue-02","Topic-Exchange","#.add");
channel.queueBind("Topic-Queue-03","Topic-Exchange","*.*");
channel.queueBind("Topic-Queue-04","Topic-Exchange","#.#");
channel.basicPublish("Topic-Exchange","Hello.add",null,"我是Hello.add消息".getBytes());
channel.basicPublish("Topic-Exchange","Hello.World.add",null,"我是Hello.World.add消息".getBytes());
channel.basicPublish("Topic-Exchange","add",null,"我是add消息".getBytes());
channel.close();
connection.close();
}
}
消费者代码(共四个,因为在生产者中我创建了4个队列)
public class TopiceConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Topic-Queue-01",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者一号(*.add): " + new String(body));
}
});
}
}
public class TopiceConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Topic-Queue-02",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者二号(#.add): " + new String(body));
}
});
}
}
public class TopiceConsumer3 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Topic-Queue-03",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者三号(*.*): " + new String(body));
}
});
}
}
public class TopiceConsumer4 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setUsername("jin");
connectionFactory.setPassword("jin");
connectionFactory.setVirtualHost("/jin");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.basicConsume("Topic-Queue-04",true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("我是消费者四号(#.#): " + new String(body));
}
});
}
}
持续更新中
持续更新中