1 Simple 模式
Simple 模式是最简单的一个模式,由一个生产者,一个队列,一个消费者组成,生产者将消息通过交换机(此时,图中并没有交换机的概念,如不定义交换机,会使用默认的交换机)把消息存储到队列,消费者从队列中取出消息进行处理。
P------------》QUEUE------------》C
直接上代码`
生产者:
public class Send {
private final static String QUEUE_NAME = "queue1";
public static void main(String[] args) {
// 1、创建连接工程
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2、创建连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
// 3、声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 消息内容
String message = "Hello world";
// 4、发送消息到指定队列
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println(" [x] Sent '" + message + "'");
} catch (TimeoutException | IOException e) {
e.printStackTrace();
} finally {
// 关闭通道
if (channel != null && channel.isOpen()) {
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 关闭连接
if (connection != null && connection.isOpen()) {
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
消费者
public class Recv {
private final static String QUEUE_NAME = "queue1";
public static void main(String[] args) throws IOException, TimeoutException {
// 1、创建连接工程
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setVirtualHost("/");
// 2、获取 Connection和 Channel
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 3、声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {
});
}
}
2 Fanout 模式
P -----------》X(交换机)---------》QUEUE---------> C1
---------> C2
Fanout——发布订阅模式,是一种广播机制
此模式包括:一个生产者、一个交换机 (exchange)、多个队列、多个消费者。生产者将消息发送到交换机,交换机不存储消息,将消息存储到队列,消费者从队列中取消息。如果生产者将消息发送到没有绑定队列的交换机上,消息将丢失
生产者:
public class Productor {
private static final String EXCHANGE_NAME = "fanout_exchange";
public static void main(String[] args) {
// 1、创建连接工程
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2、获取连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
// 消息内容
String message = "hello fanout mode";
// 指定路由key
String routeKey = "";
String type = "fanout";
// 3、声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, type);
// 4、声明队列
channel.queueDeclare("queue1", true, false, false, null);
channel.queueDeclare("queue2", true, false, false, null);
channel.queueDeclare("queue3", true, false, false, null);
channel.queueDeclare("queue4", true, false, false, null);
// 5、绑定 channel 与 queue
channel.queueBind("queue1", EXCHANGE_NAME, routeKey);
channel.queueBind("queue2", EXCHANGE_NAME, routeKey);
channel.queueBind("queue3", EXCHANGE_NAME, routeKey);
channel.queueBind("queue4", EXCHANGE_NAME, routeKey);
// 6、发布消息
channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));
System.out.println("消息发送成功!");
} catch (IOException | TimeoutException e) {
e.printStackTrace();
System.out.println("消息发送异常");
}finally {
// 关闭通道和连接......
}
}
}
消费者:
public class Customer {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
final String queueName = Thread.currentThread().getName();
Connection connection = null;
Channel channel = null;
try {
// 获取连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
Channel finalChannel = channel;
finalChannel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery delivery) throws IOException {
System.out.println(delivery.getEnvelope().getDeliveryTag());
System.out.println(queueName + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
}
});
System.out.println(queueName + ":开始接收消息");
} catch (IOException |
TimeoutException e) {
e.printStackTrace();
} finally {
// 关闭通道和连接......
}
}
};
public static void main(String[] args) throws IOException, TimeoutException {
// 创建线程分别从四个队列中获取消息
new Thread(runnable, "queue1").start();
new Thread(runnable, "queue2").start();
new Thread(runnable, "queue3").start();
new Thread(runnable, "queue4").start();
}
}
执行完 Productor 发现四个队列中分别增加了一条消息,而执行完 Customer 后四个队列中的消息都被消费者消费了
3 Direct 模式
P---------->X----bangdingkey1-------->QUEUE1----------->C1
----bangdingkey2-------->QUEUE2----------->C2
Direct 模式是在 Fanout 模式基础上添加了 routing key,Fanout(发布/订阅)模式是交换机将消息存储到所有绑定的队列中,而 Direct 模式是在此基础上,添加了过滤条件,交换机只会将消息存储到满足 routing key 的队列中。
在上图中,我们可以看到交换机绑定了两个队列,其中队列 Q1绑定的 routing key 为 “orange” ,队列Q2绑定的routing key 为 “black” 和 “green”。在这样的设置中,发布 routing key 为 “orange” 的消息将被路由到 Q1,routing key 为 “black” 或 “green” 的消息将被路由到 Q2
在 rabbitmq 中给队列绑定 routing_key,routing_key 必须是单词列表
生产者:
public class Productor {
private static final String EXCHANGE_NAME = "direct_exchange";
public static void main(String[] args) {
// 1、创建连接工程
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2、获取连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
// 消息内容
String message = "hello direct mode";
// 指定路由key
String routeKey = "email";
String type = "direct";
// 3、声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, type);
// 4、声明队列
channel.queueDeclare("queue1", true, false, false, null);
channel.queueDeclare("queue2", true, false, false, null);
channel.queueDeclare("queue3", true, false, false, null);
// 5、绑定 channel 与 queue
channel.queueBind("queue1", EXCHANGE_NAME, "email");
channel.queueBind("queue2", EXCHANGE_NAME, "sms");
channel.queueBind("queue3", EXCHANGE_NAME, "vx");
// 6、发布消息
channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));
System.out.println("消息发送成功!");
} catch (IOException | TimeoutException e) {
e.printStackTrace();
System.out.println("消息发送异常");
} finally {
// 关闭通道和连接......
}
}
}
4 Topic 模式
P---------->X----.orange.-------->QUEUE1----------->C1
------lazy.#------>QUEUE2----------->C2
Topic 模式是生产者通过交换机将消息存储到队列后,交换机根据绑定队列的 routing key 的值进行通配符匹配,如果匹配通过,消息将被存储到该队列,如果 routing key 的值匹配到了多个队列,消息将会被发送到多个队列;如果一个队列也没匹配上,该消息将丢失。
- routing_key 必须是单词列表,用点分隔,其中 * 和 # 的含义为:
*:1个单词
#:0个或多个单词
生产者:
public class Productor {
private static final String EXCHANGE_NAME = "topic_exchange";
public static void main(String[] args) {
// 1、创建连接工程
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 2、获取连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
// 消息内容
String message = "hello topic mode";
// 指定路由key
String routeKey = "com.order.test.xxx";
String type = "topic";
// 3、声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, type);
// 4、声明队列
channel.queueDeclare("queue5",true,false,false,null);
channel.queueDeclare("queue6",true,false,false,null);
// 5、绑定 channel 与 queue
channel.queueBind("queue5", EXCHANGE_NAME, "*.order.#");
channel.queueBind("queue6", EXCHANGE_NAME, "#.test.*");
// 6、发布消息
channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes("UTF-8"));
System.out.println("消息发送成功!");
} catch (IOException | TimeoutException e) {
e.printStackTrace();
System.out.println("消息发送异常");
} finally {
// 关闭通道和连接......
}
}
}
5 Work 模式
P---------->QUEUE1----------->C1
------------>C2
当有多个消费者时,如何均衡消息者消费消息的多少,主要有两种模式:
轮询模式分发:按顺序轮询分发,每个消费者获得相同数量的消息----默认的prefetch=0
公平分发:根据消费者消费能力公平分发,处理快的处理的多,处理慢的处理的少,按劳分配 prefetch=1
worker :
public class Worker1 {
public static void main(String[] args) {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.96.109");
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try {
// 获取连接、通道
connection = factory.newConnection();
channel = connection.createChannel();
Channel finalChannel = channel;
// Channel 使用 Qos 机制
finalChannel.basicQos(1)
finalChannel.basicConsume("queue1", true, new DeliverCallback() {
@Override
public void handle(String consumerTag, Delivery delivery) throws IOException {
System.out.println("Worker1" + ":收到消息是:" + new String(delivery.getBody(), "UTF-8"));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, new CancelCallback() {
@Override
public void handle(String consumerTag) throws IOException {
}
});
System.out.println("Worker1 开始接收消息");
System.in.read();
} catch (IOException |
TimeoutException e) {
e.printStackTrace();
} finally {
// 关闭通道和连接......
}
}
}