rabbitmq五大模型:
代码结构:
编写工具类生成连接:
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;
public class RabbitMqUtils {
private static ConnectionFactory connectionFactory;
static {
connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("123");
}
public static Connection getConnection(){
try {
return connectionFactory.newConnection();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
return null;
}
public static void closeConnectionAndChanel(Channel channel,Connection connection){
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1.直连模型
- 生产者,也就是要发送消息的程序
- 消费者:消息的接受者,会一直等待消息到来
- queue:消息队列,图中红色部分。类似一个邮箱,可以缓存消息;生产者向其中投递消息,消费者从其中取出消息
例子:
生产者:
package helloword;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
@Test
public void pro() throws IOException, TimeoutException {
//创建连接工厂
//创建连接mq的连接工厂对象
ConnectionFactory factory = new ConnectionFactory();
//设置连接rabbitmq主机
factory.setHost("127.0.0.1");
//设置端口号
factory.setPort(5672);
//设置访问虚拟主机的用户名和密码
factory.setUsername("ems");
factory.setPassword("123");
//设置连接那个虚拟主机
factory.setVirtualHost("/ems");
//获取连接对象
Connection connection = factory.newConnection();
//获取连接中通道
Channel channel = connection.createChannel();
//通道绑定对应消息队列
//参数1: 队列名称 如果队列不存在自动创建
//参数2: 用来定义队列特性是否要持久化 true 持久化队列 false 不持久化
//参数3: exclusive 是否独占队列 true 独占队列 false 不独占
//参数4: autoDelete: 是否在消费完成后自动删除队列 true 自动删除 false 不自动删除
//参数5: 额外附加参数
channel.queueDeclare("hello", true, false, false, null);
//发布消息
//参数1: 交换机名称 参数2:队列名称 参数3:传递息额外设置 参数4:消息的具体内容
channel.basicPublish("", "hello", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rabbitmq".getBytes());
//关闭连接
channel.close();
connection.close();
}
}
消费者:
package helloword;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Consumer {
public static void main(String[] args) throws IOException, TimeoutException {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(5672);
factory.setVirtualHost("/ems");
factory.setUsername("ems");
factory.setPassword("123");
Connection connection = factory.newConnection();
//创建通道
Channel channel = connection.createChannel();
//通道绑定队列:与生产端一致
channel.queueDeclare("hello", true, false, false, null);
//获取消息
//参数1: 消费那个队列的消息 队列名称
//参数2: 开始消息的自动确认机制[只要消费就从队列删除消息]
//参数3: 消费时的回调接口
channel.basicConsume("hello", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("取出消息:===>" + new String(body));
}
});
}
}
效果:
2.work模型:
Work queues,也被称为(Task queues),任务模型。当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。此时就可以使用 work 模型:让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的。
总的来说:生产者直接发送消息到队列,配置多个消费者
实例1:
生产者:
package workquene;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import utils.RabbitMqUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
for (int i=0 ; i<20; i++){
channel.basicPublish("","work", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rabbitmq".getBytes());
}
RabbitMqUtils.closeConnectionAndChanel(channel,connection);
}
}
消费者1:
package workquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
channel.basicConsume("work", true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("取出消息:===>" + new String(body));
}
});
}
}
消费者2:
package workquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
channel.basicConsume("work", true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("取出消息:===>" + new String(body));
}
});
}
}
效果:
实例2:
但使用这种模型有两个个缺陷,在消息平均分配给绑定该队列的多个消费者的基础上,其一:消费者的消费速度可能不一样,其二:所有消费者拿到消息以后还没有消费就进行了确认使得队列没有再保存消息了。倘若有消费者消息没消费完就宕机了,那么没有消费的消息就丢失了,这是绝不允许的,因此就出现了下面这种对rabbitmq的消息确认机制进行配置解决办法:
1.消费者每次只从消息队列取一条消息:
channel.basicQos(1);
2.关闭消息自动确认
//使用手动确认就要取消自动确认,第二参数设置为false
channel.basicConsume("work", false, new DefaultConsumer(channel)
3.手动确认消息:
channel.basicAck(envelope.getDeliveryTag(), false);
生产者:
package workquene2;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import utils.RabbitMqUtils;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
for (int i=0 ; i<20; i++){
channel.basicPublish("","work", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello rabbitmq".getBytes());
}
RabbitMqUtils.closeConnectionAndChanel(channel,connection);
}
}
消费者1:
package workquene2;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
//每次接收一个消息
channel.basicQos(1);
//使用手动确认就要取消自动确认,第二参数设置为false
channel.basicConsume("work", 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);
}
});
}
}
消费者2:
package workquene2;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work", true, false, false, null);
channel.basicQos(1);
channel.basicConsume("work", false, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("取出消息:===>" + new String(body));
channel.basicAck(envelope.getDeliveryTag(), false);
}
});
}
}
效果:
消费者1:
消费者2:
3.fanout模型(广播)
- 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
- 消费者将临时队列和交换机绑定,整个生产消费模型就连通了
- 交换机把消息发送给绑定过的所有队列
- 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
例子:
生产者:
public class Producer {
@Test
public void pro() throws IOException {
//获取连接
Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机 [ 参数一:交换机名字,参数2:交换机类型:fanout(广播模式) 固定]
channel.exchangeDeclare("register", "fanout");
//发布消息[参数1:交换机名字,参数2:路由,参数3:消息持久化,参数4:消息内容]
channel.basicPublish("register", "", null, "fanout...".getBytes());
//关闭
RabbitMQUtils.closeConnection(channel, connection);
}
}
消费者1:
package fanoutquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare("register", "fanout");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//[绑定]=>临时队列和交换机 [参数1:临时队列,参数2:交换机,参数3:路由]
channel.queueBind(queue, "register", "");
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
消费者2:
package fanoutquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare("register", "fanout");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列和交换机 [参数1:临时队列,参数2:交换机,参数3:路由]
channel.queueBind(queue, "register", "");
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
消费者3:
package fanoutquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer3 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare("register", "fanout");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列和交换机 [参数1:临时队列,参数2:交换机,参数3:路由]
channel.queueBind(queue, "register", "");
//消费消息
channel.basicConsume(queue, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者3号:==>" + new String(body));
}
});
}
}
细节:
- 临时队列在关闭消费者运行后自动删除,运行期间无法删除
- 先运行生产者发布消息再运行消费者是不行的, 先运行生产者交换机没有绑定队列可能自动丢弃了消息
效果:
4.Routing模型
有时候我们并不希望所有种类信息都发给绑定同一个交换机的队列,我们更希望绑定同一个交换机的队列能够接受特定种类的消息,当然,每条队列不只能够接受一种特定消息。Routing模型很好地满足了这种需求。
实例1:
生产者:
package routingquene;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.junit.Test;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Producer {
@Test
public void pro() throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
//创建通道
Channel channel = connection.createChannel();
//声明交换机[参数1:交换机名字, 参数2:交换机类型,direct路由模式]=>基于指令的 Routing key 转发
channel.exchangeDeclare("logs_direct", "direct");
//发布的路由名称==>根据路由key的不同发送到不同的绑定队列中
String key = "error";
//发布消息[参数1:交换机名字,参数2:路由名字,参数3:消息内容]
channel.basicPublish("logs_direct", key, null, ("发送给指定路由" + key + "的消息").getBytes());
//关闭连接
RabbitMqUtils.closeConnectionAndChanel(channel, connection);
}
}
消费者1:
package routingquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
//创建通道
Channel channel = connection.createChannel();
//声明交换机[参数1:交换机名字, 参数2:交换机类型,direct路由模式]
channel.exchangeDeclare("logs_direct", "direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列与交换机并设置指定路由名称
channel.queueBind(queue, "logs_direct", "info");
channel.queueBind(queue, "logs_direct", "error");
channel.queueBind(queue, "logs_direct", "warn");
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
消费者2:
package routingquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMqUtils.getConnection();
//创建通道
Channel channel = connection.createChannel();
//声明交换机[参数1:交换机名字, 参数2:交换机类型,direct路由模式]
channel.exchangeDeclare("logs_direct", "direct");
//创建临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列与交换机并设置指定路由名称
channel.queueBind(queue, "logs_direct", "error");
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
效果:
实例2:
修改生产者路由规则代码:
String key = "info";
效果:
- 交换机根据每条消息附带的路由规则key发送到队列名与路由规则key值相同的队列。
- 路由规则key在前三种模型中指的是队列
5.Topic模型
在Routing模型的基础上增加了通配符
- #匹配多个单词
- *匹配一个单词
# 统配符
*(star) can substitute for exactly one word. 匹配不多不少恰好1个词
#(hash) can substitute for zero or more words. 匹配一个或多个词
# 如:
audit.# 匹配audit.irs.corporate或者 audit.irs 等
audit.* 只能匹配 audit.irs
实例1:
生产者:
package topicquene;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import org.junit.Test;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Producer {
@Test
public void pro() throws IOException {
//创建连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机[交换机名字+交换机类型]
channel.exchangeDeclare("topics", "topic");
//发布消息==>使用动态路由(通配符方式)
String key = "user.update"; //指定发布的路由key
channel.basicPublish("topics", key, null, ("发送消息给指定的路由key" + key).getBytes());
//关闭连接
RabbitMqUtils.closeConnectionAndChanel(channel, connection);
}
}
消费者1:
package topicquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer1 {
public static void main(String[] args) throws IOException {
//创建连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare("topics", "topic");
//声明临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列与交换机并设置获取交换机中动态路由
String key = "user.*";//使用通配符指定路由key
channel.queueBind(queue, "topics", key);
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
消费者2:
package topicquene;
import com.rabbitmq.client.*;
import utils.RabbitMqUtils;
import java.io.IOException;
public class Consumer2 {
public static void main(String[] args) throws IOException {
//创建连接
Connection connection = RabbitMqUtils.getConnection();
Channel channel = connection.createChannel();
//声明交换机
channel.exchangeDeclare("topics", "topic");
//声明临时队列
String queue = channel.queueDeclare().getQueue();
//绑定临时队列与交换机
String key = "user.#";//使用通配符指定路由key
channel.queueBind(queue, "topics", key);
//消费消息
channel.basicConsume(queue, 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));
}
});
}
}
效果:
实例2:
修改生产者路由规则key:
String key = "user.update.test";
效果: