【rabbitMQ】真就一篇就够!!

rabbitmq消息队列

什么是异步调用,什么是同步调用?

同步调用?
  • A服务调用B服务,需要等待B服务执行完毕之后才能继续执行,才能往后执行

  • 同步调用有RestTimplate,ribbon,Feign,Dubbo

异步调用?
  • A服务调用服务B,b在执行的过程中可以往下执行,无需等待服务B的执行结果
  • 通过消息队列可以实现异步调用

消息队列概念?
  • MQ全称为Message Queue,消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。
  • 消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

其他的mq介绍

  • RabbitMQ 稳定可靠,数据一致,支持多协议,有消息确认,基于erlang语言
  • Kafka 高吞吐,高性能,快速持久化,无消息确认,无消息遗漏,可能会有有重复消息,依赖于zookeeper,成本高.
  • ActiveMQ 不够灵活轻巧,对队列较多情况支持不好.
  • RocketMQ 性能好,高吞吐,高可用性,支持大规模分布式,协议支持单一

RabbitMQ安装及配置

基于 CentOS 安装 RabbitMQ 3.7

说明:推荐使用本地安装,减少网络依赖

1.安装前准备
  • 如果之前安装过erlang,先删除

    yum remove erlang*
    
  • 安装C++编译环境

    # yum -y install make gcc gcc-c++
    yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel unixODBC unixODBC-devel httpd python-simplejson
    
  • 下载erlang和rabbitMQ

    # 下载erlang
    wget http://www.erlang.org/download/otp_src_20.1.tar.gz
    
    # 下载rabbitMQ
    wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.0/rabbitmq-server-generic-unix-3.7.0.tar.xz
    
2.安装erlang
  • 解压erlang安装包

    tar -xvf otp_src_20.1.tar.gz
    
  • 进入解压文件夹

    cd otp_src_20.1
    
  • 指定安装目录及安装配置(需要先安装并配置JDK)

    # erlang指定安装在/usr/local/erlang目录 
    ./configure --prefix=/usr/local/erlang --enable-smp-support --enable-threads --enable-sctp --enable-kernel-poll --enable-hipe --with-ssl --without-javac
    
  • 编译与安装

    make && make install
    
  • 配置erlang环境变量

    vi /etc/profile
    
    • 将 export PATH=$PATH:/usr/local/erlang/bin 添加到文件末尾

  • 重新加载profile文件

    source /etc/profile
    
3.安装RabbitMQ
  • 解压RabbitMQ安装包

    • 由于下载的安装包为xz文件,先将xz解压为tar
    xz -d rabbitmq-server-generic-unix-3.7.0.tar.xz
    

    • 再解压缩tar文件
    tar -xvf rabbitmq-server-generic-unix-3.7.0.tar
    
  • 启动RabbitMQ

    • 进入到解压的RabbitMQ的sbin目录
    cd rabbitmq_server-3.7.0/sbin
    
    • 启动
    ./rabbitmq-server -detached
    
    • 查看进程
    ps aux|grep rabbit
    #ps a 显示现行终端机下的所有程序,包括其他用户的程序。
    #ps u   以用户为主的格式来显示程序状况。
    #ps x   显示所有程序,不以终端机来区分。
    
4.启动管理界面
  • 启动RabbitMQ的管理系统插件(需进入sbin目录)

    ./rabbitmq-plugins enable rabbitmq_management
    

5.放行端口

如果没有网络指令需要先安装:yum install net-tools

  • 查看并放行端口

  • 也可以直接关闭防火墙

    • CentOS7

      #关闭防火墙 
      systemctl stop firewalld
      #开机禁用 
      systemctl disable firewalld
      #查看状态
      systemctl status firewalld
      
    • CentOS6

      #1.永久性生效,重启后不会复原
      #开启: 
      chkconfig iptables on
      #关闭: 
      chkconfig iptables off
      
      #2.即时生效,重启后复原
      #开启: 
      service iptables start
      #关闭: 
      service iptables stop
      
      #3.查询TCP连接情况:
      netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
      
      #4.查询端口占用情况:
      netstat   -anp   |   grep  portno(例如:netstat –apn | grep 80)
      
  • 云服务器需要在控制台添加“安全组设置”

    • 阿里云服务器

      )

    • 华为云服务器

rabbitMQ的逻辑结构

rabbitMQ的用户管理

创建用户
  1. 命令创建
# 到/usr/local/rabbitmq_server-3.7.0/sbin目录下执行
./rabbitmqctl add_user username password
  1. 通过web端的管理工具创建

用户权限
用户级别
  • 超级管理员administrator,可以登录控制台,查看所有信息,可以对用户和策略进行操作
  • 监控者monitoring,可以登录控制台,可以查看节点的相关信息,比如进程数,内存磁盘使用情况
  • 策略制定者policymaker ,可以登录控制台,制定策略,但是无法查看节点信息
  • 普通管理员 management 仅能登录控制台
  • 其他, 无法登录控制台,一般指的是提供者和消费者
设置用户权限
./rabbitmqctl set_user_tags ktkt administrator

rabbitMQ的工作模式

rabbitMQ提供了各种消息的通信方式-工作模式

消息通信由两个角色完成:消息生产者(producer)和消息消费者(Consumer)

参考官方文档:RabbitMQ Tutorials — RabbitMQ

简单模式

生产者将消息发到队列,消费者从队列中取消息 ,一条消息对应一个消费者

工作模式

Work模式就是一条消息可以被多个消费者尝试接收,但是最终只能有一个消费者能获取

订阅模式

多个消息队列,每个消息队列有一个消费者监听

通过交换机对生产者传过来的消息进行操作,根据交换机的模式传入到对应的queue里面去

fanout模式:每个队列里面都复制一份

路由模式

生产者将消息发送到了type为direct模式的交换机,消费者的队列在将自己绑定到路由的时候会给自己绑定一个key,只有消费者发送对应key格式的消息时候队列才会收到消息

总概念图

案例演示(maven)

简单模式
  1. 第一步:先把简单模式的项目创建好,由于创建maven项目过于简单 我就不截图了

    1. 创建一个普通的maven项目为mq_demo1
    2. 创建一个子模块消息提供者poducer
    3. 在创建一个子模块消息消费者consumer
    4. 都要导入依赖
    
            <dependency>
                <groupId>com.rabbitmq</groupId>
                <artifactId>amqp-client</artifactId>
                <version>5.7.3</version>
            </dependency>
    
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
                <version>3.3.2</version>
            </dependency>
        
    
    1. 在资源目录创建log4j.properties
    log4j.rootLogger=DEBUG,A1 log4j.logger.com.kt = DEBUG 
    log4j.logger.org.mybatis = DEBUG
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
    
  2. 第二步:分别在consumer和poducer创建连接工具类

/**
 * Created by IntelliJ IDEA.
 * User: kt
 * Date: 2020/12/4 10:36
 *
 * @author kt
 */
public class MQUtil {
    public static Connection getConnection() {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //在工厂对象中设置mq的连接信息  ip port 虚拟主机 username password
        factory.setHost("填本机或者服务器ip");
        factory.setPort(5672);
        //factory.setVirtualHost("host1");
        factory.setUsername("ktkt");
        factory.setPassword("ktkt");
        //通过工厂获得与mq的连接对象
        Connection connection = null;
        try {
            connection = factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return connection;
    }
}
  1. 在consumer创建业务类,因为是消息消费者(接收者),所以我们要写接收消息的业务方法
/**
 * Created by IntelliJ IDEA.
 * User: kt
 * Date: 2020/12/6 0:21
 * @author kt
 */
public class ReceiveMsg {
    public static void main(String[] args) {
        Connection connection = MQUtil.getConnection();
        try {
            Channel channel = connection.createChannel();

            //通过consumer来处理数据
            Consumer consumer = new DefaultConsumer(channel){
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //body就是从队列中获取的数据
                    String msg = new String(body);
                    System.out.println("接收:"+msg);
                }
            };
            //参数1:接收哪个队列的数据
            //参数2:消息确认 是否应答,收到消息是否回复
            //参数3:
            channel.basicConsume("queue1",true,consumer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 在poducer创建业务类,因为是消息提供者,所以我们要写发送消息的业务方法
/**
 * Created by IntelliJ IDEA.
 * User: kt
 * Date: 2020/12/5 23:54
 * @author kt
 */
public class SendMsg {
    public static void main(String[] args) throws TimeoutException {

        String msg = "Hello,rabbit";
        //获取连接
        Connection connection = MQUtil.getConnection();
        try {
            Channel channel = connection.createChannel();

            //定义队列---用Java代码创建队列
            //param1:定义队列的名称
            //param2:队列中的数据是否提供持久化(选择了就停服务不会丢失数据)
            //param3:是否排外  当连接关闭时是否删除当前队列 ture:删除  是否为连接私有
            //param4:自动删除 当此队列的连接数为0 此队列销毁 无论是否有数据
            //param5:设置当前队列的参数
            //channel.queueDeclare("queue7",false,false,false,null);

            //发送消息
            // 参数1:交换机名称  如果直接发送到队列 则交换机名称为 ""
            // 参数2: 队列名称
            // 参数3: 设置当前这条消息的属性 比如:有效时间
            // 参数4: 消息的内容
            channel.basicPublish("","queue1",null,msg.getBytes());
            System.out.println("发送"+msg);
            channel.close();
            connection.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  1. 可以开始测试了,同时启动,该发送的发送!!
工作模式

工作模式无非就是在简单模式上多加了一个消费者,就是多个消费者消费同一条消息

  1. 所以,再创建一个消费者consumer1,依赖和结构和consumer一样

  2.  在consumer创建业务类,因为是消息消费者(接收者),所以我们要写接收消息的业务方法
    /**
     * Created by IntelliJ IDEA.
     * User: kt
     * Date: 2020/12/6 0:21
     * @author kt
     */
    public class ReceiveMsg1 {
        public static void main(String[] args) {
            Connection connection = MQUtil.getConnection();
            try {
                Channel channel = connection.createChannel();
    
                //通过consumer来处理数据
                Consumer consumer = new DefaultConsumer(channel){
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                        //body就是从队列中获取的数据
                        String msg = new String(body);
                        System.out.println("consumer1接收:"+msg);
                    }
                };
                //参数1:接收哪个队列的数据
                //参数2:消息确认 是否应答,收到消息是否回复
                //参数3:
                channel.basicConsume("queue1",true,consumer);
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
  3. 测试,会发现是轮询的一个但错,但是也不能说是全轮询,如果只有两个消费者的时候,当一个消费者消息还没消费完时,另一方消费完接着接收

订阅模式

多个队列多个消费者监听,通过交换机的fanout模式来对每个队列都进行复制消息 fanout:是对绑定的每个队列都发送所有的消息

同样使用工作模式的案例

在发送的时候指定交换机,而不是发送到队列里面去,因为这个时候我们是订阅模式,自动复制进队列

//发送方只需要更改为
channel.basicPublish("ex1","",null,msg.getBytes());

//接收方可以从交换机绑定的队列中获取消息,比如我交换机ex1又queue3和queue4,那么我consumer和consumer1分别从queue3和queue4中接收消息其实是一样的消息
//consumer 
channel.basicConsume("queue3",true,consumer);
//consumer1
channel.basicConsume("queue4",true,consumer);
路由模式

​ 发送方通过指定的key然后通过direct交换机发送到指定的队列

这一个其实也是在基于工作模式的,在发送和接收有些许地方要更改,多余的代码我不写第二遍啦,需要的可以私信我要源码或者自己手动cv哦

我们在交换机和队列的绑定也有要注意的

当key为a的时候就发送到key为的队列中,我这里做个简单的测试,

//prodicer的sendMsg的方法
 			   /**
                * 如果第一个参数写了交换机  参数二就是key
                * 这里我简单的发送一下,后面我们实际的开发中肯定不是这样的了
                * */
                if (msg.startsWith("a")){
                    channel.basicPublish("ex2","a",null,msg.getBytes());
                }else if(msg.startsWith("b")){
                    channel.basicPublish("ex2","b",null,msg.getBytes());
                }
//consumer的接收方法  因为我该交换机绑定了queue5和queue6
	channel.basicConsume("queue5",true,consumer);	
//consumer1的接收方法
	channel.basicConsume("queue6",true,consumer);

测试一下:

基本没啥问题了,这就是路由模式,执行流程没说太多,因为在前面对于工作模式的介绍以及画图,都应该能看的很明白,多看多想okok

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值