MQ系列集合

面试问题

RabbitMQ系列

特点

  • 优点
    开箱即用,方便部署,支持AMQP协议,支持非常多客户端语言
  • 缺点
    在这里插入图片描述

消息队列种类

  1. 工作模式,一个发送消息多个接收消息竞争消息
  2. 发布订阅模式,交换器绑定队列,所有队列都能获得消息
  3. 路由模式,交换器与队列绑定路由
  4. 通配符模式。
  5. Headers模式
  6. RPC模式
  • JMS(activemq)是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的
    jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,jms是java语言专属的消
    息服务标准,它是在api层定义标准,并且只能用于java应用;

RabbitMQ 如何保证消息不丢失

在这里插入图片描述

  1. 消息持久化
    要想做到消息持久化,必须满足以下三个条件,缺一不可。
  • Exchange 设置持久化
一般只需要:channel.exchangeDeclare(exchangeName, “direct/topic/header/fanout”, true);即在声明的时候讲durable字段设置为true即可
  • Queue 设置持久化
queue的持久化是通过durable=true来实现的

channel.queueDeclare("queue.persistent.name", true, false, false, null);
  • Message持久化
发送消息设置发送模式deliveryMode=2,代表持久化消息
如果队列持久化了但是消息没有持久化,那么mq重启后消息就消失了。
channel.basicPublish("exchange.persistent", "persistent", MessageProperties.PERSISTENT_TEXT_PLAIN, "persistent_test_message".getBytes());

这里的关键是:MessageProperties.PERSISTENT_TEXT_PLAIN
  1. ACK确认机制
    多个消费者同时收取消息,比如消息接收到一半的时候,一个消费者死掉了(逻辑复杂时间太长,超时了或者消费被停机或者网络断开链接),如何保证消息不丢?
    这个使用就要使用Message acknowledgment 机制,就是消费端消费完成要通知服务端,服务端才把消息从内存删除。
    这样就解决了,及时一个消费者出了问题,没有同步消息给服务端,还有其他的消费端去消费,保证了消息不丢的case。
  2. 消息补偿机制
    消息补偿机制需要建立在消息要写入DB日志,发送日志,接受日志,两者的状态必须记录。
    然后根据DB日志记录check 消息发送消费是否成功,不成功,进行消息补偿措施,重新发送消息处理。
  3. mq部署方式
    1 简单部署方式,一个挂了就没了
    2.普通部署,集群部署,一个挂了等待他恢复
    3.镜像部署

RabbitMQ保证不消费重复数据

  1. 生产消息重复:由于网络问题,ack确认超时,mq内部错误,宕机等原因导致导致消息的发送方不能正常收到确认消息,重新发送消息。
    由于生产者发送消息给MQ,在MQ确认的时候出现了网络波动,生产者没有收到确认,实际上MQ已经接收到了消息。这时候生产者就会重新发送一遍这条消息。生产者中如果消息未被确认,或确认失败,我们可以使用定时任务+(redis/db)来进行消息重试。在内存中、redis、数据库中建立一个list消费记录,每次消费消息前先扫描一下这个list,
  2. 消费者获得重复数据:由于网络原因,mq内部问题,消费者消费之后出现内部错误,mq宕机等原因导致mq没有收到消费者的确认消息然后重发消息。
    让每个消息携带一个全局的唯一ID,即可保证消息的幂等性,具体消费过程为:
    消费者获取到消息后先根据id去查询redis/db是否存在该消息。
    如果不存在,则正常消费,消费完毕后写入redis/db。
    如果存在,则证明消息被消费过,直接丢弃。

Springboot整合rabbitmq

1.添加amqp依赖

2.创建配置类@Configuration:@Bean申明交换机( ExchangeBuilder.topicExchange())和队列,交换机和队列进行绑定。

3.发送消息

4.消费方接收消息:添加注解: //监听email队列

@RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_EMAIL}) 
public void receive_email(String msg,Message message,Channel channel){         
	System.out.println(msg);
}
  • 一般情况下,消息发送方只管发送消息,不管队列绑定,由消息接收方绑定
//通配符模式,消息发送
    @Test
    public void topic() {
        rabbitTemplate.convertAndSend(EXCHANGE, "a.info", "ashkdfhaik");
    }


//消息接收方,在监听的时候进行队列绑定
@Component
public class topicConsumer {

    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue,
                    exchange = @Exchange(value = "exchange1",type = "topic"),
                    key = {"#.info.#"}
            )
    })
    public void infoGetMsg(String msg) {
        System.out.println("info接收到消息:" + msg);
    }

声明队列和交换机参数

  1. 队列
/**
* 参数明细:
* 1、队列名称
* 2、是否持久化,mq重启后还存在
* 3、是否独占此队列
* 4、队列不用是否自动删除,只要发送方和消费方同事没有连接就会删除
* 5、参数
*/
channel.queueDeclare(QUEUE_INFORM_EMAIL, true, false, false, null);
  1. 交换机
/**
* 参数明细
* 1、交换机名称
* 2、交换机类型,fanout、topic、direct、headers
*/
channel.exchangeDeclare(EXCHANGE_FANOUT_INFORM, BuiltinExchangeType.FANOUT);
  1. 消费者
/**
 * 监听队列String queue, boolean autoAck,Consumer callback
 * 参数明细
 * 1、队列名称
 * 2、是否自动回复,设置为true为表示消息接收到自动向mq回复接收到了,mq接收到回复会删除消息,设置
为false则需要手动回复,只要接收到消息就会发送确认消息,不会等业务完成,如果执行业务中出现问题,消息就没有了,所以不要设置自动确认。
 * 3、消费消息的方法,消费者接收到消息后调用此方法,消费消息之后的回调方法。
 */
channel.basicConsume(QUEUE_INFORM_EMAIL, true, defaultConsumer);

手动确认消息

public static void main(String[] args) {
    Connection connection = null;
    Channel channel = null;
    try {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("localhost");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        connection = connectionFactory.newConnection();

        channel = connection.createChannel();
        channel.queueDeclare(QUEUE1, true, false, false, null);
        channel.exchangeDeclare(EXCHANGE,BuiltinExchangeType.FANOUT);

        channel.queueBind(QUEUE1,EXCHANGE,"");
    //重点开始
        channel.basicQos(1);//关闭消息自动确认,每次接收一个消息,在获取消息之前设置
        Channel finalChannel = channel;
        DefaultConsumer consumer = new DefaultConsumer(finalChannel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String s1 = new String(body, "utf-8");
                System.out.println("fansout1 接收到消息:" + s1);
                //参数一:确认当前消息的标志位,参数二:是否开启多个消息同事确认。
                finalChannel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE1, false, consumer); //消费完消息的回调方法,参数二,false:关闭自动确认消息,参数三:回调方法
    //重点结束
    } catch (IOException e) {
        e.printStackTrace();
    } catch (TimeoutException e) {
        e.printStackTrace();
    }
}

Kafka、ActiveMQ、RabbitMQ、RocketMQ 对比

在这里插入图片描述

Kafka

在这里插入图片描述

特点

  • 优点
  1. 性能好,每秒几十万处理
  2. 支持分布式部署,支持可扩展性
  3. 支持分区,可复制,可容错
  4. 异步收发消息性能好
  • 缺点
  1. 发送消息不实时,需要积攒一起发
  2. topic数量太大之后性能下降

在这里插入图片描述

kafka高性能高吞吐的原因

  1. 磁盘读写,连续的顺序读写,保证了消息能够有效堆积
    在这里插入图片描述

在这里插入图片描述

RocketMQ

相比于rabbitmq,性能上有较大提升,而且用的是java开发,易于阅读和二次扩展
相比于kafka,性能差距不大,而且对于相应的时延有了较大的改进。相应是毫秒级别的。
在这里插入图片描述

开发问题

出现shutdown问题

检查发送方和消费方交换器绑定的是否是同一个通信方式,fanout或者topic方式。必须保持一致。

使用junit测试

使用junit测试消费者时,由于junit是单元测试,执行完毕之后所有的资源就释放了,不支持多线程运行,不能持续监听mq消息队列,所以建议使用main方法测试消费方。而且使用main方法不建议将通道和链接关闭,原因和单元测试相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值