消息队列-RabbitMQ各种消息使用

实现公平队列

公平队列:也就是能力强的消费者处理的消息多一点,能者多劳。也叫工作队列。

如何实现公平消费:使用手动 ack 模式,也就是消费者手动发送消息告诉 mq 服务器端,消息消费完成,可以从队列删除该消息。

主动拉去:消费者和MQ服务器端第一次建立连接的时候。
主动推送:消费者已经和MQ服务器端保持长连接了,只要生产者投递消息,MQ服务端会立即将消息转发给消费者。

公平队列实现原理:MQ服务端没次只会给消费者发送一条消息,如果消费者没有返回ack,就不会继续发消息。

实现代码:
生产者:

public class Product {
    private static final String QUEUE_NAME = "kaico";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1.创建连接
        Connection connection = RabbitMQConnection.getConnection();
        // 2.设置通道
        Channel channel = connection.createChannel();
        //开启消息确认机制
        channel.confirmSelect();
        // 3.设置消息
        String msg = "kaico开始学习RabbitMQ了111";
        System.out.println("msg:" + msg);
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

        //判断生产者投递消息是否成功
        if (channel.waitForConfirms()) {
            System.out.println("发送消息成功");
        } else {
            System.out.println("发送消息失败");
        }
        channel.close();
        connection.close();
    }
}

消费者

public class Consumer {
    private static final String QUEUE_NAME = "kaico";
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1.创建连接
        Connection connection = RabbitMQConnection.getConnection();
        // 2.设置通道
        final Channel channel = connection.createChannel();
        //MQ每次只会给消费者发送一条消息,消费者必须返回ack之后才会继续发送消息给消费者。
        channel.basicQos(1);//一次性处理消息的数量
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("消费者获取消息:" + msg);
                //手动发送消息告诉 mq 服务器端,从队列删除该消息
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        // 3.监听队列
        channel.basicConsume(QUEUE_NAME, false, defaultConsumer); //第二个参数是否开启自动应答模式

//        channel.close();
//        connection.close();

    }
}

保证消息不丢失

生产者:确保生产者消息投递成功到MQ成功;消息确认机制。
消费者:确保消费者消费消息成功,采用 ack 确认。
MQ服务器:需要将数据持久化到硬盘。

其他情况:硬盘坏了、持久化的过程断电了、或者其他情况投递消息失败的情况。
解决办法:通过表记录每次生产者投递消息,如果长期没有被消费,手动的补偿消费。

生产者投递消息失败的情况?
1、MQ服务器挂了
2、MQ拒绝接收消息(队列满了)

生产者和消费者的确认机制见上面的代码案例。
MQ持久化设置:
第一步、创建队列是设置成可持久化的
这种方式是把队列持久化
在这里插入图片描述1、durable是否持久化: durable为持久化、 Transient 不持久化
2、autoDelete 是否自动删除,当最后一个消费者断开连接之后队列是否自动被删除,可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除

第二步、代码设置
设置交换器的持久化

// 三个参数分别为 交换器名、交换器类型、是否持久化
channel.exchangeDeclare(EXCHANGE_NAME, "topic", true);  

设置队列的持久化

// 参数1 queue :队列名  
// 参数2 durable :是否持久化  
// 参数3 exclusive :仅创建者可以使用的私有队列,断开后自动删除  
// 参数4 autoDelete : 当所有消费客户端连接断开后,是否自动删除队列  
// 参数5 arguments  
channel.queueDeclare(QUEUE_NAME, true, false, false, null);  

设置消息的持久化

// 参数1 exchange :交换器  
// 参数2 routingKey : 路由键  
// 参数3 props : 消息的其他参数,其中 MessageProperties.PERSISTENT_TEXT_PLAIN 表示持久化  
// 参数4 body : 消息体  
channel.basicPublish("", queue_name, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());  

七种类型的消息

官网:https://www.rabbitmq.com/getstarted.html

交换机类型

  1. Direct exchange(直连交换机):根据生产者投递不同的路由键,再由交换机发送到队列实现匹配路由键。
  2. Fanout exchange(扇型交换机):只要绑定了队列,就会把消息发送到队列。
  3. Topic exchange(主题交换机):
  4. Headers exchange(头交换机)

交换机核心作用:分发路由消息、中转。
队列:容器存放多个不同的消息,遵循先进先出的原则。
消息:传递的参数
路由键:交换机根据路由键的值,发送到不同的队列中。
扇形交换机主要的特征:只要队列绑定同一个交换机,生产者将消息投递到交换机中,交换机会将消息发送给所有绑定的队列进行存放消息。

点对点队列

这个没有交换机,这个其实是默认的交换机(直连交换机),我们需要提供一个生产者一个队列以及一个消费者。

工作队列

一个生产者,一个默认的交换机(DirectExchange),一个队列,两或多个消费者。

一个队列对应了多个消费者,默认情况下,由队列对消息进行平均分配,消息会被分到不同的消费者手中。消费者可以配置各自的并发能力,进而提高消息的消费能力,也可以配置手动 ack,来决定是否要消费某一条消息。

发布订阅模式

一个生产者,多个消费者,每一个消费者都有自己的一个队列,生产者没有将消息直接发送到队列,而是发送到了交换机,每个队列绑定交换机,生产者发送的消息经过交换机,到达队列,实现一个消息被多个消费者获取的目的。需要注意的是,如果将消息发送到一个没有队列绑定的 Exchange上面,那么该消息将会丢失,这是因为在 RabbitMQ 中 Exchange 不具备存储消息的能力,只有队列具备存储消息的能力。

首先创建路由,路由类型:direct
在这里插入图片描述

生产者

public class ExProducer {
    private static final String EXCHANGE_NAME = "kaico_room19_direct";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        //  创建Connection
        Connection connection = RabbitMQConnection.getConnection();
        // 创建Channel
        Channel channel = connection.createChannel();
        // 通道关联交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);
        //定义路由键
        String routingKey = "kaico.sms";
        String msg = "kaico正在学习发布订阅模式消息" + routingKey;
        channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
        channel.close();
        connection.close();
        //如果消息没有绑定队列,消息可能会丢失。
    }
}

邮件消费者

public class EmailConsumer {
    /**
     * 定义短信队列
     */
    private static final String QUEUE_NAME = "consumerDirect_email";
    /**
     * 定义交换机的名称
     */
    private static final String EXCHANGE_NAME = "kaico_room19_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        System.out.println("邮件消费者...");
        // 创建我们的连接
        Connection connection = RabbitMQConnection.getConnection();
        // 创建我们通道
        final Channel channel = connection.createChannel();
        // 消费者将路由绑定队列
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("邮件消费者获取消息:" + msg);
            }
        };
        // 开始监听消息 自动签收
        channel.basicConsume(QUEUE_NAME, true, defaultConsumer);

    }
}

短信消费者

public class SmsConsumer {
    /**
     * 定义短信队列
     */
    private static final String QUEUE_NAME = "consumerDirect_sms";
    /**
     * 定义交换机的名称
     */
    private static final String EXCHANGE_NAME = "kaico_room19_direct";

    public static void main(String[] args) throws IOException, TimeoutException {
        System.out.println("短信消费者...");
        // 创建我们的连接
        Connection connection = RabbitMQConnection.getConnection();
        // 创建我们通道
        final Channel channel = connection.createChannel();
        // 消费者将路由绑定队列
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("短信消费者获取消息:" + msg);
            }
        };
        // 开始监听消息 自动签收
        channel.basicConsume(QUEUE_NAME, true, defaultConsumer);

    }
}

Direct

DirectExchange 的路由策略是将消息队列绑定到一个 DirectExchange 上,当一条消息到达 DirectExchange 时会被转发到与该条消息 routing key 相同的 Queue 上,在direct 类型上增加了 routing key,如果消费者没有指定路由键的话(默认)就会接收所有的消息。可以在后台管理页面接触绑定。
在这里插入图片描述

在这里插入图片描述

Topic

TopicExchange 是比较复杂但是也比较灵活的一种路由策略,在 TopicExchange 中,Queue 通过 routingkey 绑定到 TopicExchange 上,当消息到达 TopicExchange 后,TopicExchange 根据消息的 routingkey 将消息路由到一个或者多个 Queue 上。

在这里插入图片描述

创建交换机
在这里插入图片描述匹配规则:消费者根据路由键中是否有 # 和 * 来模糊匹配,符号 “#” 匹配路由键的一个或多个词,符号 “”匹配路由键的一个词。
例如:topic.
.queue那么这个队列会接收topic.aaaa.queue这样格式的消息,不能接收 topic.aaaa.bbbb.queue这样格式的消息,topic.#.queue 能接收 topic.aaaa.bbbb.queue 这样格式的消息

RPC

在这里插入图片描述

  1. 首先 Client 发送一条消息,和普通的消息相比,这条消息多了两个关键内容:一个是 correlation_id,这个表示这条消息的唯一 id,还有一个内容是 reply_to,这个表示消息回复队列的名字。
  2. Server 从消息发送队列获取消息并处理相应的业务逻辑,处理完成后,将处理结果发送到 reply_to 指定的回调队列中。
  3. Client 从回调队列中读取消息,就可以知道消息的执行情况是什么样子了。

这种情况其实非常适合处理异步调用。

代码案例暂无。

Publisher Confirms

publisher confirms是一个RabbitMQ的插件用于实现可靠的发布。当publisher confirms在channel上启用时,broker将异步确认客户端发送的信息,意味着服务端接收到了消息。
生产者

public class Producer {
    private static final String QUEUE_NAME = "kaico_room19_ceshi";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        System.out.println("生产者启动。。。");
        // 1.创建连接
        Connection connection = RabbitMQConnection.getConnection();
        // 2.设置通道
        Channel channel = connection.createChannel();
        // 生产者调用confirmSelect将channel设置为confirm模式注意
        channel.confirmSelect();
        // 3.设置消息
        String msg = "kaico学习rabbitmq的工作队列" + LocalDateTime.now().toString();
        System.out.println("msg:" + msg);
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        //确认消息发送到mq成功【确认机制】
        if (channel.waitForConfirms()) {
            System.out.println("发送消息成功");
        } else {
            System.out.println("发送消息失败");
        }
        channel.close();
        connection.close();
    }
}

Springboot整合RabbitMQ

springboot 只需要配置转换机和队列的信息就会帮我们自动创建对应的信息。

maven依赖

  <!-- 添加springboot对amqp的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

yml配置

spring:
  rabbitmq:
    ####连接地址
    host: www.kaicostudy.com
    ####端口号
    port: 5672
    ####账号
    username: kaico
    ####密码
    password: kaico
    ### 地址
    virtual-host: /kaicoStudy

编写配置类:也就是交换机和队列的信息,spring会自动创建对应的队列和交换机

@Configuration
public class OrderRabbitMQConfig {
    /**
     * 派单队列
     */
    public static final String ORDER_DIC_QUEUE = "order_dic_queue";
  
    /**
     * 派单交换机
     */
    public static final String ORDER_EXCHANGE_NAME = "order_exchange_name";

    /**
     * 定义派单队列
     *
     * @return
     */
    @Bean
    public Queue directOrderDicQueue() {
        return new Queue(ORDER_DIC_QUEUE);
    }


    /**
     * 定义订单交换机
     *
     * @return
     */
    @Bean
    DirectExchange directOrderExchange() {
        return new DirectExchange(ORDER_EXCHANGE_NAME);
    }

    /**
     * 派单队列与交换机绑定
     *
     * @return
     */
    @Bean
    Binding bindingExchangeOrderDicQueue() {
        return BindingBuilder.bind(directOrderDicQueue()).to(directOrderExchange()).with("orderRoutingKey");
    }
 
}

消费者

@Component
@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange(value = "order_exchange_name", type = ExchangeTypes.DIRECT), // 交换机(直连)
        key = "testRouting",                // 路由key
        value = @Queue("order_dic_queue"))) // 派单队列
public class MailMsgConsumer {

    @RabbitHandler
    public void process(String msg){

        System.out.println("邮件消费者消费消息:" + msg);
    }
}

生产者发送消息

@RestController
public class OrderController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @RequestMapping("/sendEasyMsg")
    public String sendEasyMsg() {
        String msg = "kaico学习Springboot整合Rabbitmq,时间:" + LocalDateTime.now().toString();
        rabbitTemplate.convertAndSend("order_exchange_name", "testRouting", msg);

        return "成功";
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值