RabbitMQ使用手册

1. RabbitMQ配置

1.1RabbitMQ管理命令行

# 1.服务器启动相关命令行
systemctl start|restart|stop|status rabbitmq-server
# 2.管理命令行
rabbitmqctl help #查看更多命令
# 3.插件管理命令行
rabbitmq-plugins enable|disable|list
1.2 Web管理界面介绍
1.2.1 OverView概览

image-20201114094327707

2. 消息队列模式

第一种模型(直连)

image-20201114094618637

在上图所示的模型中有以下概念

  • P:生产者
  • C:消费者
  • queue:消息队列

1.开发生产者

connection = RabbitmqUtils.getConnection();
channel = connection.createChannel();
/**
 * 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
 * 参数三:是否独占队列、参数四:是否自动删除队列
 * 参数五:额外参数
 */
channel.queueDeclare("hello", false, false, false, null);
/**
 * 参数一:交换机名称、参数二:队列名称、
 * 参数三:额外消息设置、参数四:消息具体内容
 */
channel.basicPublish("", "hello", false, false, null, "hello rabbitmq".getBytes());

2.消费者

Connection connection = RabbitMqUtils.getConnection();
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));
    }
});

工具类RabbitMQUtils.java

package com.rabbitmq.study;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * @FileName: RabbitMQUtils
 * @Author Steven
 * @Date: 2020/11/14
 */
public class RabbitMQUtils {
    private static ConnectionFactory factory;

    static {
        factory = new ConnectionFactory();
        factory.setHost("xxx.xxx.xxx.xxx");
        factory.setPort(5672);
        factory.setVirtualHost("/cms");
        //这只访问虚拟主机的用户名、密码
        factory.setUsername("ems");
        factory.setPassword("123");

    }

    public static Connection getConnection() {
        try {
            return factory.newConnection();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void closeChannelAndConn(Channel channel, Connection conn) {
        try {
            if (channel != null) {
                channel.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.参数说明

/**
* 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
* 参数三:是否独占队列、参数四:是否自动删除队列
* 参数五:额外参数
*/
channel.queueDeclare("aa", true, false, false, null);

第二种模型(work queue)

image-20201114164419337

work queue,也被称为(task queue),任务模型,多个消费者绑定一个队列共同消费,队列中的消息一旦被消费就会消失,因此消息不回被重复消费

角色:

  • P:生产者,任务发布
  • queue:队列存放消息
  • C1:消费者1,消费消息较慢
  • C2:消费者2,消费消息较快

1.生产者Send.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
 * 参数一:队列名字(不存在会自动创建)、参数二:是否要持久化、
 * 参数三:是否独占队列、参数四:是否自动删除队列
 * 参数五:额外参数
 */
channel.queueDeclare("work", true, false, false, null);
/**
 * 参数一:交换机名称、参数二:队列名称、
 * 参数三:额外消息设置、参数四:消息具体内容
 */
for (int i = 1; i <=100 ; i++) {
    channel.basicPublish("", "work", false, false, MessageProperties.PERSISTENT_TEXT_PLAIN, (i+"hello rabbitmq").getBytes());
}
RabbitMQUtils.closeChannelAndConn(channel,connection);

2.消费者:Recv1.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",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));
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

3.消费者:Recv2.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("work",true,false,false,null);
channel.basicConsume("work",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));
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

消息确认机制

1.消费者:Recv1.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
//每次只能消费一个消息
channel.basicQos(1);
channel.queueDeclare("work", true, 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("消费者2:" + new String(body));
        //参数1:确认具体为队列中的那个消息,参数2:是否同时确认多个消息
        channel.basicAck(envelope.getDeliveryTag(),false);
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

2.消费者:Recv2.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
//每次只能消费一个消息
channel.basicQos(1);
channel.queueDeclare("work",true,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("消费者2:"+new String(body));
        //参数1:确认具体为队列中的那个消息,参数2:是否同时确认多个消息
        channel.basicAck(envelope.getDeliveryTag(),false);
    }
});

第三种模型(fanout)

fanout:扇出,又称为广播

image-20201116225823129

在广播模式下,消息发送流程

  • 多个消费者
  • 每个消费者都绑定这自己的队列(queue)
  • 每个队列都要绑定到交换机(exchange)上
  • 生产者只将消息发送给交换机
  • 交换机(exchange)决定将消息发送给哪个队列

1.开发生产者:send.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();  
/**
    * 通道声明为交换机
    * 参数1:交换机名称、参数2:交换机类型 fanout:代表广播类型
 */
channel.exchangeDeclare("logs","fanout");
channel.basicPublish("logs","",null,"RabbitMq fanout".getBytes());

2.消费者:Recv.java

channel.exchangeDeclare("logs","fanout");
//创建临时队列
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName,"logs","");
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));
    }
});

第四种模型(Routing)

1.Routing之订阅模型-Direct(直连)

在fanout模式中,一条消息,会被所有订阅的消费者消费,但在某些情景下,我们希望不同的消息被不同的消费者消费,这时就用到Direct类型的Exchange。

在Direct模型下:

  • 队列与交换机绑定,不能任意绑定,而是指定一个RoutingKey(路由key)
  • 生产者向Exchange(交换机)发送消息时需要指定RoutingKey​(路由key)
  • Exchange不在把消息交给绑定的队列,而是根据消息的Routing Key进行判断,只有当消息中的Routing Key队列中的Routing Key完全一致才会收到消息

流程

image-20201119101017421

图解

  • P:生产者,向exchange发送消息时会指定一个Routing Key
  • X:Exchange(交换机),接受生产者消息,然后把消息发送给与Routing Key相同的队列
  • C1:消费者,指定需要接受Routing Key为error的消息
  • C2:消费者,指定需要接受Routing key为error、info、warning的消息

1.生产者

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("direct.logs", "direct");
String queueName = channel.queueDeclare().getQueue();
String routingKey = "error";
channel.queueBind(queueName, "direct.logs", routingKey);
channel.basicPublish("direct.logs", routingKey, null, (routingKey + " Rabbit Direct").getBytes("utf-8"));
RabbitMQUtils.closeChannelAndConn(channel, connection);

2.消费者-1

channel.exchangeDeclare("direct.logs", "direct");
String queueName = channel.queueDeclare().getQueue();

channel.queueBind(queueName, "direct.logs", "info");
channel.queueBind(queueName, "direct.logs", "warning");
channel.queueBind(queueName, "direct.logs", "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("Recv1日志:" + new String(body));
    }
});

3.消费者-2

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
/**
 * 参数一:交换机名称、参数二:交换机的类型:(direct)直连式
 */
channel.exchangeDeclare("direct.logs", "direct");
/**
 * 声明一个临时队列
 */
String queueName = channel.queueDeclare().getQueue();
/**
 * 参数一:队列名称、参数二:交换机名称、参数三:RoutingKey(路由key)
 */
channel.queueBind(queueName, "direct.logs", "info");
channel.queueBind(queueName, "direct.logs", "warning");
channel.queueBind(queueName, "direct.logs", "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("Recv1日志:" + new String(body));
    }
});

2.Routing之订阅模型-Topic(直连)

topic类型的exchange与direct相比,都可以根据RoutingKey把消息路由到不同的队列中,只不过topic在绑定队列时RoutingKey可以使用通配符,这种模型RoutingKey一般有一个或多个单词组成多个单词间用“.”分割类如:item.insert

image-20201122103218370
# 通配符
 *(start):匹配一个单词
 #(start):匹配多个单词
#如:
 hello.*: hello.java、hello.world
 hello.#: hello.java.world    

1.生产者:send.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
// 参数一:交换机名称
channel.exchangeDeclare("topics","topic");
String routingKey="user.save.findAll";
channel.basicPublish("topics",routingKey,null,("RabbitMQ Topic RoutingKey:"+routingKey).getBytes());
RabbitMQUtils.closeChannelAndConn(channel,connection);

2.消费者:Recv.java

Connection connection = RabbitMQUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("topics","topic");
String queueName = channel.queueDeclare().getQueue();
String routingKey="user.#";
channel.queueBind(queueName,"topics",routingKey);
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("RabbitMQ tutorials Topic:"+new String(body));
    }
});

3.SpringBoot整合RabbitMQ

3.1 初始环境单间

1.引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2.在application.yml中添加相关配置

server:
  address: 8080
spring:
  rabbitmq:
    addresses: 47.101.36.177
    username: ems
    password: 123
    virtual-host: /cms
    port: 5672
  application:
    name: rabbitmq-study

RabbitMQ简化操作对象,使用时直接在项目中注入即可

3.2 第一种Hello World模型使用

1.生产者:Send.java

//注入RabbitMQ模板对象
@Autowired
private RabbitTemplate rabbitTemplate;

@Test
public void testHelloWord() {
    rabbitTemplate.convertAndSend("hello","hello world");
}

2.消费者:HelloConsumer.java

@Component
// 持久化,非独占、非自动删除的队列
@RabbitListener(queuesToDeclare = @Queue(value = "hello",durable = "false",autoDelete = "false"))
public class HelloConsumer {
    @RabbitHandler
    public void recive(String message){
        System.out.println("消息队列中的消息:"+message);
    }
}

3.3 第二种work queue模型使用

1.生产者:Send.java

@Test
public void testWork(){
    for (int i = 0; i < 10; i++) {
        rabbitTemplate.convertAndSend("work","Work模型");
    }

}

2.消费者:WorkConsumer.java

@Component
public class WorkConsumer {

    @RabbitListener(queuesToDeclare=@Queue("work"))
    public void receive1(String message){
        System.out.println("work model1:"+message);
    }

    @RabbitListener(queuesToDeclare=@Queue("work"))
    public void receive2(String message){
        System.out.println("work model2:"+message);
    }
}

3.4第三种广播模型(fanout)使用

1.生产者:send.java

/**
 * 广播模型fanout
 */
@Test
public void fanoutModel(){
    rabbitTemplate.convertAndSend("logs","","fanout广播模型");
}

2.消费者:FanoutConsumer

@Component
public class FanCoutModel {

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,//不指定时,创建临时队列
                    exchange =@Exchange(name = "logs",type = "fanout"))
    })
    public void receive1(String message){
        System.out.println("message1:"+message);
    }

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,//不指定时,创建临时队列
                    exchange =@Exchange(name = "logs",type = "fanout"))
    })
    public void receive2(String message){
        System.out.println("message1:"+message);
    }

}

3.5第三种路由(RoutingKey)模型使用

1.生产者:send.java

/**
 * route模式
 */
@Test
public void testRoute(){
    rabbitTemplate.convertAndSend("directs","error","通过路由发送error信息");
}

2.消费者:RouteConsumer.java

@Component
public class RouteConsumer {
    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,exchange = @Exchange(name = "directs",type = "direct"),
                    key = {"info","error","warning"})
    })
    public void receive1(String message){
        System.out.println("receive1:"+message);
    }
    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,exchange = @Exchange(name = "directs",type = "direct"),
            key ={"error"})
    })
    public void receive2(String message){
        System.out.println("receive2:"+message);
    }

}

3.6第三种动态路由模型(Topic)使用

1.生产者:send.java

/**
 * topic模式(动态路由)
 */
@Test
public void testTopic(){
    rabbitTemplate.convertAndSend("topics","user.save","topic动态路由模式");
}

2.消费者:TopicConsumer.java

@Component
public class TopicConsumer {

    @RabbitListener(bindings = {
            @QueueBinding(value = @Queue,exchange = @Exchange(
                    value = "topics",type = "topic"
            ),key = {"user.*"})
    })
    public void receive1(String message){
        System.out.println("receive1:"+message);
    }

    @RabbitListener(bindings = {@QueueBinding(value = @Queue,exchange = @Exchange(
            name = "topics",type = "topic"),key = {"user.#"})})
    public void receive2(String message){
        System.out.println("receive2:"+message);
    }
}

4.RabbitMQ集群

4.1集群架构
4.1.1普通集群(副本集群)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值