RabbitMq 七大发布消息之helloword最简单的发布和消费消息
RabbitMq 七大发布消息之Publish/Subscribe 发布/订阅,广播模式
一、概述
Routing 路由模式共有两种类型direct和Topics,direct可以直接绑定一个确定的routing-key,而topics则可以实现模糊动态的绑定相应的routing-key。
二.Routing之订阅模式--direct(直连):
在上一篇发布/订阅模式中的交换机type的fanout类型,是属于广播形式的,即一条消息可以被所有的订阅者消费,此种情况的我们的路由是可以为空的。但是,在某些场景下,我们希望不同的消息被不同的队列消费,做到专人专职的效果,此时就需要用到路由模式中的direct类型了。官方描述:https://www.rabbitmq.com/tutorials/tutorial-four-java.html
官方示意图如下:
如上图所示,当交换机type类型为 direct 时,当消息为error时C1和C2两个消费者都能消费,当消息为info和warning时则只能C2能进行消费,当消息为debug时,C1和C2均不会消费该条消息。
而所用于描述的error,info,warning等均表示为routing-key,即路由的键,不同的路由的键会转发不同的消息到不同的队列中,分门别类,各取所需。
基于路由 direct 类型的生产者消费者的代码:
1.工具类:
package utils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @Author:
* @Despriction:
* @Package: utils
* @Date:Created in 2020/3/23 8:44
* @Modify By:
*/
public class RabbitMqUtil {
private static ConnectionFactory connectionFactory;
static {
//第一步:创建rabbitmq的连接工厂
connectionFactory = new ConnectionFactory();
//第二步:设置连接的rabbitmq服务器的连接ip
connectionFactory.setHost("192.168.4.6");
//第三步:连接端口号,默认的为5672
connectionFactory.setPort(5672);
//第四步:设置连接哪个虚拟主机
connectionFactory.setVirtualHost("/ems");
//第五步:设置连接虚拟主机的用户和密码
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
}
//获取rabbitmq的连接
public static Connection getConnection() {
//第六步:获取连接对象
Connection connection = null;
try {
connection = connectionFactory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return connection;
}
public static void getClose(Connection connection, Channel channel) {
try {
if (channel != null) {
channel.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
2.生产者:生产者代码中的 routingKey 是可以动态进行更改的,此时为error,即生产error的消息
package direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.junit.Test;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction: routing 之 direct 生产消费模式
* @Package: direct
* @Date:Created in 2020/3/25 10:11
* @Modify By:
*/
public class Provider {
@Test
public void sendMsg() throws IOException {
//获取连接对象
Connection connection = RabbitMqUtil.getConnection();
//创建连接通道
Channel channel = connection.createChannel();
//通道连接交换机,若交换机不存在则会新建一个
String exchangeName = "logs_direct";
/**
* 参数1:交换机名称
* 参数2:交换机类型
*/
channel.exchangeDeclare(exchangeName,"direct");
//routing-key 名称,即该生产者生产基于什么路由的消息
String routingKey = "error";
//发布消息
channel.basicPublish(exchangeName,routingKey,false,null,("基于路由 direct 模式发布消息,路由key为:[" + routingKey +"]").getBytes());
//释放内存
RabbitMqUtil.getClose(connection,channel);
}
}
3.消费者1:routingKey 的值为error表示仅能接收消费 error形式的消息
package direct;
import com.rabbitmq.client.*;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction:
* @Package: direct
* @Date:Created in 2020/3/25 10:37
* @Modify By:
*/
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取rabbit连接
Connection connection = RabbitMqUtil.getConnection();
//创建通道
Channel channel = connection.createChannel();
//交换机名称
String exchangeName = "logs_direct";
//通道声明交换机和交换机类型
channel.exchangeDeclare(exchangeName,"direct");
//获取一个临时队列
String queueName = channel.queueDeclare().getQueue();
//routing-key 名称
String routingKey = "error";
//使用路由绑定队列和交换机
/**
* 参数1:队列名称
* 参数2:交换机名称
* 参数3:路由key
* 参数4:其他的一些参数,可封装到map中
*/
channel.queueBind(queueName,exchangeName,routingKey,null);
//消费消息
/**
* 参数1:队列名称
* 参数2:自动确认,可改为false进行手动确认
* 参数3:Consumer 接口的实现类,可以在此进行具体的逻辑处理
*/
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:" + new String(body));
}
});
}
}
4.消费者2:消费者2存在三个routingKey ,其值分别为 error,info,warning;即表示可以接收并消费这三种形式的消息
package direct;
import com.rabbitmq.client.*;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction:
* @Package: direct
* @Date:Created in 2020/3/25 10:37
* @Modify By:
*/
public class Consumer2 {
public static void main(String[] args) throws IOException {
//获取rabbbit连接
Connection connection = RabbitMqUtil.getConnection();
//新建连接通道
Channel channel = connection.createChannel();
//交换机名称
String exchangeName = "logs_direct";
//声明交换机的名称和类型
channel.exchangeDeclare(exchangeName,"direct");
//获取一个临时队列
String queueName = channel.queueDeclare().getQueue();
//路由key
String routingKey_error = "error";
String routingKey_info = "info";
String routingKey_warning = "warning";
//该通道使用路由将交换机和队列进行绑定
/**
* 参数1:队列名称
* 参数2:交换机名称
* 参数3:路由key
* 参数4:其他的一些参数,可封装到map中
*/
channel.queueBind(queueName,exchangeName,routingKey_error,null);
channel.queueBind(queueName,exchangeName,routingKey_info,null);
channel.queueBind(queueName,exchangeName,routingKey_warning,null);
//消费消息
/**
* 参数1:队列名称
* 参数2:自动确认,可改为false进行手动确认
* 参数3:Consumer 接口的实现类,可以在此进行具体的逻辑处理
*/
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:" + new String(body));
}
});
}
}
当生产者的 routingKey = “error” 时的控制台打印:
可见消费者1和消费者2都能消费生产消息为error形式的消息。
当生产者的 routingKey = “info” 时的控制台打印:
由图可知消费者1仍然只有上一次生产的error形式的消息,而消费者2多消费了一条生产消息为info的消息。
当生产者的 routingKey = “debug” 时的控制台打印:
生产者代码的更改
控制台打印:
消费者1和消费2均没有消费得到生产的debug形式的消息。
三.Routing之订阅模式--topics(主题):
topics类型的交换机与direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过topics类型可以让队列在绑定routingKey的时候使用通配符(* 和 #),而direct不能使用通配符,在灵活性上不如topics。topics类型的routingKey一般都是由一个或者多个单词组成,多个单词之间用 “.” (小数点)分隔开,例如:user.save.*;*.delete.#
官方文档:https://www.rabbitmq.com/tutorials/tutorial-five-java.html
通配符 * :可以正好替换一个单词。
通配符 #:可以替换零个或多个单词。
官方示意图如下:
代码如下:
工具类代码和之前的一致。
1.生产者代码:生产者的路由key为:"public.user.save"
package topics;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.junit.Test;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction:
* @Package: topics
* @Date:Created in 2020/3/25 15:17
* @Modify By:
*/
public class Provider {
@Test
public void sendMsg() throws IOException {
//获取rabbit连接
Connection connection = RabbitMqUtil.getConnection();
//创建通道
Channel channel = connection.createChannel();
//声明创建一个类型为topics的交换机
String exchangeName = "logs_topics";
/**
* 参数1:交换机名称
* 参数2:交换机类型
*/
channel.exchangeDeclare(exchangeName,"topic");
//声明一个RoutingKey
String routingKey = "public.user.save";
//发布消息
channel.basicPublish(exchangeName,routingKey,null,("基于动态路由 topic 模式发布消息,路由key为:[" + routingKey +"]").getBytes());
//释放内存
RabbitMqUtil.getClose(connection,channel);
}
}
2.消费者1代码:消费者1的路由的key:routingKey01 = "*.user.*";匹配规则是满足生产者的,因此控制台会打印出来
package topics;
import com.rabbitmq.client.*;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction:
* @Package: topics
* @Date:Created in 2020/3/25 15:30
* @Modify By:
*/
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtil.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "logs_topics";
channel.exchangeDeclare(exchangeName,"topic");
//获取一个临时队列
String queueName = channel.queueDeclare().getQueue();
String routingKey01 = "*.user.*";
channel.queueBind(queueName,exchangeName,routingKey01,null);
//消费消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者1:" + new String(body));
}
});
}
}
3.消费者2的代码:消费者2拥有三个routingKey,分别为:"#.user.#"; "public.*.*"; "*.save"; 其中01,02是满足生产者模式的,所以控制台也会有打印,但只有一条打印。
package topics;
import com.rabbitmq.client.*;
import utils.RabbitMqUtil;
import java.io.IOException;
/**
* @Author:
* @Despriction:
* @Package: topics
* @Date:Created in 2020/3/25 15:58
* @Modify By:
*/
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtil.getConnection();
Channel channel = connection.createChannel();
String exchangeName = "logs_topics";
channel.exchangeDeclare(exchangeName,"topic");
String queueName = channel.queueDeclare().getQueue();
String routingKey01 = "#.user.#";
String routingKey02 = "public.*.*";
String routingKey03 = "*.save";
channel.queueBind(queueName,exchangeName,routingKey01,null);
channel.queueBind(queueName,exchangeName,routingKey02,null);
channel.queueBind(queueName,exchangeName,routingKey03,null);
//消费消息
channel.basicConsume(queueName,true,new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者2:" + new String(body));
}
});
}
}
控制台打印: