RabbitMQ学习笔记5-五种模式之三(publish/subscribe、routing、topic)
exchange介绍
讲到发布订阅模式,就不能不提一下rabbitmq的exchange路由器。
exchange有4种模式,分别是header、fanout、direct和topic。其中fanout对应的是publish/subscribe模式,direct对应的是routing模式,topic对应的是topic模式
header
header exchange(头交换机)和主题交换机有点相似,但是不同于主题交换机的路由是基于路由键,头交换机的路由值基于消息的header数据。主题交换机路由键只有是字符串,而头交换机可以是整型和哈希值。
fanout
一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息经过交换机,到达队列,实现一个消息被多个消费者获取的目的。需要注意的是,如果将消息发送到一个没有队列绑定的exchange上面,那么该消息将会丢失,这是因为在rabbitMQ中exchange不具备存储消息的能力,只有队列具备存储消息的能力。
direct
这种模式添加了一个路由键,生产者发布消息的时候添加路由键,消费者绑定队列到交换机时添加键值,这样就可以接收到需要接收的消息。
topic
基本思想和路由模式是一样的,只不过路由键支持模糊匹配,符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。
publish/subscribe模式
模式
描述
发布/订阅模式,是通过exchange路由器分发到不同queue队列中,其中queue队列需要订阅exchange。其实发布/订阅是exchang路由的一种类型,type=fanout,这种模式不需要匹配rountingKey,即为发布/订阅模式。特点:每个消费者获取到的消息都是一样的。
代码
生产者
package com.lin.rabbit.ps;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class PsProvider {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
Channel channel = con.createChannel();
//声明交换机
channel.exchangeDeclare(PsConstant.PS_ROUTING_NAME, "fanout");
//发送信息
String msg = "hello, this is publish/subscribe model!";
channel.basicPublish(PsConstant.PS_ROUTING_NAME, "", null, msg.getBytes("UTF-8"));
System.out.println("send msg:"+msg);
//关闭通道和连接
channel.close();
con.close();
}
}
消费者1
package com.lin.rabbit.ps;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class PsConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(PsConstant.PS_1_QUEUE_NAME, false, false, false, null);
//队列与路由器绑定
channel.queueBind(PsConstant.PS_1_QUEUE_NAME,PsConstant.PS_ROUTING_NAME, "");
//获取消息
DefaultConsumer defaultConsumer = 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("receiver "+PsConstant.PS_1_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(PsConstant.PS_1_QUEUE_NAME, false, defaultConsumer);
}
}
消费者2
package com.lin.rabbit.ps;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class PsConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(PsConstant.PS_2_QUEUE_NAME, false, false, false, null);
//队列与路由器绑定
channel.queueBind(PsConstant.PS_2_QUEUE_NAME,PsConstant.PS_ROUTING_NAME, "");
//获取消息
DefaultConsumer defaultConsumer = 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("receiver "+PsConstant.PS_2_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(PsConstant.PS_2_QUEUE_NAME, false, defaultConsumer);
}
}
辅助类
package com.lin.rabbit.ps;
public class PsConstant {
public static String PS_ROUTING_NAME ="ps-routing";
public static String PS_1_QUEUE_NAME ="ps1-queue";
public static String PS_2_QUEUE_NAME ="ps2-queue";
}
演示效果
1) 先运行一下生产者产生exchange,启动消费者1和消费者2
2) 启动生产者
3) 查看消费者1
4) 查看消费者2
5) 查看控制台queue
6) 查看控制台exchange
routing模式
模式
描述
路由模式,是通过exchange路由器分发到不同queue队列中,其中queue队列需要绑定exchange。其是routing路由的一种类型,type=direct,这种模式是通过rountingKey的全匹配来路由,通过rountingKey将不同消息发送到不同queue队列中。特点:每个消费者只能获得rountingKey对应的消息。
代码
生产者
package com.lin.rabbit.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class RoutingProvider {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
Channel channel = con.createChannel();
//声明交换机
channel.exchangeDeclare(RoutingConstant.ROUTING_ROUTING_NAME, "direct");
//每次一条记录
channel.basicQos(1);
//发送信息
String msgerror = "hello, this is routing model! for error!";
channel.basicPublish(RoutingConstant.ROUTING_ROUTING_NAME, "error", null, msgerror.getBytes("UTF-8"));
System.out.println("send msg:"+msgerror);
String msginfo = "hello, this is routing model! for info!";
channel.basicPublish(RoutingConstant.ROUTING_ROUTING_NAME, "info", null, msginfo.getBytes("UTF-8"));
System.out.println("send msg:"+msginfo);
//关闭通道和连接
channel.close();
con.close();
}
}
消费者1
package com.lin.rabbit.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RoutingConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(RoutingConstant.ROUTING_1_QUEUE_NAME, false, false, false, null);
//每次一条记录
channel.basicQos(1);
//队列与路由器绑定
channel.queueBind(RoutingConstant.ROUTING_1_QUEUE_NAME,RoutingConstant.ROUTING_ROUTING_NAME, "error");
DefaultConsumer defaultConsumer = 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("receiver "+RoutingConstant.ROUTING_1_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(RoutingConstant.ROUTING_1_QUEUE_NAME, false, defaultConsumer);
}
}
消费者2
package com.lin.rabbit.routing;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class RoutingConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(RoutingConstant.ROUTING_2_QUEUE_NAME, false, false, false, null);
//每次一条记录
channel.basicQos(1);
//队列与路由器绑定
channel.queueBind(RoutingConstant.ROUTING_2_QUEUE_NAME,RoutingConstant.ROUTING_ROUTING_NAME, "error");
channel.queueBind(RoutingConstant.ROUTING_2_QUEUE_NAME,RoutingConstant.ROUTING_ROUTING_NAME, "info");
channel.queueBind(RoutingConstant.ROUTING_2_QUEUE_NAME,RoutingConstant.ROUTING_ROUTING_NAME, "warning");
DefaultConsumer defaultConsumer = 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("receiver "+RoutingConstant.ROUTING_2_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(RoutingConstant.ROUTING_2_QUEUE_NAME, false, defaultConsumer);
}
}
辅助类
package com.lin.rabbit.routing;
public class RoutingConstant {
public static String ROUTING_ROUTING_NAME ="routing-routing";
public static String ROUTING_1_QUEUE_NAME ="routing1-queue";
public static String ROUTING_2_QUEUE_NAME ="routing2-queue";
}
演示效果
1) 先运行一下生产者产生exchange,启动消费者1和消费者2
2) 启动生产者
3) 查看消费者1
4) 查看消费者2
5) 查看控制台queue
6) 查看控制台exchange
topic模式
模式
描述
主题模式,是通过exchange路由器分发到不同queue队列中,其中queue队列需要绑定exchange。其是routing路由的一种类型,type=topic,这种模式是通过rountingKey进行点通配符匹配(注意通配符是在“.”后面起效的,#代表0个或多个,*代表一个)来路由,通过rountingKey将不同消息发送到不同queue队列中。特点:每个消费者只能获得rountingKey对应的消息。
代码
生产者
package com.lin.rabbit.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
public class TopicProvider {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
Channel channel = con.createChannel();
//声明交换机
channel.exchangeDeclare(TopicConstant.TOPIC_ROUTING_NAME, "topic");
//每次一条记录
channel.basicQos(1);
//发送信息
String msgadd = "hello, this is topic model! for good.add!";
channel.basicPublish(TopicConstant.TOPIC_ROUTING_NAME, "good.add", null, msgadd.getBytes("UTF-8"));
System.out.println("send msg:"+msgadd);
String msgdelete = "hello, this is topic model! for good.delete!";
channel.basicPublish(TopicConstant.TOPIC_ROUTING_NAME, "good.delete", null, msgdelete.getBytes("UTF-8"));
System.out.println("send msg:"+msgdelete);
//关闭通道和连接
channel.close();
con.close();
}
}
消费者1
package com.lin.rabbit.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class TopicConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(TopicConstant.TOPIC_1_QUEUE_NAME, false, false, false, null);
//每次一条记录
channel.basicQos(1);
//队列与路由器绑定
channel.queueBind(TopicConstant.TOPIC_1_QUEUE_NAME,TopicConstant.TOPIC_ROUTING_NAME, "good.add");
DefaultConsumer defaultConsumer = 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("receiver "+TopicConstant.TOPIC_1_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(TopicConstant.TOPIC_1_QUEUE_NAME, false, defaultConsumer);
}
}
消费者2
package com.lin.rabbit.topic;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.lin.rabbit.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
public class TopicConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection con = ConnectionUtils.getConnection();
//创建通道
final Channel channel = con.createChannel();
//声明队列
channel.queueDeclare(TopicConstant.TOPIC_2_QUEUE_NAME, false, false, false, null);
//每次一条记录
channel.basicQos(1);
//队列与路由器绑定
channel.queueBind(TopicConstant.TOPIC_2_QUEUE_NAME,TopicConstant.TOPIC_ROUTING_NAME, "good.#");
DefaultConsumer defaultConsumer = 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("receiver "+TopicConstant.TOPIC_2_QUEUE_NAME+" msg:"+msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(TopicConstant.TOPIC_2_QUEUE_NAME, false, defaultConsumer);
}
}
辅助类
package com.lin.rabbit.topic;
public class TopicConstant {
public static String TOPIC_ROUTING_NAME ="topic-routing";
public static String TOPIC_1_QUEUE_NAME ="topic1-queue";
public static String TOPIC_2_QUEUE_NAME ="topic2-queue";
}
演示效果
1) 先运行一下生产者产生exchange,启动消费者1和消费者2
2) 启动生产者
3) 查看消费者1
4) 查看消费者2
5) 查看控制台queue
6) 查看控制台exchange