Rabbitmq消息队列——NWU_LK

Rabbitmq消息队列

消息队列简介

消息队列(Message Queue)别名消息中间件,是经典的生产者消费者模型。生产者不断向消息队列发送消息,消费者从消息队列消费消息,因此消息的生产和消费都是异步的。
目前消息中间件主要的几种有:RabbitMQ、ActiveMQ、Kafka、RocketMQ等。

  • ActiveMQ
    Apache出品的产品,行业中最流行、能力最强的消息中间件,提供丰富的API,在中小型企业很受欢迎。缺点就是吞吐量小,性能相对较弱。
  • Kafka
    高吞吐量,设计的初衷用在大数据中,但是对数据的一致性要求不强,偶尔会丢失数据。
  • RocketMQ
    纯java开发的消息中间件,具有高吞吐量、高可用性,对分布式事务有一定的支持(开源的版本不支持分布式事务)。
  • RabbitMQ
    RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。Spring默认支持的就是RabbitMQ。AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
amqp协议介绍

在这里插入图片描述
qmqp也符合生产者消费者模型,它会将消息以通道的形式发送。生产者与Rabbitmq的server建立连接,建立完连接后,生产者将对应于一个虚拟主机(Virtual host),可以将该虚拟主机理解为数据库中库的概念,每个应用对应一个虚拟主机。访问虚拟主机需要权限,因此需要对用户和虚拟主机做一个绑定。当生产者进入到虚拟主机之后,就会将通道中的消息放到交换机(Exchange)中,也有的消息模型会直接将消息放入队列当中。消费者也需要连接到server,连接成功后,则一直会监听着某个虚拟主机的队列,一旦有消息则会进行消费。

Ubuntu 安装Rabbitmq
  1. 由于rabbitMq需要erlang语言的支持,在安装rabbitMq之前需要安装erlang,执行命令:sudo apt-get install erlang-nox
  2. 添加公钥:wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
  3. 更新软件包:sudo apt-get update
  4. 安装 RabbitMQ:sudo apt-get install rabbitmq-server
  5. 查看 RabbitMq状态(默认已启动):systemctl status rabbitmq-server,active (running) 说明处于运行状态
  6. 启用 web端可视化操作界面,配置Management Plugin插件:sudo rabbitmq-plugins enable rabbitmq_management
  7. 重启让配置生效:sudo service rabbitmq-server restart
  8. 创建用户:sudo rabbitmqctl add_user admin 123456,RabbitMQ默认会创建guest用户,但是 3.3 及后续版本,guest 只能在服务本机登录。建议创建其他新用户,授权,用来做其他操作。
  9. 查看所有用户:sudo rabbitmqctl list_users
  10. 给admin管理员角色:sudo rabbitmqctl set_user_tags admin administrator
  11. 赋予virtual host中所有资源的配置、写、读权限:sudo rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'
  12. 浏览器访问管理页面:输入http://虚拟机IP地址:15672/
  13. 停止服务:sudo service rabbitmq-server stop
其他Rabbitmq命令
命令说明
sudo rabbitmqctl查看所有命令和帮助文档
sudo rabbitmqctl stop停止服务
sudo rabbitmqctl status查看服务状态
sudo rabbitmqctl list_users查看当前所有用户
sudo rabbitmqctl delete_user guest删掉默认用户,除guest
sudo rabbitmqctl add_user username password添加新用户
sudo rabbitmqctl set_user_tags username administrator设置用户身份
sudo rabbitmqctl set_permissions -p / username “.” “.” “.*”赋予用户默认vhost的全部操作权限
sudo rabbitmqctl list_user_permissions username查看用户的权限
Rabbitmq的角色
  1. 超级管理员(administrator)
    可登陆管理控制台(启用management plugin的情况下),可查看所有的信息,并且可以对用户,策略(policy)进行操作。

  2. 监控者(monitoring)
    可登陆管理控制台(启用management plugin的情况下),同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)

  3. 策略制定者(policymaker)
    可登陆管理控制台(启用management plugin的情况下), 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。与administrator的对比,administrator能看到这些内容

  4. 普通管理者(management)
    仅可登陆管理控制台(启用management plugin的情况下),无法看到节点信息,也无法对策略进行管理。

  5. 其他
    无法登陆管理控制台,通常就是普通的生产者和消费者。

环境准备
  1. 在管理页面处创建测试用的虚拟主机/test(虚拟主机名必须以斜杠开头),测试用的用户名为user,密码为123456,并将虚拟主机绑定到用户上。
    在这里插入图片描述

在这里插入图片描述
2. 创建maven项目,引入amqp的依赖

<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>5.8.0</version>
</dependency>
  1. 创建用于连接amqp服务器的工具类
public class RabbitMQUtil {
    //创建连接rabbitmq的工厂对象
    public static ConnectionFactory factory;
    static {
        factory=new ConnectionFactory();
        // 设置主机名、端口号、连接的虚拟主机
        factory.setHost("192.168.192.130");
        factory.setPort(5672);
        factory.setVirtualHost("/test");
        //设置访问虚拟主机的名称和密码
        factory.setUsername("user");
        factory.setPassword("123456");
    }
    public static Connection getCN(){
        try {
            return factory.newConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
直连模式

在这里插入图片描述

无交换机,一对一模型,一个服务提供者,一个消息队列,一个消费者.

  1. 创建消息生产者
public class Provider {
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection= RabbitMQUtil.getCN();
        //创建发送消息的通道
        Channel channel = connection.createChannel();
        /*
          通道绑定对应的消息队列
          参数一:队列名称为hello
          参数二:定义队列是否持久化,即关闭后要将队列存在磁盘,false的话重启rabbitmq会删除队列,该属性不能配置队列中的消息是否持久化
          参数三:是否一个连接独占队列
          参数四:是否消费完成后删除队列,false不自动删除,也即把消费者服务断开后删除队列
          参数五:额外附加参数
         */
        channel.queueDeclare("hello",false,false,false,null);
        //发布消息
        //参数一:交换机
        //参数二:队列名称
        //参数三:发布消息时的一些属性,比如消息是否持久化
        //参数四:发送的内容
        channel.basicPublish("","hello",null,"hello,rabbitmq2".getBytes());
        channel.close();
        connection.close();
    }
}
  1. 创建消息消费者
    Consumer端不建议关闭channel和connection,因为它要一直接收消息。
public class Consumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection= RabbitMQUtil.getCN();
        //创建通道
        Channel channel = connection.createChannel();
        //通道绑定队列
        channel.queueDeclare("hello",false,false,false,null);
        //消费消息
        //参数一:消息队列名称
        //参数二:开启消息的自动确认机制
        //参数三:消费时的回调接口
        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));
            }
        });
    }
}
工作队列模式

在这里插入图片描述

无交换机,一对多模型,一个服务提供者,一个消息队列,多个消费者。默认情况消息被一条一条平均分配给每一个消费者消费。

消息确认机制:若设置为true,则所有消息在未消费之前就已经被分配给了每个消费者。如果期间消费者宕机的话那么被分配给它的消息就会丢失。因此一般选择关闭消息自动确认,实现能者多劳的功能。channel.basicQos(1)指该消费者在接收到队列里的消息但没有返回确认结果之前,它不会将新的消息分发给它。

  1. 创建生产者
public class Provider {
    @Test
    public void test() throws IOException, TimeoutException {
        Connection connection= RabbitMQUtil.getCN();
        Channel channel = connection.createChannel();
		//队列名称为work,可以随机起
        channel.queueDeclare("work",false,false,false,null);
        for (int i=1;i<10;i++){
            channel.basicPublish("","work",null,(i+":hello,rabbitmq").getBytes());
        }

    }
}
  1. 创建消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        channel.basicQos(2);//每次只能消费一个消息
        channel.queueDeclare("work",false,false,false,null);
        //关闭自动确认消息
        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);
            }
        });
    }
}
public class Consumer2 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        channel.basicQos(2);
        channel.queueDeclare("work",false,false,false,null);
        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);
            }
        });
    }
}
广播模型

fanout模型(广播模型):有交换机,一个生产者,多个消费者。生产者将信息发送给交换机,由交换机发送消息给临时队列,每个消费者对应一个临时队列。交换机会将消息放松给绑定过的所有队列,实现一条消息被多个消费者消费。

在这里插入图片描述

  1. 创建生产者
public class Provider {
    public static void main(String[] args) throws IOException, TimeoutException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        //声明fanout类型的交换机,名称为myExchange
        channel.exchangeDeclare("myExchange","fanout");
        //发送消息
        channel.basicPublish("myExchange","",null,"=====fanout=====".getBytes());
        channel.close();
        cn.close();
    }
}
  1. 创建消费者
    创建三个消费者,代码都为以下内容
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        //创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机,此处的路由key为空
        channel.queueBind(queueName,"myExchange","");
        channel.basicConsume(queueName,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));
            }
        });
    }
}
路由模型——Direct模型

跟广播模型类似,其主要是添加了一些路由规则。在广播模型中生产者的消息要发送给每一个消费者。但是在路由——direct模型中,可以实现不同的消息发送给不同的消费者。在Direct模型下,生产者在发送消息时,必须要对消息指定Routing key,消费者的临时队列与交换机的绑定也不再是任意的,需要指定Routing key。这样就可以实现消息路由。比如以下例子实现Consumer1接收error消息,Conusmer2可以接收error、info、debug消息。
在这里插入图片描述

  1. 创建生产者
public class Provider {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        //声明fanout类型的交换机,名称为myExchange
        channel.exchangeDeclare("exchange_direct","direct");
        //设置routing key
        String routingkey="error";
        //发送消息
        channel.basicPublish("exchange_direct",routingkey,null,("=====direct=====routingkey:"+routingkey+"=====").getBytes());
    }
}
  1. 创建消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        //创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机
        channel.queueBind(queueName,"exchange_direct","error");

        channel.basicConsume(queueName,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));
            }
        });
    }
}
public class Consumer2 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        //创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        //队列绑定交换机
        channel.queueBind(queueName,"exchange_direct","error");
        channel.queueBind(queueName,"exchange_direct","info");
        channel.queueBind(queueName,"exchange_direct","debug");
        channel.basicConsume(queueName,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));
            }
        });
    }
}
路由模型——Topic模型

Direct模型的拓展,支持统配的路由。临时队列和交换机绑定时可以将routing key写成通配符的情况。.代表单词间的分隔符,*代表一个单词,#代表一个或多个单词。比如消费者routingkey可以是*.hello.#,表示hello前可以有一个单词,后面可以有多个单词。
在这里插入图片描述

  1. 生产者的创建
public class Provider {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        channel.exchangeDeclare("topicExchange","topic");

        String routingkey="user";
        channel.basicPublish("topicExchange",routingkey,null,"这是topic模型".getBytes());
    }
}
  1. 消费者的创建
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection cn = RabbitMQUtil.getCN();
        Channel channel = cn.createChannel();
        channel.exchangeDeclare("topicExchange","topic");
        String queuename=channel.queueDeclare().getQueue();
        channel.queueBind(queuename,"topicExchange","user.#");

        channel.basicConsume(queuename,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));
            }
        });
    }
}
Springboot集成Rabbitmq

详情见——Springboot集成Rabbitmq

Rabbitmq的常见作用
  • 异步处理:用户注册需要发送邮件和短信,如果仅仅起到的是通知的作用,那么加入消息队列后可以加快业务速度,不用等待邮件和短信发送完成后业务才完成
  • 系统解耦:比如可以在订单系统和库存系统加入消息队列,当库存系统崩溃的时候,订单请求还在
  • 流量削峰:在前端加入消息队列,比如秒杀活动时,如果消息队列的请求数达到一定个数后就会返回错误页面
Rabbitmq集群
  • 普通集群(副本集群):主节点用来和生产者打交道,从节点负责同步主节点的数据,但只能同步exchange的数据,不能同步queue的数据。虽然从节点不包含队列数据,但其包含队列元数据,因此在每个节点的web管理界面都能看到队列的信息。消费者消费消息时,可以是任何一个节点,当如果要在从节点上消费消息,必须保证主节点是活着的,其原理是从节点会将主节点上的消息拿回来后再发给消费者。
    在这里插入图片描述
  1. 准备n台虚拟机,配置各个虚拟机的IP和主机名
  2. 同步集群中各个节点的/var/lib/rabbitmq/.erlang.cookie文件
    用scp命令将任意一个节点中的该文件拷贝到其他机器中即可。
  3. 后台方式启动所有节点:rabbitmq-server -detached
    后台启动的方式看不到web页面,因此只能再命令行测试
  4. 用命令rabbitmqctl cluster_status查看集群状态,发现每台服务器仍然是单个节点,未成集群。
  5. 关闭n-1个节点,剩余一个节点(该节点为主节点):rabbitmqctl stop_app
  6. 将关闭掉的n-1个节点加入到集群中:rabbitmqctl join_cluster rabbit@剩余一个节点的主机名
  7. 启动n-1个节点:rabiitmqctl start_app
  • 镜像集群:一主多从,每个节点都会同步主节点的所有信息,所以可以相互替换实现高可用。当主节点宕机后,某个从节点会变为主节点,从节点仍然会提供服务。主节点再次启动后会变成从节点。
    在这里插入图片描述
    在普通集群的基础上设置策略即可实现镜像集群。
    查看当前策略:rabbitmqctl list_policies
    设置策略的语法为:rabbitmqctl set_policy [可选参数] <name> <pattern> <definition>
参数说明
name策略名称
patternqueue的匹配模式,正则表达式
definition包括ha-mode、ha-params、ha-sycn-mode

ha-mode包含以下几种:

  • all:表示在集群所有节点进行镜像
  • exactly:表示在指定节点个数是进行镜像,数量由ha-params指定
  • nodes:表示在指定的节点上进行镜像,节点名称在ha-params指定

ha-params用来配置ha-mode中用到的参数
ha-sycn-mode:消息的同步方式,自动还是手动,默认自动

添加策略:rabbitmqctl set_policy high-available '^hello' '{"ha-mode":"all","ha-sycn-mode":"automatic"}',该策略的名称为high-available,并且匹配所有hello开头的队列,在所有节点上镜像,并且自动同步。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值