RabbitMq的安装,原理及项目中的使用

一.Windows下安装RabbitMq

1.安装erlang语言开发包(rabbitmq为erlang语言开发)

a.Erlang 下载安装地址 http://www.erlang.org/downloads 下载完成后运行安装

b.配置环境变量
1)添加系统环境变量ERLANG_HOME,值为安装目录.
2) 修改系统环境变量Path,在PATH变量中添加“%ERL_HOME%\bin”
3) 重启电脑后,在控制台输入 erl,如果出现类似“Eshell V6.1 (abort with ^G)”字样,说明安装成功。

2.安装RabbitMq

a.RabbitMq下载安装地址 http://www.rabbitmq.com/install-windows.html 下载完成后运行安装

b.运行服务
rabbitMq默认自启动
可以修改rabbitmq的配置文件,也可以用默认配置运行。在开始菜单栏里可以看到运行指令reinstall/remove/start/stop
在这里插入图片描述
或者直接打开RabbitMQ Command Prompt命令框。
在这里插入图片描述
c.Rabbit MQ插件
激活Rabbit MQ’s Management Plugin 使用Rabbit MQ 管理插件,可以更好的可视化方式查看Rabbit MQ 服务器实例的状态,你可以在命令行中使用下面的命令激活。
输入:rabbitmq-plugins.bat enable rabbitmq_management
在这里插入图片描述
创建管理用户

输入:rabbitmqctl.bat add_user gujie gujie1991 (用户名 密码) (这一步及接下来的两步可以不设置,有默认的账号 guest,密码guest)
在这里插入图片描述
设置管理员

输入:rabbitmqctl.bat set_user_tags gujie administrator在这里插入图片描述
设置权限

输入:rabbitmqctl.bat set_permissions -p / zhangweizhong “." ".” “.*”在这里插入图片描述
其他命令查询用户: rabbitmqctl.bat list_users查询vhosts(虚拟机): rabbitmqctl.bat list_vhosts权限、作用域等基本就设置完了。

启动:rabbitmq-server start 访问localhost:15672出现下列页面表示成功。(此时可对连接、通道、交换机、队列、用户进行管理)
在这里插入图片描述

二.RabbitMq原理

RabbitMQ是消息代理。从本质上说,它接受来自生产者的信息,并将它们传递给消费者。在两者之间,它可以根据你给它的路由,缓冲规则进行传递消息。

示例图

在这里插入图片描述

1.Exchange
1)交换机属性:

name:交换机名称
type:交换机类型 direct,topic,fanout,headers
durability:是否需要持久化,true为持久化
auto delete:当最后一个绑定到exchange上的队列被删除后,exchange没有绑定的队列了,自动删除该exchange
internal:当前exchange是否用于rabbitMQ内部使用,默认为false
arguments:扩展参数,用于扩展AMQP协议自制定化使用

2)direct exchange类型:

direct exchange:所有发送到direct exchange的消息被转发到routing key中指定的queue

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

流转示意图如下
在这里插入图片描述

3)topic exchange类型:

topic exchange:所有发送到topic exchange的消息被转发到所有关心routingkey中topic的queue上
exchange将routingkey和某topic进行模糊匹配,此时队列需要绑定一个topic。

注意:topic可以使用通配符进行模糊匹配#匹配一个或多个词,注意是词只能匹配一个词例如“log.#”能匹配到“log.info.oa”“log.”只能匹配到“log.erro”这种格式

具体示例图如下图,usa.news能被usa.#,#.news所消费,usa.weather能被usa.#,#.weather所消费…
在这里插入图片描述

4)fanout exchange类型:

fanout exchange:不处理路由键,只需要简单的将队列绑定到交换机上,发送到该交换机的消息都会被转发到于该交换机绑定的所有队列上,fanout交换机由于不需要进行routingkey的对比 直接发送所以绑定的queue,所以转发消息是最快的

示意图如下图所示
在这里插入图片描述
headers类型的不常用,就不介绍了

2.Binding

binding:绑定exchange和exchange/queue之间的连接关心。binding中可以包含routingkey或者参数

3.Queue

queue:消息队列,实际存储消息数据,durability表示是否持久化,durable表示是,transient表示否。auto delete:如选择yes,表示当最后一个监听被移除后,该queue会被自动删除。

4.Message

message:服务器和应用程序之间传送的数据 本质上就是一段数据,由properties和payload(body)组成

常用属性:delivery mode,headersheaders(自定义属性),content_type,content_encoding,priority,correlation_id,reply_to,expiration,message_id,timestamp,type,user_id,app_id,cluster_id

5.Virtual host

virtual host 虚拟主机

虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个virtual host里面可以有若干个exchange和queue,但是里面不能有相同名称的exchange或queue

6.Channel

消息通道,包含了大量的API可用于编程。在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。

消费模式pull与push

pull模式:
客户端(指一个connection,一般情况指一个tcp的连接建立)连接到broker之后,启动一个线程,这个线程的任务就是循环调用方法从broker中拉取相应的消息至本地。如果是同步方法调用获取则将相应的消息存放在本地内存中,当同步方法消费消息时,则从该内存区中直接获取相应的消息进行消费;

push模式:
客户端连接到broker之后,启动一个线程,这个线程的任务就是循环调用方法从broker中拉取相应的消息至本地。如果是异步方法调用,则直接调用监听器方法,间接调用业务消费消息的方法,而不使用本地内存进行消息的缓存;所以这里的异步只是客户端的异步,而非broker的主动推送。通过这种方式既能解决多客户端的连接,也能解决类似服务端的push型的消息推送。在互联网中这种实现才具有普便性,因为这种方式即解决了性能问题又解决了异步消息的需求。

消息队列的使用过程大概如下:

1)客户端连接到消息队列服务器,打开一个channel。
2)客户端声明一个exchange,并设置相关属性。  
3)客户端声明一个queue,并设置相关属性。
4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
5)客户端投递消息到exchange。exchange接收到消息后,就根据消息的
key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。

RabbitMQ支持消息的持久化,也就是数据写在磁盘上,为了数据安全考虑,我想大多数用户都会选择持久化。消息队列持久化包括3个部分:

1)exchange持久化,在声明时指定durable 为 1
2)queue持久化,在声明时指定durable 为 1
3)消息持久化,在投递时指定delivery_mode 为 2(1是非持久化)

三.SpringBoot整合RabbitMQ

1.添加依赖

在pom.xml中添加如下代码:

 <!-- 添加springboot对amqp的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
2.编写配置信息

根据自己的配置在application.properties中加入如下信息:
(这里端口是5672,不是15672…15672是管理端的端口!)

spring.application.name=spirng-boot-rabbitmq-sender
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
3.Direct模式
1).生产者
配置Queue
@Configuration
public class SenderConf {
     @Bean
     public Queue queue() {
          return new Queue("queue");
     }
}
使用AmqpTemplate去发送消息
@Component
public class HelloSender {
    @Autowired
    private AmqpTemplate template;
    
    public void send() {
    template.convertAndSend("queue","hello,rabbit~");
    }
}
编写测试类
@SpringBootTest(classes=App.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class TestRabbitMQ {
    
    @Autowired
    private HelloSender helloSender;

    @Test
    public void testRabbit() {
        helloSender.send();
    }
}
2).消费者
@Component
public class HelloReceive {

    @RabbitListener(queues="queue")    //监听器监听指定的Queue
    public void processC(String str) {
        System.out.println("Receive:"+str);
    }
}
注意:Direct模式相当于一对一模式,一个消息被发送者发送后,会被转发到一个绑定的消息队列中,然后被一个接收者接收
4.Topic转发模式
1).生产者
配置队列Queue及交换机Exchange,按照相应的规则绑定
@Configuration
public class SenderConf {

        @Bean(name="message")
        public Queue queueMessage() {
            return new Queue("topic.message");
        }

        @Bean(name="messages")
        public Queue queueMessages() {
            return new Queue("topic.messages");
        }

        @Bean
        public TopicExchange exchange() {
            return new TopicExchange("exchange");
        }

        @Bean
        Binding bindingExchangeMessage(@Qualifier("message") Queue queueMessage, TopicExchange exchange) {
            return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
        }

        @Bean
        Binding bindingExchangeMessages(@Qualifier("messages") Queue queueMessages, TopicExchange exchange) {
            return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");//*表示一个词,#表示零个或多个词
        }
}
使用AmqpTemplate去发送消息
@Component
public class HelloSender {
    @Autowired
    private AmqpTemplate template;
    
    public void send() {
    template.convertAndSend("exchange","topic.message","hello,rabbit!");
    }
}
注意:方法的第一个参数是交换机名称,第二个参数是发送的key,第三个参数是内容,RabbitMQ将会根据第二个参数去寻找有没有匹配此规则的队列,如果有,则把消息给它,如果有不止一个,则把消息分发给匹配的队列(每个队列都有消息!)
2).消费者
@RabbitListener(queues="topic.message")    //监听器监听指定的Queue
    public void process1(String str) {    
        System.out.println("message:"+str);
    }
    @RabbitListener(queues="topic.messages")    //监听器监听指定的Queue
    public void process2(String str) {
        System.out.println("messages:"+str);
    }
5.Fanout形式
1).生产者
配置队列Queue及交换机Exchange,按照相应的规则绑定
@Configuration
public class SenderConf {

        @Bean(name="Amessage")
        public Queue AMessage() {
            return new Queue("fanout.A");
        }


        @Bean(name="Bmessage")
        public Queue BMessage() {
            return new Queue("fanout.B");
        }

        @Bean(name="Cmessage")
        public Queue CMessage() {
            return new Queue("fanout.C");
        }

        @Bean
        FanoutExchange fanoutExchange() {
            return new FanoutExchange("fanoutExchange");//配置广播路由器
        }

        @Bean
        Binding bindingExchangeA(@Qualifier("Amessage") Queue AMessage,FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(AMessage).to(fanoutExchange);
        }

        @Bean
        Binding bindingExchangeB(@Qualifier("Bmessage") Queue BMessage, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(BMessage).to(fanoutExchange);
        }

        @Bean
        Binding bindingExchangeC(@Qualifier("Cmessage") Queue CMessage, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(CMessage).to(fanoutExchange);
        }
}
使用AmqpTemplate去发送消息
@Component
public class HelloSender {
    @Autowired
    private AmqpTemplate template;
    
    public void send() {
    template.convertAndSend("fanoutExchange","","hello,rabbit!");
    }
}
注意:Fanout Exchange形式又叫广播形式,因此我们发送到路由器的消息会使得绑定到该路由器的每一个Queue接收到消息,这个时候就算指定了Key,或者规则(即convertAndSend方法的参数2),也会被忽略!
2).消费者
@Component
public class HelloReceive {
    @RabbitListener(queues="fanout.A")
    public void processA(String str1) {
        System.out.println("ReceiveA:"+str1);
    }
    @RabbitListener(queues="fanout.B")
    public void processB(String str) {
        System.out.println("ReceiveB:"+str);
    }
    @RabbitListener(queues="fanout.C")
    public void processC(String str) {
        System.out.println("ReceiveC:"+str);
    }
}
6.Pull模式与Push模式拉取消息:
1).生产者

package com.example.tutorials;
 
 
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
 
 
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
 
 
/**
 * 使用 channel.basicGet() 方法,拉取指定队列中的内容
 * @create 2017-09-05
 * amqp-client 4.2.0
 **/
public class D7_PullSend {
    private final static String QUEUE_NAME = "pull_queue";
 
 
    /**
     * 生产者,
     * @param argv
     * @throws Exception
     */
    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        //设置登录账号
        factory.setHost(ServerInfo.host);
        factory.setPort(ServerInfo.port);
        factory.setUsername(ServerInfo.uName);
        factory.setPassword(ServerInfo.uPwd);
        //链接服务器
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
 
 
        Map<String,Object> args = new HashMap<>();
//        args.put("x-message-ttl",15*1000);//消息过期时间为 15 秒
        //args.put("x-expires",1*60*1000); //队列过期时间为 1 分钟
        //定义一个队列
        boolean duiable=false;//持久化
        boolean exclusive = false;//排他队列
        boolean autoDelete=false;//没有consumer时,队列是否自动删除
        channel.queueDeclare(QUEUE_NAME, duiable, exclusive, autoDelete, args);
 
 
        //发送消息
        System.out.println("输入要发送的消息,退出输入 x ");
        String message ;
        //
        AMQP.BasicProperties prop = new AMQP.BasicProperties
                .Builder()
                .expiration("15000") //消息过期时间为 15 秒
                .build();
        do{
            Scanner scanner = new Scanner(System.in);
            message = scanner.next();
            channel.basicPublish(""
                    , QUEUE_NAME
                    , prop
                    , message.getBytes("UTF-8"));
            System.out.println(" [x] Sent '" + message + "'");
        }while(!"x".equals(message));
        //关闭链接
        channel.close();
        connection.close();
    }
}
2).消费者(pull)
package com.example.tutorials;
 
 
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.GetResponse;
 
 
import java.util.Scanner;
 
 
/**
 * 使用 channel.basicGet() 方法,拉取指定队列中的内容
 * @create 2017-09-05
 * amqp-client 4.2.0
 **/
public class D7_PullRecv {
    private final static String QUEUE_NAME = "pull_queue";
 
 
    /**
     * 消费者, "Hello World!"
     * @param argv
     * @throws Exception
     */
    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        //设置登录账号
        factory.setHost(ServerInfo.host);
        factory.setPort(ServerInfo.port);
        factory.setUsername(ServerInfo.uName);
        factory.setPassword(ServerInfo.uPwd);
        //链接服务器
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //定义一个队列
        boolean duiable=false;//持久化
        boolean exclusive = false;//排他队列
        boolean autoDelete=false;//没有consumer时,队列是否自动删除
        channel.queueDeclare(QUEUE_NAME, duiable, exclusive, autoDelete, null);
 
 
        //接收消息
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
        boolean autoAck = true; //自动应答
 
 
        System.out.println("输入回车获取消息,退出输入 x ");
        String message ="";
        GetResponse resp ;
        do{
            Scanner scanner = new Scanner(System.in);
            scanner.nextLine();
            resp = channel.basicGet(QUEUE_NAME,autoAck);
            if(resp==null){
                System.out.println(QUEUE_NAME+" 队列无消息");
                continue;
            }
            message = new String(resp.getBody(), "UTF-8");
            System.out.println(String.format(" [x] Recv Count %s , msg = %s;"
                ,resp.getMessageCount()
                ,message));
        }while(!"x".equals(message));
    }
}
3).消费者(push)
ConnectionFactory factory = new ConnectionFactory();
		// 设置登录账号
		factory.setHost("127.0.0.1");
		factory.setPort(5672);
		factory.setUsername("guest");
		factory.setPassword("guest");
		// 链接服务器
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();

		// 定义一个队列
		boolean duiable = true;// 持久化
		boolean exclusive = false;// 排他队列
		boolean autoDelete = false;// 没有consumer时,队列是否自动删除
		channel.exchangeDeclare(EXCHANGE, "direct", true, false, false, null);
		channel.queueDeclare(QUEUE_NAME, duiable, exclusive, autoDelete, null);
		channel.queueBind(QUEUE_NAME, EXCHANGE, ROUTINGKEY2);

		QueueingConsumer consumer = new QueueingConsumer(channel);

		channel.basicConsume(QUEUE_NAME, true, consumer);
		String string = "";
		while (true) {
			Delivery delivery = consumer.nextDelivery();
			string = new String(delivery.getBody());
		}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值