消息中间件RabbitMQ

MQ:Message Queue,消息队列

MOM:Message Oriented Middleware,面向消息的中间件

主流实现方式:

JMS:Java Message Service,java消息服务。java规范;只能使用java实现;只有两种消息模型(点对点和发布订阅)。(ActiveMQ)

AMQP:Advanced Message Queueing Protocol,高级消息队列协议。本质上是协议,只规定了数据格式;任何语言都可以实现;可以有任意多种消息模型。(RabbitMQ)

三个主要作用:
1.异步
2.解耦
3.削峰填谷

主流产品:
1.ActiveMQ:Apache,基于JMSjava
2.RabbitMQ:独立社区(spring) 基于AMQP实现 ,用erlang语言实现(erlang面向并发的编程语言)
3.RocketMQ:阿里开源 贡献给apache
4.Kafka:Apache 追求吞吐量

RabbitMQ:

  • Connection:连接

  • Channel:管道 信道 默认一个连接只有一个信道

  • Exchange:交换机,接受消息并转发消息

    Fanout:转发消息给所有队列
    Direct:指定RK(RoutingKey),符合RK的队列可以获取消息
    Topic:通配符 *(匹配一个单词)  #(匹配任意多个)
    
  • Queue:队列,接受 转发 存储消息

  • RoutingKey:路由键

  • Producer:生产者,发送消息的应用程序

  • Consumer:消费者,接受并消费消息的应用程序

五种消息模型:

	1.简单模型(Simple)
		一个生产者 一个队列 一个消费者
	2.工作模型(work queue)
		一个生产者 一个队列 多个消费者  消息只能被一个消费者消费
		能者多劳(Fair Dispatch 公平分发):channel.basicQos(1)

	3.发布订阅(publish/subscribe)
		一个生产者 一个交换机 多个队列 每个队列有自己的消费者
		
	4.路由模型(route)
		多了一个routingKey
	
	5.通配模型(Topic)
	两个通配符
		*:匹配一个单词
		#:匹配 零个或多个单词(任意多个)

简单面试题
1.怎么避免消息堆积?

  • 搭建消费者集群(工作队列) 配合能者多劳、公平分发
    能者多劳:channel.basicQos(prefetch: 1)

      spring.rabbitmq.listener.simple.prefetch=1
    
  • 多线程并发消费:concurrency。container启动的时候会根据设置的concurrency的值创建n个BlockingQueueConsumer。注意当队列的Exclusive(默认false)为true时,concurrency参数只能是1

      spring.rabbitmq.listener.simple.concurrency=4
    

2.怎么避免消息丢失?

  • 生产者确认机制:确保消息到达MQ、

    spring.rabbitmq.publisher-confirm-type=none/simple/correlated
    spring.rabbitmq.publisher-returns=true
    
  • 消息持久化:确保不会因为MQ服务器宕机导致消息丢失,交换机持久化、队列持久化、消息持久化

      @Queue("名称")	//默认持久化
    
  • 消费者确认机制:确保消息被消费者正确无误的消费,手动确认basicAck

    spring.rabbitmq.listener.simple.acknowledge-mode=none/auto/manual
    

生产者:
1.依赖 AMQP启动器
2.yml配置:

spring.rabbitmq.host/port/vitual-host/username/password
spring.rabbitmq.publisher-confirm-type=none/simple/correlated
spring.rabbitmq.publisher-returns=true

3.java配置

rabbitTemplate.setConfirmCallback();
rabbitTemplate.setReturnCallback();

4.代码
发送消息

rabbitTemplate.convertAndSend(交换机, RK, 消息内容)

RabbitConfig:

//交换机
@Bean
public Exchange exchange(){
	return new FanoutExchange/DirectExchange/TopicExchange("名称", durable, autoDelete, null);
	return ExchangeBuilder.fanoutExchange/directExchange/topicExchange().durable(true/false).autoDelete().withArguments().build();
}

//队列
@Bean
public Queue queue(){
	return new Queue("名称", durable, autoDelete, exclusive, arguments);
	// x-message-ttl x-dead-letter-exchange x-dead-letter-routing-key
	return QueueBuilder.durable(名称)/nonDurable("名称").autoDelete().exclusive().deadLetterExchange().deadLetterRoutingKey().ttl().build()
}

//队列绑定到交换机
@Bean
public Binding binding(){
	return new Binding("队列名称", DestinationType.QUEUE, "交换机", "RK", arguments);
	return BindingBuilder.bind(队列对象).to(交换机).with(RK).and()/noargs()
}

消费者:

1.个性化配置

spring.rabbitmq.listener.type=simple/direct 
spring.rabbitmq.listener.simple.prefetch=1
spring.rabbitmq.listener.simple.acknowledge-mode=none/auto/manual
spring.rabbitmq.listener.simple.concurrency=4

2.代码

@RabbitListener(queues={})
@RabbitListener(bindings = @QueueBinding(
	value = @Queue("名称"),
	exchange = @Exchange(value="名称", ignoreDeclaretionExceptions="true", type=ExchangeTypes.TOPIC/DIRECT/FANOUT)
	key = {RK列表}
))

//手动确认
channel.baskAck(message.getMessageProperties().getDeliveryTag(), multipe)
//是否重新入队
channel.basicNack(message.getMessageProperties().getDeliveryTag(), multipe, requeue)
channel.basicReject(message.getMessageProperties().getDeliveryTag(), requeue)

例:ConsumerListener消费者代码

@Component
public class ConsumerListener {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "SPRING_RABBIT_QUEUE", durable = "true"),
            exchange = @Exchange(value = "SPRING_RABBIT_EXCHANGE", ignoreDeclarationExceptions = "true", type = ExchangeTypes.TOPIC),
            key = {"a.b"}
    ))
    public void listener(String msg, Channel channel, Message message) throws IOException {
        try {
            System.out.println(msg);
            // 手动确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            e.printStackTrace();
            // 是否已经重试过
            if (message.getMessageProperties().getRedelivered()){
                // 已重试过直接拒绝
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            } else {
                // 未重试过,重新入队
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            }
        }
    }
}

死信队列:普通的队列,只是存放的是死信消息而已
死信交换机:普通交换机,只是转发的是死信消息而已
Dead Letter:死信,有三类消息会变成死信消息:

	1.basicNack、basicRejected,requeue参数为false
	2.超时消息
	3.队列已满,依然入队

延时队列

x-message-ttl:延时时间
x-dead-letter-exchange:死信交换机 
x-dead-letter-routing-key:死信routingkey

创建交换机的两种方式:
①注解方式(如上消费者所示)
②代码方式(如下所示)

new Queue("队列名称", durable, autoDelete, exclusive, argments)
QueueBuilder.nonDurable()/durable().autoDelete().exclusive().withArguments().build()

new Topic/Direct/FanoutExchange("交换机名称", durable, autoDelete, argments);
ExchangeBuilder.fanout/direct/topicExchange("名称").durable().autoDelete().withArguments().build()

new Binding("队列名称", DestinationType.Queue, "交换机名称", RK, arguments);
BindingBuilder.bind(队列对象).to(交换机对象).with(RK).noargs();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值