Rabbitmq的几种工作模式
- 简单模式 HelloWorld
- 工作队列模式 Work Queue
- 发布订阅模式 Publish/subscribe
- 路由模式 Routing
- 通配符模式 Topic
- rpc
-
publisher confirms
先记录一下前5种,不整合spring
首先创建虚拟机VirtualHost,虚拟机可以理解为数据库,每个虚拟机之间是隔离的,消息不能相互传递
创建用户的时候有几种模式
角色说明:
1、 超级管理员(administrator) 可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操 作。
2、 监控者(monitoring) 可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用 情况,磁盘使用情况等)
3、 策略制定者(policymaker) 可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上 图红框标识的部分)。
4、 普通管理者(management) 仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
5、 其他 无法登陆管理控制台,通常就是普通的生产者和消费者。
创建链接
public class RabbitUtils {
private static ConnectionFactory connectionFactory = new ConnectionFactory ();
static {
//创建链接
connectionFactory.setHost ("192.168.137.129");
connectionFactory.setPort (5672);
connectionFactory.setUsername ("duoduo");
connectionFactory.setPassword ("duoduo");
connectionFactory.setVirtualHost ("/duoduo");
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = connectionFactory.newConnection ();
return conn;
} catch (Exception e) {
throw new RuntimeException (e);
}
}
}
简单模式一个生产者一个消费者,不需要创建exchange (交换机),会默认创建一个交换机
生产者
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//获取TCP长连接
Connection conn = RabbitUtils.getConnection();
//创建通信“通道”,相当于TCP中的虚拟连接
Channel channel = conn.createChannel();
//创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
//第一个参数:队列名称ID
//第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
//第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
//第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
//其他额外的参数, null
channel.queueDeclare("helloworld",false, false, false, null);
String message = "helloworldRabbitMq";
//四个参数
//exchange 交换机,暂时用不到,在后面进行发布订阅时才会用到
//队列名称
//额外的设置属性
//最后一个参数是要传递的消息字节数组
channel.basicPublish("", "helloworld", null,message.getBytes());
channel.close();
conn.close();
System.out.println("===发送成功===");
}
}
消费者
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
//获取TCP长连接
Connection conn = RabbitUtils.getConnection ();
//创建通道
Channel channel = conn.createChannel ();
//创建队列,声明并创建一个队列,如果队列已存在,则使用这个队列
//第一个参数:队列名称ID
//第二个参数:是否持久化,false对应不持久化数据,MQ停掉数据就会丢失
//第三个参数:是否队列私有化,false则代表所有消费者都可以访问,true代表只有第一次拥有它的消费者才能一直使用,其他消费者不让访问
//第四个:是否自动删除,false代表连接停掉后不自动删除掉这个队列
//其他额外的参数, null
channel.queueDeclare ("helloworld", false, false, false, null);
//从MQ服务器中获取数据
//创建一个消息消费者
//第一个参数:队列名
//第二个参数代表是否自动确认收到消息,false代表手动编程来确认消息,这是MQ的推荐做法
//第三个参数要传入DefaultConsumer的实现类
channel.basicConsume ("helloworld", false, new DefaultConsumer (channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String (body);
System.out.println ("消费者接收到的消息:" + message);
//每生产一个消息自增1,作为消息的id,保证消息不被重复的消费
System.out.println ("消息的TagId:" + envelope.getDeliveryTag ());
//false只确认签收当前的消息,设置为true的时候则代表签收该消费者所有未签收的消息
Channel _channel = super.getChannel ();
//将消息的id确定为已经消费,后续会分析如何保证消息不丢失和不重复消费消息
_channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
WorkQueue工作队列模式,一个生产者多个消费者,因为消费的是同一个队列,所以消费者之间是竞争关系,不用exchange,底层会有默认的交换机
和上一个模式是一样的,只不过多个客户端同时绑定同一个队列
客户端轮询的消费队列里面的消息,ABC三个客户端,共有90个消息,C服务器的性能不好,消费的时间长一点,但是ABC三个客户端每个都会消费30个消息
这里可以用channel.basicQos(1);消费完消息之后从队列马上取一条新消息
A的性能最好再B再C,使用channel.basicQos(1);这段代码之后A可能消费60条消息 B消费20条C只消费了10条
发布订阅模式 Publish/Subscribe
需要将交换机的类型定义为fanout,并将交换机和队列进行绑定,不需要设定routing key,生产者将消息发送给交换机,由交换机将消息发送给绑定的队列,再由消费者获取消息.
生产者
public class Producer {
public static void main(String[] args) throws Exception {
Connection connection = RabbitUtils.getConnection ();
Channel channel = connection.createChannel ();
String message = "发布订阅模式";
//定义了交换机,交换机是要事先创建好,如果是spring可以用程序取初始化,这里我们用页面的方式创建,发布订阅模式的交换机的模式要选择fanout
//第一个参数为交换机 第二个参数为routingkey,因为是发布订阅模式,不需要定义,第三个参数要发送的消息
for (int i = 0; i < 10; i++) {
channel.basicPublish ("fanoutTest", "", null, message.getBytes ());
}
channel.close ();
connection.close ();
}
}
两个消费者
public class Consumers {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("pubsub", false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:routing key 因为是发布订阅模式,不是要路由key
channel.queueBind ("pubsub", "fanoutTest", "");
channel.basicQos (1);
channel.basicConsume ("pubsub", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("pubsub1", false, false, false, null);
//queueBind用于将队列与交换机绑定
//参数1:队列名 参数2:交互机名 参数三:routing key 因为是发布订阅模式,不是要路由key
channel.queueBind ("pubsub1", "fanoutTest", "");
channel.basicQos (1);
channel.basicConsume ("pubsub1", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
可以发现queueBind方法将两个队列同时绑定了同一个交换机,并且没有声明路由key,发布订阅模式不需要定义路由key,当生产者发布消息的时候,首先发送给交换机,由交换机同时转发给两个队列,保证两个消费者都能获取到
路由模式 Routing
需要设置类型为 direct 的交换机,交换机和队列进行绑定,并且指定 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。这里是routing key精确匹配
首先创建模式为direct模式的交换机
生产者
public class Producer {
public static void main(String[] args) throws Exception {
Map stringStringMap = new HashMap ();
stringStringMap.put("routing","路由模式");
stringStringMap.put("routing1","路由模式1");
Connection connection = RabbitUtils.getConnection ();
Channel channel = connection.createChannel ();
for (Map.Entry stringStringEntry : stringStringMap.entrySet ()) {
//路由模式定义了路由key,这里将map的key作为路由key
channel.basicPublish ("directTest",stringStringEntry.getKey () , null, stringStringEntry.getValue ().getBytes ());
}
channel.close ();
connection.close ();
}
}
消费者
public class Consumers {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("routing", false, false, false, null);
//第三个参数为路由key,路由模式是路由key要精确匹配
channel.queueBind ("routing", "directTest", "routing");
channel.basicQos (1);
channel.basicConsume ("routing", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("routing1", false, false, false, null);
//第三个参数为路由key,路由模式是路由key要精确匹配
channel.queueBind ("routing1", "directTest", "routing1");
channel.basicQos (1);
channel.basicConsume ("routing1", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
通配符模式 Topic
需要设置类型为 topic 的交换机,交换机和队列进行绑定,并且指定通配符方式的 routing key,当发送消息到交换机后,交换机会根据 routing key 将消息发送到对应的队列。利用通配符的方式发送到不同的队列
通配符的#和的区别,可以上网找找资料,其实只能代表匹配一个单词,#可以代表多个单词
生产者
public class Producer {
public static void main(String[] args) throws Exception {
Map stringStringMap = new HashMap ();
stringStringMap.put("a.b.c.d","通配符模式a.b.c.d");
stringStringMap.put("1.2.3.d","通配符模式1.2.3.d");
Connection connection = RabbitUtils.getConnection ();
Channel channel = connection.createChannel ();
for (Map.Entry stringStringEntry : stringStringMap.entrySet ()) {
//路由模式定义了路由key,这里将map的key作为路由key
channel.basicPublish ("topicTest",stringStringEntry.getKey () , null, stringStringEntry.getValue ().getBytes ());
}
channel.close ();
connection.close ();
}
}
消费者
public class Consumers {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("topic", false, false, false, null);
//第三个参数为路由key,主题模式是路由key模糊匹配,#匹配多个单词
channel.queueBind ("topic", "topicTest", "#.d");
channel.basicQos (1);
channel.basicConsume ("topic", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取TCP长连接
Connection connection = RabbitUtils.getConnection ();
//获取虚拟连接
final Channel channel = connection.createChannel ();
//声明队列信息
channel.queueDeclare ("topic1", false, false, false, null);
//第三个参数为路由key,主题模式是路由key模糊匹配,*代表匹配一个单词
channel.queueBind ("topic1", "topicTest", "*.*.*.d");
channel.basicQos (1);
channel.basicConsume ("topic1", false, new DefaultConsumer (channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println ("接收消息:" + new String (body));
channel.basicAck (envelope.getDeliveryTag (), false);
}
});
}
}
得到的结果两个消费者都能消费到两个消息
接收消息:通配符模式a.b.c.d
接收消息:通配符模式1.2.3.d
也就是 ..*.d能同时匹配 a.b.c.d 1.2.3.d
同事#.d也能同时匹配a.b.c.d 1.2.3.d