zoukankan      html  css  js  c++  java
  • RabbitMQ(一) 初识RabbitMQ

    RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的。

    哪些大厂在用RabbitMQ,为什么

    • 滴滴、美团、头条
    • 开源、性能优秀、稳定性保障
    • 提供可靠性消息投递模式(confim)、返回模式(return)
    • 与SpringAMQP完美结合、API丰富
    • 集群模式丰富,表达式配置,HA模式,镜像队列模型
    • 保证数据不丢失的前提做到高可靠性、可用性

    什么是AMQP高级消息队列协议

    AMQP:Advance Message Queuing Protocol

    AMQP翻译:高级消息队列协议

    AMQP定义:是具有现代特征的二进制协议。是一个提供统一消息服务的应用层标准高级队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。

    AMQP协议模型

    在这里插入图片描述

    AMQP核心概念

    Server:又称Broker,接受客户端的连接,实现AMQP实体服务

    Connection:连接,应用程序与Broker的网络连接

    Channel:网络信道,几乎所有的操作都在channel中进行,Channel是进行消息读写的通道。客户端可建立多个Channel,每个Channel代表一个会话任务。

    Message:消息,服务器和应用程序之间传送的数据,由Properties和Body组成。Properties可以对消息进行修饰,比如消息的优先级,延迟等高级特性;Body则就是消息体内容。

    Virtual host:虚拟地址,用于进行逻辑隔离,最上层的消息路由。一个Virtual Host里面可以有若干个Exchange和Queue,统一Virtual Host里面不能有相同名称的Exchange和Queue

    Exchange:交换机,接收消息,根据路由键转发消息到绑定的队列

    Binding:Exchange和Queue之间的虚拟连接,binding中可以包含routing key

    Routing key:一个路由规则,虚拟机可用它来确定如何路由一个特定消息

    Queue:也成为Mesage Queue,消息队列,保存并将它们转发给消费者

    RabbitMQ的整体架构

    在这里插入图片描述

    RabbitMQ消息是如何流转的

    在这里插入图片描述

    RabbitMQ的安装与使用

    • 官网地址:http://www.rabbitmq.com/

    • 提前准备:安装Linux必要依赖

    • 下载RabbitMQ必须安装包(简单RPM安装)

    • 配置文件修改

    • 服务启动:rabbitmq-server start &

    • 服务的停止:rabbitmqctl stop_app

    • 管理插件:rabbitmq-plugins enable rabbitmq_management

    • 访问:http://locahost:15672

    准备:
    yum install 
    build-essential openssl openssl-devel unixODBC unixODBC-devel 
    make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz
    
    下载:
    wget www.rabbitmq.com/releases/erlang/erlang-18.3-1.el7.centos.x86_64.rpm
    wget http://repo.iotti.biz/CentOS/7/x86_64/socat-1.7.3.2-5.el7.lux.x86_64.rpm
    wget www.rabbitmq.com/releases/rabbitmq-server/v3.6.5/rabbitmq-server-3.6.5-1.noarch.rpm
    
    配置文件:
    vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app
    比如修改密码、配置等等,例如:loopback_users 中的 <<"guest">>,只保留guest
    服务启动和停止:
    启动 rabbitmq-server start &  (&表示后台启动)
    停止 rabbitmqctl app_stop
    
    默认插件列表: rabbitmq-plugins  list
    管理插件:rabbitmq-plugins enable rabbitmq_management
    访问地址:http://localost:15672/
    

    rabbitmqctl --help 查询具体参数

    快速入门-消息生产与消费

    • ConnectionFactory:获取连接工厂
    • Connection:一个连接
    • Channel:数据通信信道,可发送和接收消息
    • Queue:具体的消息存储队列
    • Producer&Consumer生产者和消费者

    生产者代码

    需要引入

    <dependency>
    	<groupId>com.rabbitmq</groupId>
    	<artifactId>amqp-client</artifactId>
    	<version>3.6.5</version>
    </dependency>
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    /**
     * 生产者
     *
     * @author niugang
     */
    public class Producer {
    
    
        public static void main(String[] args) throws Exception {
            //1 创建一个ConnectionFactory, 并进行配置
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("localhost");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
    
            //2 通过连接工厂创建连接
            Connection connection = connectionFactory.newConnection();
    
            //3 通过connection创建一个Channel
            Channel channel = connection.createChannel();
    
            //4 通过Channel发送数据
            for (int i = 0; i < 15; i++) {
                String msg = "Hello RabbitMQ!";
                //1 exchange   2 routingKey
                //exchange 为 空字符串 为走默认的exchange  (AMQP default)  根据路由key 和消息队列  名称 是否匹完全匹配进行投递、
                // 从控制台 Exchanges->(AMQP default)
                // The default exchange is implicitly bound to every queue, with a routing key equal to the queue name. It is not possible to explicitly bind to, or unbind from the default exchange. It also cannot be deleted.
                //默认交换被隐式地绑定到每个队列,路由键等于队列名称。无法显式绑定到默认交换器或从默认交换器取消绑定。它也不能被删除。
                channel.basicPublish("", "test001", null, msg.getBytes());
            }
    
            //5 记得要关闭相关的连接
            channel.close();
            connection.close();
        }
    }
    

    消费者代码

    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 lombok.extern.slf4j.Slf4j;
    
    /**
     * 消费者
     *
     * @author niugang
     */
    @Slf4j
    public class Consumer {
    
        public static void main(String[] args) throws Exception {
    
            //1 创建一个ConnectionFactory, 并进行配置
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("localhost");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
    
            //2 通过连接工厂创建连接
            Connection connection = connectionFactory.newConnection();
    
            //3 通过connection创建一个Channel
            Channel channel = connection.createChannel();
    
            //4 声明(创建)一个队列
            String queueName = "test001";
            //exclusive 如果我们声明一个排他队列(仅限于此连接),则为true。
            channel.queueDeclare(queueName, true, false, false, null);
    
            //5 创建消费者
            QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
    
            //6 设置Channel
            channel.basicConsume(queueName, true, queueingConsumer);
    
            while (true) {
                //7 获取下一个消息,方法阻塞
                Delivery delivery = queueingConsumer.nextDelivery();
                String msg = new String(delivery.getBody());
                log.info("消费端: {}", msg);
                //封装一些发送参数
              //  Envelope envelope = delivery.getEnvelope();
            }
    
        }
    }
    
    

    在这里插入图片描述

    Exchange交换机

    Exchange接收消息,并根据路由键转发消息所绑定的队列
    在这里插入图片描述

    交换机属性

    Name:交换机名称

    Type:交换器类型direct、topic、fanout、headers

    Durability:是否需要持久化,true为持久化

    Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange

    Internal:当前Exchange是否用于RabbitMQ内部使用,默认为false

    Arguments:扩展参数,用于扩展AMQP协议自制定化使用

    Direct Exchange

    所有发送到Direct Exchange的消息被转发到RouteKey中指定的Queue

    direct 直连模式 routing_key必须完全匹配

    注意:Direct模式可以使用RabbitMQ自带的Exchange:fefault Exchange,所以不需要将Exchange进行任何绑定(binding)操作,消息传递,RouteKey必须完全匹配才会被队列接收,否则该消息会被抛弃

    在这里插入图片描述
    生产者代码

    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    /**
     * @author niugang
     */
    public class Producer4DirectExchange {
    
    	
    	public static void main(String[] args) throws Exception {
    		
    		//1 创建ConnectionFactory
    		ConnectionFactory connectionFactory = new ConnectionFactory();
    		connectionFactory.setHost("localhost");
    		connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
    		//2 创建Connection
    		Connection connection = connectionFactory.newConnection();
    		//3 创建Channel
    		Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_direct_exchange";
    		String routingKey = "test.direct";
    		//5 发送
    		
    		String msg = "Hello World RabbitMQ 4  Direct Exchange Message 111 ... ";
    		channel.basicPublish(exchangeName, routingKey , null , msg.getBytes()); 		
    		
    	}
    	
    }
    

    消费者代码

    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;
    
    /**
     * @author niugang
     */
    public class Consumer4DirectExchange {
    
    	public static void main(String[] args) throws Exception {
    		
    		
            ConnectionFactory connectionFactory = new ConnectionFactory() ;  
            
            connectionFactory.setHost("localhost");
            connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
            connectionFactory.setAutomaticRecoveryEnabled(true);
            connectionFactory.setNetworkRecoveryInterval(3000);
            Connection connection = connectionFactory.newConnection();
            
            Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_direct_exchange";
    		String exchangeType = "direct";
    		String queueName = "test_direct_queue";
    		String routingKey = "test.direct";
    		
    		//表示声明了一个交换机
    		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    		//表示声明了一个队列
    		channel.queueDeclare(queueName, false, false, false, null);
    		//建立一个绑定关系:
    		channel.queueBind(queueName, exchangeName, routingKey);
    		
            //durable 是否持久化消息
            QueueingConsumer consumer = new QueueingConsumer(channel);
            //参数:队列名称、是否自动ACK、Consumer
            channel.basicConsume(queueName, true, consumer);  
            //循环获取消息  
            while(true){  
                //获取消息,如果没有消息,这一步将会一直阻塞  
                Delivery delivery = consumer.nextDelivery();  
                String msg = new String(delivery.getBody());    
                System.out.println("收到消息:" + msg);  
            } 
    	}
    }
    

    Topic Exchange

    • 所有发送到Topic Exchange 的消息被转发到所有关心RouteKey指定Topic的Queue上
    • Exchange将RouteKey和某Topic进行模糊匹配,此时队列需要绑定到一个Topic

    注意:可以使用通配符进行模糊匹配

    符号"#"匹配一个或多个词

    符号"*"匹配不多不少一个词

    例如“log.#”能够匹配到“log.info.oa”

    例如“log.*“能够匹配到“log.error”

    在这里插入图片描述
    生产者代码

    /**
     * @author niugang
     */
    public class Producer4TopicExchange {
    
    	
    	public static void main(String[] args) throws Exception {
    		
    		//1 创建ConnectionFactory
    		ConnectionFactory connectionFactory = new ConnectionFactory();
    		connectionFactory.setHost("localghost");
    		connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
    		//2 创建Connection
    		Connection connection = connectionFactory.newConnection();
    		//3 创建Channel
    		Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_topic_exchange";
    		String routingKey1 = "user.save";
    		String routingKey2 = "user.update";
    		String routingKey3 = "user.delete.abc";
    		//5 发送
    		
    		String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
    		channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes()); 
    		channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes()); 	
    		channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes()); 
    		channel.close();  
            connection.close();  
    	}
    	
    }
    
    

    消费者代码

    /**
     * @author niugang 
     */
    public class Consumer4TopicExchange {
    
    	public static void main(String[] args) throws Exception {
    		
    		
            ConnectionFactory connectionFactory = new ConnectionFactory() ;  
            
            connectionFactory.setHost("localhost");
            connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
            connectionFactory.setAutomaticRecoveryEnabled(true);
            connectionFactory.setNetworkRecoveryInterval(3000);
            Connection connection = connectionFactory.newConnection();
            
            Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_topic_exchange";
    		String exchangeType = "topic";
    		String queueName = "test_topic_queue";
    		//String routingKey = "user.*";
    		String routingKey = "user.#";
    		// 1 声明交换机 
    		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    		// 2 声明队列
    		channel.queueDeclare(queueName, false, false, false, null);
    		// 3 建立交换机和队列的绑定关系:
    		channel.queueBind(queueName, exchangeName, routingKey);
    		
            //durable 是否持久化消息
            QueueingConsumer consumer = new QueueingConsumer(channel);
            //参数:队列名称、是否自动ACK、Consumer
            channel.basicConsume(queueName, true, consumer);  
            //循环获取消息  
            while(true){  
                //获取消息,如果没有消息,这一步将会一直阻塞  
                Delivery delivery = consumer.nextDelivery();  
                String msg = new String(delivery.getBody());    
                System.out.println("收到消息:" + msg);  
            } 
    	}
    }
    

    Fanout Exchange

    • 不处理路由键。只需要简单的将队列绑定到交换机上

    • 发送到交换器的消息都会被转发到与交换机绑定的所有队列上

    • Fanout交换机转发消息最快

      fanout类型的exchange不会和routing_key产生任何关系(不会走路由键)
      在这里插入图片描述
      生产者代码

    /**
     * @author niugang 
     */
    public class Producer4FanoutExchange {
    
    	
    	public static void main(String[] args) throws Exception {
    		
    		//1 创建ConnectionFactory
    		ConnectionFactory connectionFactory = new ConnectionFactory();
    		connectionFactory.setHost("localhost");
    		connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
    		//2 创建Connection
    		Connection connection = connectionFactory.newConnection();
    		//3 创建Channel
    		Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_fanout_exchange";
    		//5 发送
    		for(int i = 0; i < 10; i ++) {
    			String msg = "Hello World RabbitMQ 4 FANOUT Exchange Message ...";
    			channel.basicPublish(exchangeName, "", null , msg.getBytes()); 			
    		}
    		channel.close();  
            connection.close();  
    	}
    	
    }
    

    消费者代码

    /**
     * @author niugang
     */
    public class Consumer4FanoutExchange {
    
    	public static void main(String[] args) throws Exception {
    		
            ConnectionFactory connectionFactory = new ConnectionFactory() ;  
            
            connectionFactory.setHost("localhost");
            connectionFactory.setPort(5672);
    		connectionFactory.setVirtualHost("/");
    		
            connectionFactory.setAutomaticRecoveryEnabled(true);
            connectionFactory.setNetworkRecoveryInterval(3000);
            Connection connection = connectionFactory.newConnection();
            
            Channel channel = connection.createChannel();  
    		//4 声明
    		String exchangeName = "test_fanout_exchange";
    		String exchangeType = "fanout";
    		String queueName = "test_fanout_queue";
    		String routingKey = "";	//不设置路由键
    		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    		channel.queueDeclare(queueName, false, false, false, null);
    		channel.queueBind(queueName, exchangeName, routingKey);
    		
            //durable 是否持久化消息
            QueueingConsumer consumer = new QueueingConsumer(channel);
            //参数:队列名称、是否自动ACK、Consumer
            channel.basicConsume(queueName, true, consumer); 
            //循环获取消息  
            while(true){  
                //获取消息,如果没有消息,这一步将会一直阻塞  
                Delivery delivery = consumer.nextDelivery();  
                String msg = new String(delivery.getBody());    
                System.out.println("收到消息:" + msg);  
            } 
    	}
    }
    

    Binding-绑定

    • Exchange和Exchange、Queue之间的连接关系
    • Binding中可以包含RoutingKey或者参数

    Queue-消息队列

    • 消息队列,实际存储消息数据
    • Durability:是否持久化,Durable : 是,Transient :否
    • Auto delete:如选yes,代码当最后一个监听被移除之后,该Queue会自动被删除

    Message-消息

    • 服务器和应用之间传送的数据

    • 本质上就是一段数据,由Properties和Payload(Body)组成

    • 常用属性:delivery mode 、headers

      delivery mode 为2 标识持久化消息

    其他属性

    • content_type、content_encoding、priority
    • correlation_id、reply_to、expiration、message_id
    • timestamp、type、user_id、app_id、cluster_id

    Virtual host(虚拟主机)

    • 虚拟地址,用于进行逻辑隔离,最上层的消息路由
    • 一个Vitual Host里面可以有若干个Exchange和Queue
    • 同一个Virtual Host里面不能有相同名称的Exchange和Queue
      在这里插入图片描述
  • 相关阅读:
    创业团队的狼性管理:慈不掌兵 义不聚财
    爱德华·斯诺登
    塞班岛
    大兴雷克萨斯深度剖析2013款LS460L_深圳大兴雷克萨斯_太平洋汽车网
    金字塔底端_百度百科
    左立_百度百科
    志大才疏_百度百科
    JS~json日期格式化
    MVVM架构~knockoutjs系列之从Knockout.Validation.js源码中学习它的用法
    JS~重写alter与confirm,让它们变成fancybox风格
  • 原文地址:https://www.cnblogs.com/niugang0920/p/13043671.html
Copyright © 2011-2022 走看看