在订阅模型中,多了一个 Exchange 角色,而且过程略有变化:
P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
C:消费者,消息的接收者,会一直等待消息到来
Queue:消息队列,接收消息、缓存消息
Exchange:交换机(X)。一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
Exchange有常见以下3种类型:
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key 的队列
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与 Exchange 绑定,或者没有符合路由规则的队列,那么消息会丢失!
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_PubSub {
public static void main(String[] args) throws IOException, TimeoutException {
/**
* 1、创建连接工厂
* 2、设置参数
* 3、创建连接 Connection
* 4、创建Channel
* 5、创建交换机
* 6、创建队列
* 7、绑定队列和交换机
* 8、发送消息
* 9、释放资源
*/
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置参数
factory.setHost("192.168.16.62"); // ip 默认值 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,
//交换机类型 4种:
DIRECT("direct"), //定向
FANOUT("fanout"), //扇形(广播),发送消息到每一个队列
TOPIC("topic"), //通配符方式
HEADERS("headers"); //参数匹配
boolean durable, //是否持久化
boolean autoDelete, //自动删除
boolean internal, //内部使用。一般false
Map arguments //参数
)
*/
String exchangeName = "test_fanout";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);
// 6、创建队列
/*
queueDeclare(
String queue, //队列名称
boolean durable, //是否持久化, 当mq重启后,还在
boolean exclusive, //是否独占,只能有一个消费者监听此队列;当Connection关闭时,是否删除队列
boolean autoDelete, // 是否自动删除。当没有Consumer时自动删除
Map arguments //参数信息,删除的信息
)
*/
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
channel.queueDeclare(queue1Name, true, false, false, null);
channel.queueDeclare(queue2Name, true, false, false, null);
// 7、绑定队列和交换机
/*
queueBind(
String queue, //队列名称
String exchange, //交换机名称
String routingKey, //路由键,绑定规则,如果交换机的类型为 FANOUT ,routingKey 设置为 ""
Map arguments //
)
*/
channel.queueBind(queue1Name, exchangeName, "");
channel.queueBind(queue2Name, exchangeName, "");
// 8、发送消息
/*
basicPublish(
String exchange, // 交换机名称。简单模式下使用默认交换机:""
String routingKey, // 路由名称
BasicProperties props, // 配置信息
byte[] body // 发送消息数据
)
*/
String body = "日志信息:findALl 日志级别:info";
channel.basicPublish(exchangeName,"",null,body.getBytes());
// 9、释放资源
channel.close();
connection.close();
}
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 接收消息
*/
public class Consumer_PubSub1 {
public static void main(String[] args) throws IOException, TimeoutException {
/**
* 1、创建连接工厂
* 2、设置参数
* 3、创建连接 Connection
* 4、创建Channel
* 5、创建队列
* 6、接收消息
*/
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置参数
// ip 默认值 localhost, 192.168.16.62 :启动MQ服务器的ip
factory.setHost("192.168.16.62");
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、创建队列, 生产者已经创建,无需再声明
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
//6、接收消息
/*
basicConsume(
String queue, // 队列名称
boolean autoAck, // 是否自动确认
Consumer callback // 回调对象
)
*/
Consumer consumer = new DefaultConsumer(channel){
/*
回调方法,当收到消息后,会自动执行该方法
handleDelivery(
String consumerTag, // 标识
Envelope envelope, // 获取一些信息,交换机,路由key
AMQP.BasicProperties properties, //配置信息
byte[] body) // 数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// System.out.println("consumerTag : "+consumerTag);
// System.out.println("envelope : "+envelope);
// System.out.println("properties : "+properties);
System.out.println("控制台:" + new String(body));
}
};
channel.basicConsume(queue1Name,true,consumer);
}
}
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 接收消息
*/
public class Consumer_PubSub2 {
public static void main(String[] args) throws IOException, TimeoutException {
/**
* 1、创建连接工厂
* 2、设置参数
* 3、创建连接 Connection
* 4、创建Channel
* 5、创建队列
* 6、接收消息
*/
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 2、设置参数
// ip 默认值 localhost, 192.168.16.62 :启动MQ服务器的ip
factory.setHost("192.168.16.62");
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、创建队列, 生产者已经创建,无需再声明
String queue1Name = "test_fanout_queue1";
String queue2Name = "test_fanout_queue2";
//6、接收消息
/*
basicConsume(
String queue, // 队列名称
boolean autoAck, // 是否自动确认
Consumer callback // 回调对象
)
*/
Consumer consumer = new DefaultConsumer(channel){
/*
回调方法,当收到消息后,会自动执行该方法
handleDelivery(
String consumerTag, // 标识
Envelope envelope, // 获取一些信息,交换机,路由key
AMQP.BasicProperties properties, //配置信息
byte[] body) // 数据
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// System.out.println("consumerTag : "+consumerTag);
// System.out.println("envelope : "+envelope);
// System.out.println("properties : "+properties);
System.out.println("保存数据库:" + new String(body));
}
};
channel.basicConsume(queue2Name,true,consumer);
}
}
交换机需要与队列进行绑定,绑定之后;一个消息可以被多个消费者都收到。
发布订阅模式与工作队列模式的区别:
工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机
发布/订阅模式的生产方是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机)
发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑 定到默认的交换机