埋头苦写。先把官方文档翻译过来。整个流程跑一遍。上一篇文章,【Spring消息】RabbitMq安装及简单应用(一),把点对点发送消息写完了。之前虽然也可以一个生产者多个消费者,但是一条消息只能被一个消费者处理,所以是点对点。这篇文章来讲讲发布订阅,一对多。一条消息同时被多个消费者(本文称为订阅者)处理。
引入了一个新概念:Exchange(即上图的X概念)。(其实此概念,在Amqp协议中是必要的一环,暂时可无需理解,后续会提到)可简单先理解为一个路由功能。RabbitMq定义了四种路由模式,direct, topic, headers and fanout.(本文会讲到除headers外其它三种)
由上图可知生产者和队列之间没直接关联关系,甚至不需要知道有这么个东西,所以如果看过上一篇文章的小伙伴可以删掉队列名有关的代码。
1)生产者代码
package com.haibo.future.web.rabbitmq.demo6;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
public class RabbitProduct {
private final static String EXCHANGE_NAME = "logs";
public static void main(String[] argv) throws Exception {
for (int i = 0; i < 20; i++) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String message = "第"+i+"条"+"Hello World!";
//本次新增EXCHANGE
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//发送的时候只需要制定Exchange,不需要制定第二个参数队列名称
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
}
2)消费者代码
package com.haibo.future.web.rabbitmq.demo6;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式广播
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//消费者需要知道队列名称,因为已知是fanout广播模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来
channel.queueBind(queueName, EXCHANGE_NAME, "");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
3)测试执行
执行两个消费者,再执行生产者。结果:生产者消息,同时发给了两个消费者。日志略。
直连:即binding Key全匹配。上图中Binding Key即black。
1)、生产者:(不与队列直接关联,绑定binding Key)
package com.haibo.future.web.rabbitmq.demo7;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitProduct {
private final static String EXCHANGE_NAME = "logs2";
public static void main(String[] argv) throws Exception {
for (int i = 0; i < 20; i++) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String message = "第"+i+"条"+"Hello World!";;
//本次新增EXCHANGE:direct直接匹配,全匹配模式
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//发送的时候只需要制定Exchange,不需要制定第二个参数队列名称/此处是drect模式,第二个参数为binding key
channel.basicPublish(EXCHANGE_NAME, "black", null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
}
2)、消费者1(与队列直接关联,绑定binding Key "black")
package com.haibo.future.web.rabbitmq.demo7;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来
channel.queueBind(queueName, EXCHANGE_NAME, "black");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
3)、消费者2(与队列直接关联,绑定binding Key "red")
package com.haibo.future.web.rabbitmq.demo7;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer2 {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来,binding key 为black
channel.queueBind(queueName, EXCHANGE_NAME, "red");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
4)、消费者3(与队列直接关联,绑定binding Key "black")
package com.haibo.future.web.rabbitmq.demo7;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer3 {
private static final String EXCHANGE_NAME = "logs2";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来,binding key 为black
channel.queueBind(queueName, EXCHANGE_NAME, "black");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
5)、执行测试:
3个消费者启动,启动生产者。日志显示:消费者1、消费者3显示收到生产者的消息,消费者2不能收到生产者的消息。日志略。
直接上代码:
1)、消费者1:(rootingKey是*.orange.rabbit)
package com.haibo.future.web.rabbitmq.demo8;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来
channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.rabbit");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
2)、消费者2:(rooting key 是 *.orange.bird)
package com.haibo.future.web.rabbitmq.demo8;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer2 {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来
channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.bird");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
3)、消费者3:(rooting key是lazy.#)
package com.haibo.future.web.rabbitmq.demo8;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer3 {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv)
throws IOException,
InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = null;
try {
connection = factory.newConnection();
} catch (TimeoutException e) {
e.printStackTrace();
}
final Channel channel = connection.createChannel();
//定义路由模式direct
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//消费者需要知道队列名称,因为已知是direct模式,queueName直接从channel定义中获取
String queueName = channel.queueDeclare().getQueue();
//将队列和Exchange绑定起来
channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body)
throws IOException {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
boolean autoAck = true;
channel.basicConsume(queueName, autoAck, consumer);
}
}
4)、生产者:
package com.haibo.future.web.rabbitmq.demo8;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class RabbitProduct {
private static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] argv) throws Exception {
for (int i = 0; i < 10; i++) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String message = null;
//P-->X :本次新增EXCHANGE:direct直接匹配,全匹配模式
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//X-->Q :发送的时候只需要制定Exchange,不需要制定第二个参数队列名称/此处是drect模式,第二个参数为binding key/此处是topic模式,第二个参数为rooting key
if (i == 5) {
message = "第" + i + "条:" + "lazy.orange.rabbit" + i;
channel.basicPublish(EXCHANGE_NAME, "lazy.orange.rabbit" + i, null, message.getBytes("UTF-8"));
} else {
if (i % 2 == 1) {
message = "第" + i + "条:" + i + ".orange." + "rabbit";
channel.basicPublish(EXCHANGE_NAME, i + ".orange." + "rabbit", null, message.getBytes("UTF-8"));
} else {
message = "第" + i + "条:" + i + ".orange." + "bird";
channel.basicPublish(EXCHANGE_NAME, i + ".orange." + "bird", null, message.getBytes("UTF-8"));
}
}
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
}
5)、执行测试:
消费者1,2,3分别执行,执行生产者。结果如下:
生产者:
[x] Sent '第0条:0.orange.bird'
[x] Sent '第1条:1.orange.rabbit'
[x] Sent '第2条:2.orange.bird'
[x] Sent '第3条:3.orange.rabbit'
[x] Sent '第4条:4.orange.bird'
[x] Sent '第5条:lazy.orange.rabbit5'
[x] Sent '第6条:6.orange.bird'
[x] Sent '第7条:7.orange.rabbit'
[x] Sent '第8条:8.orange.bird'
[x] Sent '第9条:9.orange.rabbit'
消费者1:(rootingKey是*.orange.rabbit)
[*] Waiting for messages. To exit press CTRL+C
[x] Received '第1条:1.orange.rabbit'
[x] Received '第3条:3.orange.rabbit'
[x] Received '第7条:7.orange.rabbit'
[x] Received '第9条:9.orange.rabbit'
消费者2:(rooting key 是 *.orange.bird)
[*] Waiting for messages. To exit press CTRL+C
[x] Received '第0条:0.orange.bird'
[x] Received '第2条:2.orange.bird'
[x] Received '第4条:4.orange.bird'
[x] Received '第6条:6.orange.bird'
[x] Received '第8条:8.orange.bird'
消费者3:(rooting key是lazy.#)
[*] Waiting for messages. To exit press CTRL+C
[x] Received '第5条:lazy.orange.rabbit5'