学习RabbitMQ记录!

若如初见. 提交于 2020-03-05 21:07:26

一,什么是RabbitMQ ?

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。 AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

二 ,RabbitMQ 的推送流程

在这里插入图片描述
简单来说就是 消息生产者 发送消息给交换机,交换机找到和他绑定的队列 -》找到是否有消费者在监听这个队列,有就把消息发送过去。

exchanges 交换机的几种类型:
路由键介绍:队列和交换金绑定时候的一种标识。通过这个标识当我们发送信息给交换机时候带着这个路由键,系统就能够找到这个路由键下的队列。

  1. Direct Exchage 直连型的交换机,(只发送给对应的路由键下的队列)
  2. fanoutExchage 扇形交换机 (无视路由键会发送给所有绑定在这个交换机上的队列)
  3. topic exchange exchange 主题交换机 (和直连线交换机差不多,但是他的路由键可以用表达式)
    ** *(星号) 用来表示一个单词 (必须出现的)
    #(井号) 用来表示任意数量(零个或多个)单词 **

3. springboot下使用rabbitMQ 快速开发

依赖

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

配置文件

#rabbitMq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=ljb
spring.rabbitmq.password=888888
#确认消息已发送到交换机(Exchange)(可选)
spring.rabbitmq.publisher-confirm-type=correlated
#确认消息已发送到队列(Queue)(可选)
spring.rabbitmq.publisher-returns=true

消息生产者
创建交换机和队列。

@Configuration
public class RabbitMQConfig {
    //队列名字
    @Bean
    public Queue TestDirectQueue() {
        return new Queue("TestDirectQueue",true);  //true 是否持久
    }
    //Direct交换机 起名:TestDirectExchange
    @Bean
    DirectExchange TestDirectExchange() {
        return new DirectExchange("TestDirectExchange");
    }
    //绑定  将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting
    @Bean
    Binding bindingDirect() {
        return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");
    }
    //队列名字
    @Bean
    public Queue ljbQueu() {
        return new Queue("ljbQueu",true);  //true 是否持久
    }
    //Direct交换机 起名:TestDirectExchange
    @Bean
    DirectExchange libExchange() {
        return new DirectExchange("libExchange");
    }
    @Bean
    Binding ljbBind() {
        return BindingBuilder.bind(TestDirectQueue()).to(libExchange()).with("hello");
    }
    @Bean
    Binding libExchang() {
        return BindingBuilder.bind(ljbQueu()).to(libExchange()).with("hello");
    }
    
    //回调机制 防止丢失
    @Bean
    public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);
                System.out.println("ConfirmCallback:     "+"确认情况:"+ack);
                System.out.println("ConfirmCallback:     "+"原因:"+cause);
            }
        });

        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("ReturnCallback:     "+"消息:"+message);
                System.out.println("ReturnCallback:     "+"回应码:"+replyCode);
                System.out.println("ReturnCallback:     "+"回应信息:"+replyText);
                System.out.println("ReturnCallback:     "+"交换机:"+exchange);
                System.out.println("ReturnCallback:     "+"路由键:"+routingKey);
            }
        });

        return rabbitTemplate;
    }


}

使用回调机制的除了和上面代码一样在配置文件中直接交给spring也能可以这样操作

package com.example.demo.RabbitMQ;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * @Author:linjunbo
 * @Description: rabbitMq 信息发送回调
 * @Date: 2020/3/5 14:51
 */
@Component
public class RabbitMQCallBack implements RabbitTemplate.ConfirmCallback , RabbitTemplate.ReturnCallback {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * PostConstruct: 用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化.
     */
    @PostConstruct
    public void init() {
        //指定 ConfirmCallback
        rabbitTemplate.setConfirmCallback(this);
        //指定 ReturnCallback
        rabbitTemplate.setReturnCallback(this);
    }

    /**
     *   如果消息没有到达交换机,则该方法中ack = false,error为错误信息;
     *    如果消息正确到达交换机,则该方法中ack = true;
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        System.out.println("ConfirmCallback:     "+"相关数据:"+correlationData);
        System.out.println("ConfirmCallback:     "+"确认情况:"+ack);
        System.out.println("ConfirmCallback:     "+"原因:"+cause);
    }

    /**
     * 信息未正确到达队列则触发
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */

    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("ReturnCallback:     "+"消息:"+message);
        System.out.println("ReturnCallback:     "+"回应码:"+replyCode);
        System.out.println("ReturnCallback:     "+"回应信息:"+replyText);
        System.out.println("ReturnCallback:     "+"交换机:"+exchange);
        System.out.println("ReturnCallback:     "+"路由键:"+routingKey);
    }
}

control层 生产者发送信息

@RestController
public class SendRabbitMQController {
    @Autowired
    private  RabbitTemplate rabbitTemplate;
    @GetMapping("/sendDirectMessage")
    public String sendDirectMessage() {
        String messageId = String.valueOf(1);
        String messageData = "test message hello!";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String,Object> map=new HashMap<>();
        map.put("messageId",messageId);
        map.put("messageData",messageData);
        map.put("createTime",createTime);
        //将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
        rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);
        return "ok";
    }

启动程序 调用sendDirectMessage接口,控制台输出
在这里插入图片描述
可以看到信息已经成功发送到rabbitMQ 并且成功回调comfig函数。

消费者:
依赖


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

配置文件

server.port=8082
spring.rabbitmq.port=5672
spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.activemq.password=guest

创建 DirectReceiver监听队列

@Component

public class DirectReceive {

    @RabbitHandler
    @RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
    public void process(Map testMessage) {
        System.out.println("DirectReceiver消费者收到消息 (TestDirectQueue) : " + testMessage.toString());
    }
  
    }

运行起来 就能够生产者发送消息 客户端消费者监听的队列有信息就能接收到。

加入消费者确认机制。默认rabbitMQ是自动的 但是有时候希望自己来手动确认收到信息则可以这样。
1.修改DirectReceiver 监听队列 实现 ChannelAwareMessageListener 重写onMessage方法

添加消费者确认机制 消费者手动执行
basic.ack用于肯定确认
basic.nack用于否定确认(注意:这是AMQP 0-9-1的RabbitMQ扩展)
basic.reject用于否定确认,但与basic.nack相比有一个限制:一次只能拒绝单条消息

@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
      
        String msg = message.toString();
        String[] msgArray = msg.split("'");//可以点进Message里面看源码,单引号直接的数据就是我们的map消息数据
        Map<String, String> msgMap = mapStringToMap(msgArray[1].trim());
        String messageId=msgMap.get("messageId");
        String messageData=msgMap.get("messageData");
        System.out.println("messageId:"+messageId+"  messageData:"+messageData);
         //确定收到信息
        channel.basicAck(deliveryTag, true);

    } catch (Exception e) {
            //true 重新回队里 (回到队里后会一直发送给消费端,直到确定收到)
          channel.basicReject(deliveryTag, true);
        e.printStackTrace();
    }

    }
    //{key=value,key=value,key=value} 格式转换成map
    private Map<String, String> mapStringToMap(String str) {
        str = str.substring(1, str.length() - 1);
        String[] strs = str.split(",");
        Map<String, String> map = new HashMap<String, String>();
        for (String string : strs) {
            String key = string.split("=")[0].trim();
            String value = string.split("=")[1];
            map.put(key, value);
        }
        return map;

    }

二 创建RabbitMQConfig

package com.example.demo.config;

import com.example.demo.RabbitMQ.DirectReceiver;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author:linjunbo
 * @Description:
 * @Date: 2020/3/4 11:42
 */
@Configuration
public class RabbitMQConfig {
    @Autowired
    private CachingConnectionFactory connectionFactory;
    @Autowired
    private DirectReceiver directReceiver;//Direct消息接收处理类

    @Bean     //我要监听的队列bean
    public Queue TestDirectQueue() {
        return new Queue("TestDirectQueue",true);  //true 是否持久
    }
	//开启手动确认
    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer() {
    //设置mq连接工厂对象
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        //初始化消费者数量
        container.setConcurrentConsumers(1);
        //最大消费者数量
        container.setMaxConcurrentConsumers(1);
        // RabbitMQ默认是自动确认,这里改为手动确认消息
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL); 
        //监听队列
        container.setQueues(TestDirectQueue());
        //消费者监听类
        container.setMessageListener(directReceiver);
//        container.addQueues(fanoutRabbitConfig.queueA());
//        container.setMessageListener(fanoutReceiverA);
        return container;
    }


}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!