RabbitMQ的几种模式简单测试Demo

1.rabbitMq的队列,工作,订阅,路由,主题模式测试demo 地址:

链接:https://pan.baidu.com/s/1xVkK3i1lSF0s1YW2IFtseA 
提取码:u2p5

知识点:

1.消息中间件的核心设计思想:
    采用异步通讯、自动补偿与重试、分布式事务、解决流量削峰问题、系统的解耦

2.消息中间件常用名词:
    Broker 消息转发端,消息中间件Server端;
    Message 发送的消息内容
    roducer 生产者,向Server端投递消息;
    Consumer 消费者,向Server端获取消息
    MessageId 消息全局id  解决消息幂等性问题    
    
3.主流的MQ对比分析
    ActiveMQ: 基本淘汰(老项目使用) 够轻巧(源代码比RocketMQ多),支持持久化到数据库,
                  对队列数较多的情况支持不好。
    RabbitMQ: 结合erlang语言本身的并发优势,支持很多的协议:AMQP,XMPP, SMTP, STOMP,
                  也正是如此,使的它变的非常重量级,更适合于企业级的开发。
    RocketMQ: 阿里系下开源的一款分布式、队列模型的消息中间件,原名Metaq,3.0版本名称改为RocketMQ,
                  是阿里参照kafka设计思想使用java实现的一套mq,同时将阿里系内部多款mq产品
                  (Notify、metaq)进行整合,只维护核心功能,去除了所有其他运行时依赖,
                  保证核心功能最简化,在此基础上配合阿里上述其他开源产品实现不同场景下mq的架构,
                  目前主要多用于订单交易系统。
    Kafka:    Apache下的一个子项目,使用scala实现的一个高性能分布式Publish/Subscribe消息队列系统,
                具有以下特性:高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
                高堆积:支持topic下消费者较长时间离线,消息堆积量大;

4.RabitMQ环境的基本安装
    1.下载并安装erlang,下载地址:http://www.erlang.org/download
    2.配置erlang环境变量信息
          新增环境变量ERLANG_HOME=erlang的安装地址
          将%ERLANG_HOME%\bin加入到path中
    3.下载并安装RabbitMQ,下载地址:http://www.rabbitmq.com/download.html
          注意: RabbitMQ 它依赖于Erlang,需要先安装Erlang。                
          
5.Virtual Hosts:
    像mysql有数据库的概念并且可以指定用户对库和表等操作的权限。那RabbitMQ呢?
    RabbitMQ也有类似的权限管理。在RabbitMQ中可以虚拟消息服务器VirtualHost,每
    个VirtualHost相当月一个相对独立的RabbitMQ服务器,每个VirtualHost之间是相互
    隔离的。exchange、queue、message不能互通。

6.公平队列实现原理
    Mq服务器端每次只会给消费者发送一条消息,如果消费者没有返回ack,就不会继续发送消
    息。
    
7.如何保证消息不丢失?
    1.生产者 确保我们的生产者将消息投递到MQ成功; 消息确认机制如果开启了消息持久化的机制,必须消息持久化成功才会应            答给生产者
         // 开启生产确认消息投递机制   channel.confirmSelect();
          channel.waitForConfirms()==true  投递成功
    2.消费者 确保我们的消费者消费消息成功 采用手动ack确认
    3.MQ服务器端 需要将数据持久化到我们的硬盘
     其他情况下:  硬盘坏了、持久化的过程断电了?    
    如何解决 最好通过表记录每次生产者投递消息,如果长期没有被消费,手动的补偿消费。
    
8.如果在生产者投递消息失败的情况,在那些场景?
    1.MQ挂了 解决做心跳检测(heartbeat),自动重启,多次重启失败发邮件运维
    2.Mq拒绝接受消息 (队列满了) 就采用手动补偿或者日志表记录下即可

9.Rabbitmq如何开启持久化的功能?
    1.默认的情况下mq服务器端创建队列和交换机都是持久化的
    2.如果是代码创建的话,将该值(durablet)设置为true

10.Rabbitmq发布订阅的实现原理:
    核心思想:
        一个生产者投递消息,可以被多个不同的队列实现消费;
    实现原理:
        多个不同的队列绑定相同交换机,生产者只需要将消息投递到交换机之后,
        在由交换机将消息转发到所有绑定的队列实现消费。    
        
11.Direct exchange(直连(路由)交换机)
    Fanout exchange(扇型(广播)交换机)
    Topic exchange(主题交换机)
    交换机核心作用:分发路由消息、中专
    队列:容器存放多个不同消息 遵循先进先出的原则
    消息:传递的参数
    路由键:交换机根据这样的路由键的值,发送不同的队列中 匹配过程
    Fanout(广播) 扇型交换机主要的特征:只要队列绑定同一个交换机,生产者将消息投递到交换机中,交换机会将消息发送给所有绑定的队列进行存放消息。
    Direct exchange(路由直连交换机):根据生产者投递不同的路由键,在交换机发送到实现匹配路由键的队列
    Topic 主题交换机:根据路由键的key实现模糊匹配到队列存放。        

rabbitMq连接工具类

public class ConnectionUtil {
    /**
     * 1.定义rabbmq地址 ip:端口
     * 2.定义虚拟主机
     * 3.定义用户名和密码
     */
    public static Connection getConnection() throws Exception {
        //定义连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置服务地址
        factory.setHost("127.0.0.1");
        //端口
        factory.setPort(5672);
        //设置账号信息,用户名、密码、vhost
        factory.setVirtualHost("/zsq");
        factory.setUsername("zsq");
        factory.setPassword("zsq");
        // 通过工程获取连接
        Connection connection = factory.newConnection();
        return connection;
    }
}

工作模式: 多个消费者 轮询处理

生产者

public class WorkProducer {
    //向队列中发送100条消息。 工作模式:多个人一起消费一个队列消息.内部轮询机制
    //定义队列名称
    private final static String QUEUE_NAME = "test_queue_work";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列 参数依次 队列名称  是否持久化  是否排它(仅此连接) 是否自动删除  其他构造参数
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        for (int i = 0; i < 10; i++) {
            // 消息内容
            String message = "我是工作模式" + i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println(QUEUE_NAME+" ==  Sent '" + message + "'");
        }
        channel.close();
        connection.close();
    }
}

消费者

public class WorkConsumerPoll {

    //工作模式:多个人一起消费一个队列消息.内部轮询机制
    //定义队列名称
    private final static String QUEUE_NAME = "test_queue_work";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列 参数依次 队列名称  是否持久化  是否排它(仅此连接) 是否自动删除  其他构造参数
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);

        //***方式一***
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,false表示手动返回完成状态,true表示自动  false表示手动返回ack
        channel.basicConsume(QUEUE_NAME, true, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println(QUEUE_NAME+" == Received '" + message + "'");
        }

        //***方式二***
//        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
//            @Override
//            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//                String msg = new String(body, "UTF-8");
//                System.out.println("短信消费者获取消息:" + msg);
//            }
//        };
//        //创建我们的监听的消息 auto Ack 默认自动签收  必须手动ack
//        channel.basicConsume(QUEUE_NAME, true, defaultConsumer);
    }
}

工作模式  公平队列,能者多劳

每次只发送一条,处理完后再发送 ,ack

生产者 ,开启生产投递确认机制(判断投递成功,才会继续投递下一条)

public class WorkProducerMore {
    //向队列中发送100条消息。 工作模式:多个人一起消费一个队列消息.能者多劳
    //定义队列名称
    private final static String QUEUE_NAME = "test_queue_work";
    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列 参数依次 队列名称  是否持久化  是否排它(仅此连接) 是否自动删除  其他构造参数
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        // **开启生产确认消息投递机制**
        channel.confirmSelect();
        for (int i = 0; i < 10; i++) {
            String message = "我是工作模式" + i;
            if(channel.waitForConfirms()) {  //***生产确认消息投递  true投递成功***
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
                System.out.println(QUEUE_NAME + " ==  Sent '" + message + "'");
            }
        }
        channel.close();
        connection.close();
    }
}

消费者(能者多劳)

public class WorkConsumerMore {

    //工作模式:多个人一起消费一个队列消息.能者多劳
    //定义队列名称
    private final static String QUEUE_NAME = "test_queue_work";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        // 声明队列 参数依次 队列名称  是否持久化  是否排它(仅此连接) 是否自动删除  其他构造参数
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        //定义消费数  每次只能消费一条记录.当消息执行后需要返回ack确认消息 才能执行下一条
        channel.basicQos(1);  //******
        //***方式一***
        // 定义队列的消费者
//        QueueingConsumer consumer = new QueueingConsumer(channel);
//        // 监听队列,false表示手动返回完成状态,true表示自动  false表示手动返回ack
//        channel.basicConsume(QUEUE_NAME, false, consumer);   //*****
//        // 获取消息
//        while (true) {
//            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
//            String message = new String(delivery.getBody());
//            System.out.println(QUEUE_NAME+" == Received '" + message + "'");
//            // 表示使用手动确认模式 从队列中删除该消息   deliveryTag 队列下标位置
//            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);  //******
//        }

        //***方式二***
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("短信消费者获取消息:" + msg);
                // 手动发送消息告诉给mq服务器端  从队列删除该消息
                channel.basicAck(envelope.getDeliveryTag(), false); //*****
            }
        };
        //创建我们的监听的消息 auto Ack 默认自动签收  必须手动ack
        channel.basicConsume(QUEUE_NAME, false, defaultConsumer); //*****
    }
}

发布订阅(广播fanout)模式

只要是绑定了当前交换机的队列都会收到消息

生产者

public class FanoutProducer {

    private final static String EXCHANGE_NAME = "test_exchange_fanout";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /*
           声明exchange
           fanout广播模式  把接收到的消息推送给所有它知道的队列
           direct 路由模式
           topic 主题模式
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        // 消息内容
        String message = "订阅模式!";
        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        System.out.println(EXCHANGE_NAME+" ==== Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}

消费者

public class FanoutConsumer1 {
    //发布订阅模式
    private final static String QUEUE_NAME = "test_queue_work1"; //定义队列名称

    private final static String EXCHANGE_NAME = "test_exchange_fanout";//定义交换机名称

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //定义交换机模式  // 声明exchange     fanout广播模式    redirect 路由模式    topic 主题模式
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        // 声明队列  参数依次 队列名称  是否持久化  是否排它(仅此连接) 是否自动删除  其他构造参数
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,false表示手动返回完成状态,true表示自动  false表示手动返回ack
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("发布订阅模式-消费者1  " + EXCHANGE_NAME+"    "+QUEUE_NAME+" ====  Received 1 '" + message + "'");
            //创建我们的监听的消息 auto Ack 默认自动签收  必须手动ack
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

路由模式(direct)

绑定交换机 ,并且路由key匹配的队列,才能够接收到消息.

生产者

public class DirectProducer {
    //定义交换机 路由模式
    private final static String EXCHANGE_NAME = "test_exchange_direct";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /*
           声明exchange
           fanout广播模式  把接收到的消息推送给所有它知道的队列
           direct 路由模式
           topic 主题模式
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "direct"); //参数分别是(交换机名 , 交换机类型 , 是否持久化)
        //开启生产确认消息投递机制
        channel.confirmSelect();
        //消息内容
        String message = "路由模式!";
        //  routingKey="insert"   匹配路由为insert的
        if(channel.waitForConfirms()) { //生产确认消息投递  true投递成功
            channel.basicPublish(EXCHANGE_NAME, "insert", null, message.getBytes());
            System.out.println(EXCHANGE_NAME + " ==== Sent '" + message + "'");
        }
        channel.close();
        connection.close();
    }
}

消费者

public class DirectConsumer {
    private final static String QUEUE_NAME = "test_exchange_direct_2"; //定义队列名称

    private final static String EXCHANGE_NAME = "test_exchange_direct";//定义交换机名称

    public static void main(String[] argv) throws Exception {

        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        //定义交换机模式  // 声明exchange     fanout广播模式    redirect 路由模式    topic 主题模式
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 绑定队列到交换机  路由为update  和 insert
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "update");
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "insert"); //******
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("路由模式-消费者2  " + EXCHANGE_NAME+"    "+QUEUE_NAME+" ====  Received 2 '" + message + "'");
            //创建我们的监听的消息 auto Ack 默认自动签收  必须手动ack
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

主题(通配符)模式(topic)

根据路由键的key实现模糊匹配到队列,匹配到的队列能接收到消息

生产者

public class TopicProducer {

    //主题模式(通配符模式)  定义交换机
    private final static String EXCHANGE_NAME = "test_exchange_topic";

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        /*
           声明exchange
           fanout广播模式  把接收到的消息推送给所有它知道的队列
           direct 路由模式
           topic 主题模式
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        //消息内容
        String message = "主题模式!";
        //  routingKey="routekey.test.app"
        channel.basicPublish(EXCHANGE_NAME, "routekey.test.app", null, message.getBytes());//*****
        System.out.println(EXCHANGE_NAME+" ==== Sent '" + message + "'");
        channel.close();
        connection.close();
    }
}

消费者

public class TopicConsumer {
    // 主题模式(通配符模式) *号只匹配一个  #号匹配一个词或多个
    private final static String QUEUE_NAME = "test_queue_topic_work_3"; //定义队列名称

    private final static String EXCHANGE_NAME = "test_exchange_topic";//定义交换机名称

    public static void main(String[] argv) throws Exception {
        // 获取到连接以及mq通道
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();

        //定义交换机模式  // 声明exchange     fanout广播模式    redirect 路由模式    topic 主题模式
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");

        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        
        // 绑定队列到交换机    routingKey  通配"routekey.test.app"
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "#.test.*");
        // 同一时刻服务器只会发一条消息给消费者
        channel.basicQos(1);
        // 定义队列的消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 监听队列,手动返回完成
        channel.basicConsume(QUEUE_NAME, false, consumer);
        // 获取消息
        while (true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("主题模式-消费者3  " + EXCHANGE_NAME+"    "+QUEUE_NAME+" ====  Received 3 '" + message + "'");
            //创建我们的监听的消息 auto Ack 默认自动签收  必须手动ack
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值