深入浅出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";

效果:
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fire king

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值