首先通过上面两个入门教程我们引入本文:
Publish/Subscribe:在上一章中,我们学习创建了一个消息队列,她的每个任务消息只发送给一个队列,然后队列的信息由消费者各自消费。这一章,我们会将同一个任务消息发送给多个队列。这种模式就是“发布/订阅”。为了将消息发送到把那个队列上 我们讲迎来一个新的概念 交换机 。
交换机: RabbitMQ中消息传递模型的核心思想是:生产者不直接发送消息到队列。实际的运行环境中,生产者是不知道消息会发送到那个队列上,她只会将消息发送到一个交换器,交换器也像一个生产线,她一边接收生产者发来的消息,另外一边则根据交换规则,将消息放到队列中。交换器必须知道她所接收的消息是什么?它应该被放到那个队列中?它应该被添加到多个队列吗?还是应该丢弃?这些规则都是按照交换器的规则来确定的。
P 是生产者 X是交换机 交换机来决定规则
交换器的规则有:
- direct (直连)
- topic (主题)
- headers (标题)
- fanout (分发)也有翻译为扇出的。
先直接上代码吧 目录结构如下
生产者:
package wxtest.rabbitMq.Publish_Subscribe;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import wxtest.rabbitMq.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class msgProduce {
private static String exchage_name = "Msg_exchange";//设置一个交换机
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
//这里开始定义一个交换机 前面介绍的都是直接定义队列
channel.exchangeDeclare(exchage_name, "fanout");//fanout属于主题订阅方式
//分发消息
for (int i = 0; i < 5; i++) {
String message = "我是交换机的消息 " + i;
//basicPublish发布的第一个参数属于交换机 第二个参数属于按照routingKey方式发放
channel.basicPublish(exchage_name, "", null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
}
channel.close();
connection.close();
}
}
email消费者:
package wxtest.rabbitMq.Publish_Subscribe;
import com.rabbitmq.client.*;
import wxtest.rabbitMq.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class emailConsumer {
private static String exchage_name = "Msg_exchange";//设置一个交换机
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchage_name, "fanout");//绑定交换机 定义交换机
String queueName = channel.queueDeclare().getQueue();//获取队列
channel.queueBind(queueName, exchage_name, "");//绑定交换机和所对应的队列
System.out.println(" emailConsumer Waiting for messages. To exit press CTRL+C");
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
System.out.println(" consumer1 [x] Received '" + msg + "'");
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
sms消费者:
package wxtest.rabbitMq.Publish_Subscribe;
import com.rabbitmq.client.*;
import wxtest.rabbitMq.ConnectionUtil;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class smsComsumer {
private static String exchage_name = "Msg_exchange";//设置一个交换机
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchage_name, "fanout");//绑定交换机
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, exchage_name, "");//绑定交换机所对应的队列
System.out.println(" smsComsumer Waiting for messages. To exit press CTRL+C");
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
System.out.println(" smsComsumer [x] Received '" + msg + "'");
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(queueName, true, consumer);
}
}
emailConsumer输出结构如下:
smsConsumer输出结果如下:
可以看出只要我们的消费者绑定了我们消费者的交换机 那么我们交换机的信息就会分发到各路的消费者对应的队列里 供应消费者消费。那么发布订阅就到此为止 下面我们将引入路由routingKey实现固定分发,详情请看本作者博客。