消息队列之RabbitMQ

消息队列

消息队列的使用

消息队列是一个独立的服务器,它是为了防止微服务或者mysql(数据)出问题,也降低Controller与微服务的耦合性,从而提高的程序的可靠性。
在这里插入图片描述

消息队列可解决偶然的的高并发,但不能解决长期的高并发(如商城的短期秒杀),可用消息队列解决短期高并发的成本。如果长期高并发需要增加服务器解决高并发。
在这里插入图片描述
消息队列可进行异步请求处理(在为分布式开发中),如果是单体项目可用Thread线程解决高并发问题。
在这里插入图片描述
单机/单体项目:一台服务器

这是同步请求

Controller{
写数据库
发邮件
发信息
}

把同步变异步

Controller{
new Thread{run{写数据库}}

new Thread{run{发邮件}}

new Thread{run{发信息}}
}

多布式

把消息发到消息服务器(有进必有出)
有一个服务器从消息队列服务器获取信息写数据库。
有一个服务器从消息队列服务器获取信息发邮件。
有一个服务器从消息队列服务器获取信息发信息。

消息队列

MQ全称Message Queue ,消息队列(MQ)是一种应用程序对应用程序的通信方法。
消息队列是一个程序写入数据队列中,另一个程序从队列中获取信息,并通过RPC调用,可传递JAVA对象,方便操作。

缺点

增加程序的复杂度

消息队列的产品

在这里插入图片描述

RabbitMq特点

1.高可用,能搭建集群服务器
(集群服务器的两种形式:第一种:主重。第二种:无主。任何服务器都能搭建集群)
2.可扩展,有大量插件
3.有消息确认机制
4.有持久化机制,可靠性高
5.开源

RabbitMQ五种模式

简单模式

使用默认交换机 default exchange

在这里插入图片描述

从图上看,和JDK本身一样,生产者往队列添加数据,消费者从队列拿数据,如果业务场景确实这么简单,还可以使用redis的集合来代替,减少整个系统的复杂度,系统越简单问题越少

测试代码

package com.test.simple_model;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.platform.commons.logging.LoggerFactory;

import java.util.logging.Logger;


/**
 * @ProjectName: testWork
 * @Package: PACKAGE_NAME
 * @ClassName: TestRabbitMQ
 * @Author: ningshuai
 * @Description:   RabbitMQ的简单模式
 * @Date: 2020/6/11 17:10
 * @Version: 1.0
 */
public class Test_Simple_Model {
    Logger logger = (Logger) LoggerFactory.getLogger(Test_Simple_Model.class);
    private ConnectionFactory factory;
    // 初始化连接工厂
    @Before
    public void init() {
        factory = new ConnectionFactory();
        // 设置相关参数
        factory.setHost("192.168.2.127");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        factory.setUsername("Admin");
        factory.setPassword("admin");
    }

    /**、
     * 消息发送者或者是生产者
     * @throws Exception
     */
    @Test
    public void simpleSend() throws Exception {
        // 1.获取连接
        Connection conn = factory.newConnection();
        // 2.从连接获取信道
        Channel channel = conn.createChannel();
        // 3.利用channel声明一个队列
        /*
         queue 表示声明的queue对列的名字    “simple”
         durable 表示是否持久化   false持久化
         exclusive 表示当前声明的queue是否被当前信道独占
            true:当前连接创建的任何channel都可以连接该queue
            false:只有当前channel可以连接该queue
         autoDelete Boolean类型:在最后连接使用完成后,是否删除队列,false
         arguments 其他声明参数封装到map中传递给mq
         */
        channel.queueDeclare("simple", false, false, false, null);

        // 4.发送消息
        /*
        exchange  交换机名称,简单模式使用默认交换,该值设置为""
        routingkey 当前的消息绑定的routingkey,简单模式下,与队列同名即可
        props  消息的属性字段对象,例如BasicProperties,可以设置一个deliveryMode的值0 持久化,1 表示不持久化,durable配合使用
        body  消息字符串的byte数组
        */
        channel.basicPublish("", "simple", null, "简单模式的消息发送".getBytes());

    }

    /**
     * 消息接收者
     * @throws Exception
     */
    @Test
    public void simpleReciever() throws Exception {
        // 1.获取连接
        Connection conn = factory.newConnection();
        // 2.获取信道
        Channel channel = conn.createChannel();
        // 3.绑定队列
        channel.queueDeclare("simple", false, false, false, null);
        // 4.创建一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 5.绑定消费者和队列
        channel.basicConsume("simple", consumer);
        // 6.获取消息
        while(true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg=new String(delivery.getBody());
            System.out.println(msg);
        }
    }
}

工作模式

在这里插入图片描述一个队列由多个消费者共享,如果消费者处理速度落后于生产者,可以不断扩充消费,提高消息的处理能力

注意:这种模式队列的数据一旦被其中一个消费者拿走,其他消费者就不会再拿到,与下面的订阅发布模式不一样,它提供了两个队列,消息有两份

测试代码

package com.test.simple_model;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.platform.commons.logging.LoggerFactory;

import java.util.logging.Logger;


/**
 * @ProjectName: testWork
 * @Package: com.test.simple_model
 * @ClassName: Test_Work_Model
 * @Author: ningshuai
 * @Description:    RabbitMQ的工作模式
 * @Date: 2020/6/11 18:03
 * @Version: 1.0
 */
public class Test_Work_Model {

    Logger logger = (Logger) LoggerFactory.getLogger(Test_Work_Model.class);
    private ConnectionFactory factory;
    // 初始化连接工厂
    @Before
    public void init() {
        factory = new ConnectionFactory();
        // 设置相关参数
        factory.setHost("192.168.2.127");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        factory.setUsername("Admin");
        factory.setPassword("admin");
    }

    /**
     *
     * @throws Exception
     */
    @Test
    public void workSender() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();
        channel.queueDeclare("work", false, false, false, null);

        for(int i=0;i<100;i++) {
            channel.basicPublish("", "work", null, ("工作模式发送的第 ("+i+") 个消息").getBytes());
        }
    }

    /**
     *
     * @throws Exception
     */
    @Test
    public void workReceiver_a() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();
        channel.queueDeclare("work", false, false, false, null);

        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //其中第二参数表示消费者接收消息后是否自动返回回执
        channel.basicConsume("work", false, consumer);

        while(true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg=new String(delivery.getBody());
            logger.info(msg);
            Thread.sleep(50);
            //手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }

    /**
     *
     * @throws Exception
     */
    @Test
    public void workReceiver_b() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();
        channel.queueDeclare("work", false, false, false, null);

        channel.basicQos(1);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        //其中第二参数表示消费者接收消息后是否自动返回回执
        channel.basicConsume("work", false, consumer);

        while(true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String msg=new String(delivery.getBody());
            logger.info(msg);
            Thread.sleep(100);
            //手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

订阅发布模式

在这里插入图片描述生产者将消息发送交换机,交换机在将消息发给N个队列,消费者连到响应队列取消息即可,此功能比较适合将某单一系统的简单业务数据消息广播给所有接口

测试代码

package com.test.simple_model;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import org.junit.Before;
import org.junit.Test;
import org.junit.platform.commons.logging.LoggerFactory;

import java.util.logging.Logger;

/**
 * @ProjectName: testWork
 * @Package: com.test.simple_model
 * @ClassName: Test_Subscription_Model
 * @Author: ningshuai
 * @Description:  RabbitMQ订阅模式
 * @Date: 2020/6/12 10:12
 * @Version: 1.0
 */
public class Test_Subscription_Model {
    Logger logger = (Logger) LoggerFactory.getLogger(Test_Subscription_Model.class);
    private ConnectionFactory factory;
    // 初始化连接工厂
    @Before
    public void init() {
        factory = new ConnectionFactory();
        // 设置相关参数
        factory.setHost("192.168.2.127");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        factory.setUsername("Admin");
        factory.setPassword("admin");
    }

    @Test
    public void fanoutSender() throws Exception {
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        // 创建交换机
        /*
         *  参数:
         *      Exchange: 自定义交换机名称,接受端声明交换机的名字需和它保持一致
         *      type:     交换机类型,取值范围(fanout(订阅/发布),direct(路由模式),topic(主题))
         */
        channel.exchangeDeclare("fanoutEx", "fanout");
        // 发送消息
        for (int i = 0; i < 100; i++) {
            channel.basicPublish("fanoutEx", "", null, ("订阅/发布模式发送的第 (" + i + ") 个消息").getBytes());
        }
    }

    @Test
    public void fanoutReceiver() throws Exception {
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        //创建队列
        channel.queueDeclare("fanout", false, false, false, null);

        //创建交换机
        channel.exchangeDeclare("fanoutEx", "fanout");

        //绑定队列和交换机
        channel.queueBind("fanout", "fanoutEx", "");
        channel.basicQos(1);
        //创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //绑定消费者和队列
        channel.basicConsume("fanout", consumer);
        //取数据
        while(true) {
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            logger.info(new String(delivery.getBody()));
            // 手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

路由模式

在这里插入图片描述两个消费者,可以更改生产者的routingKey观察消费者获取数据的变化。从观察结果可以看到,生产者的routingKey和消费者指定的routingKey完全一致,消费者才能拿到消息

测试代码

package com.test.simple_model;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import org.junit.Before;
import org.junit.Test;
import org.junit.platform.commons.logging.LoggerFactory;

import java.util.logging.Logger;

/**
 * @ProjectName: testWork
 * @Package: com.test.simple_model
 * @ClassName: Test_Rout_Model
 * @Author: ningshuai
 * @Description: RabbitMQ的路由模式
 * @Date: 2020/6/12 10:22
 * @Version: 1.0
 */
public class Test_Rout_Model {
    Logger logger = (Logger) LoggerFactory.getLogger(Test_Subscription_Model.class);
    private ConnectionFactory factory;
    // 初始化连接工厂
    @Before
    public void init() {
        factory = new ConnectionFactory();
        // 设置相关参数
        factory.setHost("192.168.2.127");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        factory.setUsername("Admin");
        factory.setPassword("admin");
    }

    @Test
    public void routingSender() throws Exception {
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        // 创建交换机
        /*
         *  参数:
         *      Exchange: 自定义交换机名称,接受端声明交换机的名字需和它保持一致
         *      type:     交换机类型,取值范围(fanout(订阅/发布),direct(路由模式),topic(主题))
         */
        channel.exchangeDeclare("directEx", "direct");
        // 发送消息
        for (int i = 0; i < 100; i++) {
            channel.basicPublish("directEx", "receiver_b", null, ("路由模式发送的第 (" + i + ") 个消息").getBytes());
        }
    }

    @Test
    public void routingReceiver_a() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        //创建队列
        channel.queueDeclare("direct_a", false, false, false, null);

        //创建交换机
        channel.exchangeDeclare("directEx", "direct");

        //绑定队列和交换机
        channel.queueBind("direct_a", "directEx", "receiver_a");
        channel.basicQos(1);
        //创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //绑定消费者和队列
        channel.basicConsume("direct_a", consumer);
        //取数据
        while(true) {
            Delivery delivery = consumer.nextDelivery();
            logger.info(new String(delivery.getBody()));
            // 手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
    @Test
    public void routingReceiver_b() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        //创建队列
        channel.queueDeclare("direct_b", false, false, false, null);

        //创建交换机
        channel.exchangeDeclare("directEx", "direct");

        //绑定队列和交换机
        channel.queueBind("direct_b", "directEx", "receiver_b");
        channel.basicQos(1);
        //创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //绑定消费者和队列
        channel.basicConsume("direct_b", consumer);
        //取数据
        while(true) {
            Delivery delivery = consumer.nextDelivery();
            logger.info(new String(delivery.getBody()));
            // 手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

主题模式

在这里插入图片描述主题模式从使用上看,就是支持ANT,用*代表一个词,#代表多个词,否则就是精确匹配,感觉路由模式就是特殊的主题模式(没有使用ANT的通配符),具体原理现在还没去研究,先用起来再说

测试代码

package com.test.simple_model;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import org.junit.Before;
import org.junit.Test;
import org.junit.platform.commons.logging.LoggerFactory;

import java.util.logging.Logger;

/**
 * @ProjectName: testWork
 * @Package: com.test.simple_model
 * @ClassName: Test_Theme_Model
 * @Author: ningshuai
 * @Description: RabbitMQ的主题模式
 * @Date: 2020/6/12 10:25
 * @Version: 1.0
 */
public class Test_Theme_Model {

    Logger logger = (Logger) LoggerFactory.getLogger(Test_Subscription_Model.class);
    private ConnectionFactory factory;
    // 初始化连接工厂
    @Before
    public void init() {
        factory = new ConnectionFactory();
        // 设置相关参数
        factory.setHost("192.168.2.127");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        factory.setUsername("Admin");
        factory.setPassword("admin");
    }
    @Test
    public void topicSender() throws Exception {
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        // 创建交换机
        /*
         *  参数:
         *      Exchange: 自定义交换机名称,接受端声明交换机的名字需和它保持一致
         *      type:     交换机类型,取值范围(fanout(订阅/发布),direct(路由模式),topic(主题))
         */
        channel.exchangeDeclare("topicEx", "topic");
        // 发送消息
        for (int i = 0; i < 100; i++) {
            channel.basicPublish("topicEx", "acct.save", null, ("主题模式发送的第 (" + i + ") 个消息").getBytes());
        }
    }

    @Test
    public void topicReceiver_a() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        //创建队列
        channel.queueDeclare("topic_a", false, false, false, null);

        //创建交换机
        channel.exchangeDeclare("topicEx", "topic");

        //绑定队列和交换机
        channel.queueBind("topic_a", "topicEx", "acct.save");
        channel.basicQos(1);
        //创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //绑定消费者和队列
        channel.basicConsume("topic_a", consumer);
        //取数据
        while(true) {
            Delivery delivery = consumer.nextDelivery();
            logger.info(new String(delivery.getBody()));
            // 手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
    @Test
    public void topicReceiver_b() throws Exception{
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        //创建队列
        channel.queueDeclare("topic_b", false, false, false, null);

        //创建交换机
        channel.exchangeDeclare("topicEx", "topic");

        //绑定队列和交换机
        //channel.queueBind("topic_b", "topicEx", "acct.update");
        channel.queueBind("topic_b", "topicEx", "acct.*");
        channel.basicQos(1);
        //创建消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        //绑定消费者和队列
        channel.basicConsume("topic_b", consumer);
        //取数据
        while(true) {
            Delivery delivery = consumer.nextDelivery();
            logger.info(new String(delivery.getBody()));
            // 手动发送回执
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        }
    }
}

代码测试部分转载于:https://blog.csdn.net/weixin_33809981/article/details/92373083

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值