https://blog.csdn.net/Dante_003/article/details/79377908
Rabbitmq 是基于amqp(高级消息队列协议)实现的队列技术,在他之上可以完成多种类型的消息转发模型。
下面列举一些常用的消息转发场景,在rabbitmq中是怎样实现的。
1.原理
先来看一下rabbitmq消息转发的原理,便于理解消息转发以及怎样实现常见的消息转发模型。
1.1生产者
1.生产者创建连接,并创建通道连接rabbitmq
2.使用routing key绑定exchange和队列,可以绑定多个routing key到不同的队列。
3.生产者生产消息发送给exchange
4.exchange根据routing key匹配到对应队列名字,把消息转发到指定的queue上,消息会暂存在队列中,等待消费者来消费。
1.2消费者
1.消费者创建连接,并创建通道连接rabbitmq
2.消费者消费指定队列消息
注意:exchange、queue都必须要提前创建或者使用系统默认的也可以。
1.3exchange
exchange分为4种,分别是
1.direct,直接转发。exchange通过精确匹配routing key发送消息给队列
2.fanout,广播。会将消息广播到所有绑定到这个exchange的队列,无视发送消息时指定的routingkey。
3.topic,发布、订阅。routing可以可以使用通配符(#、*)来根据主题发送消息到不同的队列。
4.Headers,头信息。根据头信息的参数来决定发送到哪个队列。
1.4队列
队列是用来存储消息,在代码中可以创建临时队列,临时队列的属性是durable (false)、exclusive(true)、autoDelete(true)的队列,消息处理完自动删除。
队列的几种属性
- durable
是否持久化,队列会一直存在。 - exclusive
队列是否对当前连接特有,其它连接不能使用,当前连接断开后队列会消失。
exclusive和durable是互斥的。 - autoDelete
队列不再使用时会自动删除。
2.入门例子,消息转发
最简单的例子,生产者生产消息到队列,消费者从队列取数据,这个例子也是消息列队最基本、最常用的模型。
下面是建立连接的代码,后面的例子不再重复写这个。
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitmqUtil {
private static Connection connection;
// 获取连接
public static Connection getConnection() {
if (connection != null) {
return connection;
}
String userName = "admin";
String password = "admin";
String host = "192.168.1.248";
int port = 5672;
boolean recoveryEnabled = true;
// connection factory
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(userName);
factory.setPassword(password);
factory.setHost(host);
factory.setPort(port);
factory.setAutomaticRecoveryEnabled(recoveryEnabled);
try {
connection = factory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return connection;
}
// 获取通道
public static Channel getChannel() {
Channel channel = null;
Connection connection2 = getConnection();
try {
channel = connection2.createChannel();
} catch (IOException e) {
e.printStackTrace();
}
return channel;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
生产者、消费者
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//简单例子
public class Test1 {
private String queueName = "testQueue";
private String routingKey = "routingKey";
private String exchange = "testExchange";
public void produce() {
Channel channel = null;
try {
// 创建通道
channel = RabbitmqUtil.getChannel();
// 创建队列,持久、非专用、非自动删除的队列
channel.queueDeclare(queueName, true, false, false, null);
// 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
channel.exchangeDeclare(exchange, "direct");
// 使用routing key绑定exchange和queue
channel.queueBind(queueName, exchange, routingKey);
for (;;) {
channel.basicPublish(exchange, routingKey, null, "test".getBytes());
System.out.println("生产者:test");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
public void consume() {
Channel channel = null;
try {
channel = RabbitmqUtil.getConnection().createChannel();
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body, "UTF-8");
System.out.println(" 接收消息:'" + message + "'");
}
};
channel.basicConsume(queueName, true, defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
final Test1 test1 = new Test1();
new Thread() {
@Override
public void run() {
test1.produce();
}
}.start();
new Thread() {
@Override
public void run() {
test1.consume();
}
}.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
3.点对点通信
在rabbitmq中也可以实现点对点的通信,没有消息队列,所以不会存储消息,生产者和消费者之间直接通信。
说是没有队列,其实是创建了一个临时队列,这个队列不会持久化、自动删除、通道专用。
如下图。
在代码实现时,生产者只发送消息给exchnage,不绑定队列;消费者代码中创建临时队列,并绑定到exchange开始消费消息。
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//点对点通信
public class Test2 {
private String routingKey = "routingKey";
private String exchange = "testExchange";
public void produce() {
Channel channel = null;
try {
// 创建通道
channel = RabbitmqUtil.getChannel();
// 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
channel.exchangeDeclare(exchange, "direct");
for (;;) {
channel.basicPublish(exchange, routingKey, null, "test".getBytes());
System.out.println("生产者:test");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
public void consume() {
Channel channel = null;
try {
channel = RabbitmqUtil.getConnection().createChannel();
// 在消费者代码中创建临时队列,并绑定到指定的exchange
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, exchange, routingKey);
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body, "UTF-8");
System.out.println(" 接收消息:'" + message + "'");
}
};
channel.basicConsume(queue, true, defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
final Test2 test1 = new Test2();
new Thread() {
@Override
public void run() {
test1.produce();
}
}.start();
new Thread() {
@Override
public void run() {
test1.consume();
}
}.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
4.消息复制
像下图这样,两个队列使用相同的routing key绑定到同一个exchange上,消息会复制分发到两个队列中。代码不再实现,和例子1一样,只不过多创建一个队列。
利用这种模型可以实现多种场景的例子,例如日志收集,一个队列需要采集所有类型的日志,一个队列只采集错误日志,这样通过rabbitmq,就真正实现了一个消息录入,多种消息模式的转发。
5.广播模式
会将消息广播到所有绑定到这个exchange的队列,无视发送消息时指定的routingkey。
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//广播模式
public class Test3 {
private String queueName = "testQueue";
private String routingKey = "routingKey";
private String exchange = "testExchange1";
public void produce() {
Channel channel = null;
try {
// 创建通道
channel = RabbitmqUtil.getChannel();
// 创建队列,持久、非专用、非自动删除的队列
channel.queueDeclare(queueName, true, false, false, null);
// 创建一个exchange,使用rabbitmq内置默认exchange也可以,默认的exchange是""一个direct类型
channel.exchangeDeclare(exchange, "fanout");
// 使用routing key绑定exchange和queue
channel.queueBind(queueName, exchange, routingKey);
for (;;) {
//发送时的routingkey可以随意指定,所有绑定到这个exchange上的队列都会接收到消息
channel.basicPublish(exchange, "无视routingkey", null, "test".getBytes());
System.out.println("生产者:test");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
public void consume() {
Channel channel = null;
try {
channel = RabbitmqUtil.getConnection().createChannel();
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body, "UTF-8");
System.out.println(" 接收消息:'" + message + "'");
}
};
channel.basicConsume(queueName, true, defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
final Test3 test1 = new Test3();
new Thread() {
@Override
public void run() {
test1.produce();
}
}.start();
new Thread() {
@Override
public void run() {
test1.consume();
}
}.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
6.发布、订阅
和发布订阅消息一样,可以使用通配符关键字订阅指定的消息。
发送消息的routingkey和绑定exchange的routingkey可以是一组用”.”分开的词。词里面可以使用通配符
“*”:表示任意一个关键词
“#”:表示0个或者多个关键词
注意:两个词以上的一定要用”.”号隔开,上面两个通配符只是通配关键词,并非单个字母,如”my*.my1”这样是错误的。
下图是引用官网
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//发布订阅模式
public class Test4 {
private String queueName = "testQueue";
private String queueName1 = "testQueue1";
private String exchange = "testExchange2";
public void produce() {
Channel channel = null;
try {
// 创建通道
channel = RabbitmqUtil.getChannel();
// 创建两个队列接收订阅消息
channel.queueDeclare(queueName, true, false, false, null);
channel.queueDeclare(queueName1, true, false, false, null);
// 创建一个exchange
channel.exchangeDeclare(exchange, "topic");
// 给两个队列指定对应的订阅内容
channel.queueBind(queueName, exchange, "#.2.#");
channel.queueBind(queueName1, exchange, "#.2.?");
for (;;) {
// 发送两个routingkey消息内容
// 发送routingkey “my test1”,匹配两个队列
channel.basicPublish(exchange, "2.11", null, "test1".getBytes());
System.out.println("生产者:test1");
// 发送routingkey “my test2”,去掉后面的11,两个队列都能接收到
channel.basicPublish(exchange, "2.13.11", null, "test2".getBytes());
System.out.println("生产者:test2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
public void consume() {
Channel channel = null;
try {
channel = RabbitmqUtil.getConnection().createChannel();
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body, "UTF-8");
System.out.println(" 接收消息1:'" + message + "'");
}
};
channel.basicConsume(queueName, true, defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public void consume2() {
Channel channel = null;
try {
channel = RabbitmqUtil.getConnection().createChannel();
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String message = new String(body, "UTF-8");
System.out.println(" 接收消息2:'" + message + "'");
}
};
channel.basicConsume(queueName1, true, defaultConsumer);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
public static void main(String[] args) {
final Test4 test1 = new Test4();
new Thread() {
@Override
public void run() {
test1.produce();
}
}.start();
new Thread() {
@Override
public void run() {
test1.consume();
}
}.start();
new Thread() {
@Override
public void run() {
test1.consume2();
}
}.start();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
rabbitmq利用exchange和queue搭配的灵活性已经exchange的类型,可以完成业务场景中各种各样的需求。