第一种 simple 简单模式
模型如图
这里的P可以理解成消息的生产者,C理解成消息的接受者(消费者), 中间红色区域为队列。
首先创建获取链接的工具类看代码
package com.rabbitmq.utils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtils {
public static Connection getConnection() throws IOException, TimeoutException{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/v2_test");
factory.setUsername("guest");
factory.setPassword("guest");
return factory.newConnection();
}
}
创建消息发送者 代码如下
package com.rabbitmq.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.utils.ConnectionUtils;
public class Sender {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//创建消息通道
Channel channel = connection.createChannel();
/**
* queue 队列名称
* durable 是否持久化,是的话会将次队列持久化保存到Erlang自带的Mnesia数据库中,RabbitMQ重启后会读取该数据库
* (注意,该参数只是持久化队列,并非消息,想要确保消息的可靠性,必须要将消息也进行持久化设置)
* exclusive 是否为排他队列 1:connection关闭时,该队列是否自动删除,2:该队列是否私有
* 当设置为true的时候,一个队列只允许一个消费者,一般情况设置为false
* autoDelete 是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,
* 可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
*/
channel.queueDeclare(QNM, false, false, false, null);
String msg = "Hello World";
/**
* 发送消息
*
* exchange 交换机名称
* routingKey 路由关键字
* props 未做研究,只知道这里可以设置消息持久化 MessageProperties.PERSISTENT_TEXT_PLAIN
* body 消息
*/
channel.basicPublish("", QNM, null, msg.getBytes());
channel.close();
connection.close();
}
}
创建消息消费者 代码如下
package com.rabbitmq.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//创建队列声明 主要为了防止消息接收者先运行此程序,队列还不存在时创建队列
channel.queueDeclare(QNM, false, false, false, null);
//定义一个消费者
DefaultConsumer consumer = new DefaultConsumer(channel){
//该方法为阻塞方法,消息到达会自动触发该方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println(msg);
}
};
//自动确认
boolean autoAck = true;
channel.basicConsume(QNM, autoAck, consumer);
}
}
此时先运行消费者,再运行生产者,消费者会收到消息Hello world
第二种 Work queues工作模式
模型如图
一对多的关系,一个消息生产者对应多个消息消费者,生产者提供的消息其中一部分被C1消费,另一部分被C2消费。
消息分配模式共有两种:
轮询分配:比如共有两个消费者,生产者产生100个消息,一个消费者拿1.3.5.7…99,另一个拿2.4.6…100,每人拿50个。不会考虑每个消费者的处理速度等问题,哪怕C1需要1秒执行完,C2需要10秒执行完
公平分配:谁处理的快,就给谁分配的多。
先看轮询分配 生产者代码如下
package com.rabbitmq.work.avg;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.utils.ConnectionUtils;
public class Sender {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QNM, false, false, false, null);
for (int i = 0; i < 100; i++) {
String msg = i+"_Hello word";
channel.basicPublish("", QNM, null, msg.getBytes());
System.out.println("Sender:"+msg);
}
channel.close();
connection.close();
}
}
接收者1代码
package com.rabbitmq.work.avg;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver1 {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QNM, false, false, false, null);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println(msg);
//模拟处理耗时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
boolean autoAck = true;
channel.basicConsume(QNM, autoAck, consumer);
}
}
接收者2代码
package com.rabbitmq.work.avg;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver2 {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QNM, false, false, false, null);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println(msg);
//模拟处理耗时
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
boolean autoAck = true;
channel.basicConsume(QNM, autoAck, consumer);
}
}
经测试,接收者1和2分别接处理50条数据,消费者1速度快,2速度慢
再看公平分配:
//一次只收一个
channel.basicQos(1);
公平分配需要关闭自动确认
boolean autoAck = false;
channel.basicConsume(QNM,autoAck,consumer);
代码手动确认
//手动确认消息处理成功
channel.basicAck(envelope.getDeliveryTag(), false);
其中一个接收者整体代码如下
package com.rabbitmq.work.fair;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver {
private static final String QNM = "I`m QNM";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QNM, false, false, false, null);
//一次只收一个
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println(msg);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//手动确认消息处理成功
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QNM,autoAck,consumer);
}
}
经测试,每个接收者处理的消息数量不同,速度快的处理的多,速度慢的处理的少。减少了整体耗时,提高了效率。
第三种 发布订阅模式
模型如下
生产者将消息发送到交换机,消费者的队列与该交换机绑定,生产者发送100条消息,两个消费者各接收100条消息
生产者不再将消息发送到队列,而是发送到交换机
不再声明队列,而是声明交换机
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
交换机类型为fanout,会将消息发送到每个与他绑定的队列上
生产者整体代码如下
package com.rabbitmq.ps;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.utils.ConnectionUtils;
public class Sender {
private static final String EXCHANGE_NAME = "exchange_fanout";
private static final String EXCHANGE_TYPE = "fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
String msg = "hello ps";
channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
System.out.println("Sender:"+msg);
channel.close();
connection.close();
}
}
消费者1整体代码如下
package com.rabbitmq.ps;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver {
private static final String EXCHANGE_NAME = "exchange_fanout";
private static final String QUEUE_NAME = "exhchange_queue_name";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列与交换机进行绑定,不存在routingKey
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("Rec:"+msg);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
消费者2与消费者1唯一不同之处就是队列名字,重新起一个队列名。这里就不再贴重复代码了。经测试,两个接受者都收到了生产者发送的消息
第四种 Routing路由模式
模型如下
路由模式需要将交换机模式改为direct
//第三个参数 true,表示将该交换机持久化
channel.exchangeDeclare(EXCHANGE_ROUT_NAME, "direct",true);
生产者完整代码
package com.rabbitmq.rout;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.utils.ConnectionUtils;
public class Sender {
private static final String EXCHANGE_ROUT_NAME = "exchange_rout_name";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//第三个参数 true,表示将该交换机持久化
channel.exchangeDeclare(EXCHANGE_ROUT_NAME, "direct",true);
String msg = "";
String routingKey = "";
for (int i = 0; i < 50; i++) {
//模拟不同的routingKey和不同的消息内容
int flag = i%3;
if(flag == 0){
msg = "This is a boy`message";
routingKey = "boy";
}else{
msg = "This is a girl`message";
routingKey = "girl";
}
channel.basicPublish(EXCHANGE_ROUT_NAME, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
}
channel.close();
connection.close();
}
}
消费者1代码
package com.rabbitmq.rout;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Rexeiver {
private static final String EXCHANGE_ROUT_NAME = "exchange_rout_name";
private static final String QUEUE_NAME = "queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.basicQos(1);
channel.queueBind(QUEUE_NAME, EXCHANGE_ROUT_NAME, "boy", null);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String (body,"utf-8");
System.out.println(msg);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
接收者2比接收者1多了
channel.queueBind(QUEUE_NAME, EXCHANGE_ROUT_NAME, "girl", null);
队列名称不同
整体代码如下
package com.rabbitmq.rout;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Rexeiver2 {
private static final String EXCHANGE_ROUT_NAME = "exchange_rout_name";
private static final String QUEUE_NAME = "queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicQos(1);
channel.queueBind(QUEUE_NAME, EXCHANGE_ROUT_NAME, "boy", null);
channel.queueBind(QUEUE_NAME, EXCHANGE_ROUT_NAME, "girl", null);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String (body,"utf-8");
System.out.println(msg);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
经测试:接收1可以收到routingKey为boy的消息,接收2可以收到routingKey为boy、girl的消息
结果分析:发送者将所有的消息发送到交换机。而接收者1使用queue1与交换机绑定,而queue1只会获取routingKey为boy的消息;同理,接收者2使用queue2与交换机绑定,所以queue2可以收到routingKey为boy和girl的消息。
第五种 topics模式
模型如下
topics模式和路由模式类似但比路由和发布订阅模式强大,引入routingKey通配符
生产者代码如下
package com.rabbitmq.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.utils.ConnectionUtils;
public class Sender {
private static final String EXCHANGE_NAME = "topic exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//这里声明模式为topic
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String msg = "";
String routingKey = "";
for (int i = 0; i <50; i++) {
int flag = i%4;
if(flag == 0){
routingKey = "user.add";
msg = "User Message.add";
}else if(flag == 1){
routingKey = "user.delete";
msg = "User Message.delete";
}else if(flag == 2){
routingKey = "user.update";
msg = "User Message.update";
}else{
routingKey = "user.select";
msg = "User Message.select";
}
channel.basicPublish(EXCHANGE_NAME,routingKey, null, msg.getBytes());
}
channel.close();
connection.close();
}
}
接收者1代码如下
package com.rabbitmq.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver1 {
private static final String EXCHANGE_NAME = "topic exchange";
private static final String QUEUE_NAME = "topic queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "user.#";
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey );
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("Rec1:"+msg);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
接收者2代码如下
package com.rabbitmq.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.utils.ConnectionUtils;
public class Receiver2 {
private static final String EXCHANGE_NAME = "topic exchange";
private static final String QUEUE_NAME = "topic queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String routingKey = "user.add";
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey );
channel.basicQos(1);
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("Rec2:"+msg);
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}
其中不同之处为
接收者1
String routingKey = "user.#";
接收者2
String routingKey = "user.add";
经测试 接收1可以收到所有以user.开头的routingKey的所有消息,接收者2只可以收到routingKey为user.add的消息。
刚开始学习,知识细节不是很详细,如有问题欢迎大家及时指出。