rabbitmq学习

rabbitmq学习,rabbitmq教程,rabbitmq安装

 docker 按照rabbitmq

docker run --name rabbitmq -d -p 15672:15672 -p 5672:5672 rabbitmq:3.10-management

 官方地址:https://www.rabbitmq.com/download.html

作用:

1、削蜂,

2、解耦

3、异步处理

核心概念:

交换机、队列、信道

交换机默认有4个类型:

direct:1条信息仅允许发送到1个队列。(RoutingKey匹配队列名)

fanout:1条信息发送到所有绑定的队列。(忽略RoutingKey)

header:1条信息发送到匹配的队队。(用header头部数据匹配)

topic:1条信息发送到匹配RoutingKey规则的队列。(一般用#和*通配符)

通配符号#(无视.点号)和*(遇到.点号则中止)说明:

topic.#那么这个队列会接收topic开头的消息,如topic.hello.world
topic.*那么这个队列只接收topic开头后面一个的消息,如topic.hello

 参考:https://www.cnblogs.com/cyq1162/p/16603461.html

官网网址:https://www.rabbitmq.com/download.html

docker安装:

docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.12-management

 默认用户名密码:guest,guest

网页访问地址:http://localhost:15672

默认是存到内存中的,不持久化

队列是否进行多个消费者进行消费

模式:

队列模式:

消息丢失怎么办:应答模式:自动应答,手动应答。

队列持久化:d

消息持久化: 

不公平分发:Qos

预取值 prefetch

消息确认原理:

发布确认:单个确认、cofitureselect()、批量确认、异步确认t(通过监听器实现)、chaal.

 通过map实现,删除确认的消息

消息确认:

交换机类型:

fanout:广播,发布订阅模式

direct交换机:路由模式,多重绑定

topic交换机

当一个队列绑定#,就接收所有消息,

如果当中没有#和*号,就是direct啦

*代表一个

#代表0或者多个

 死信队列:可以一定时间去做某个事情

ttl过期

延迟队列场景

 生产者到交换机,确认机制

优先级队列,消息需要设置优先级,必须先把消息发送到队列中,然后在消费。

惰性队列

 消费者宕机,大量消息没有消费

 

rabbitmq缺点:

保证mq没有问题、一致性问题、

 镜像队列:添加策略

 如何保证rabbitmq消息不被丢失:

生产者到rabbitmq:通过开启确认机制

rabbitmq丢失:通过持久化

消费者丢失:消费者确认机制

springboot配置文件

复制代码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    virtual-host: /
    username: guest
    password: guest
    publisher-confirm-type: correlated
    publisher-returns: true
    template:
      mandatory: true
      retry:
        #发布重试,默认false
        enabled: true
        #重试时间 默认1000ms
        initial-interval: 1000
        #重试最大次数 最大3
        max-attempts: 3
        #重试最大间隔时间
        max-interval: 10000
        #重试的时间隔乘数,比如配2,0 第一次等于10s,第二次等于20s,第三次等于40s
        multiplier: 1
    listener:
      # 默认配置是simple
      type: simple
      simple:
        # 手动ack Acknowledge mode of container. auto none
        acknowledge-mode: manual
        #消费者调用程序线程的最小数量
        concurrency: 10
        #消费者最大数量
        max-concurrency: 10
        #限制消费者每次只处理一条信息,处理完在继续下一条
        prefetch: 1
        #启动时是否默认启动容器
        auto-startup: true
        #被拒绝时重新进入队列
        default-requeue-rejected: true
复制代码

 

如何防止重复消费:

通过全局id方式,幂等性:一次或者多次结果都是一样的

复制代码
@Component
@RabbitListener(queues = RabbitMqConfig.ORDER_QUEUE, ackMode = "MANUAL")
public class OrderConsumer {

    @Autowired
    private OrderService orderService;

    @RabbitHandler
    public void handleMessage(OrderMessage orderMessage, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        try {
            orderService.handleOrder(orderMessage);
            // 手动确认消息已被消费
            channel.basicAck(tag, false);
        } catch (Exception e) {
            // 出现异常,拒绝消息并将其返回到队列中重新处理
            channel.basicReject(tag, true);
            e.printStackTrace();
        }
    }

}
复制代码

 可以在消息生产者服务中设置一个消息id,然后在消费者监听到消息后获取该id,再去查询这个id是否存在。如果不存在,则正常消费消息,并将消息的id存入数据库或者Redis中。如果存在,则丢弃此消息。例如:

复制代码
// 消息生产者服务
public void sendMessage() {
    String messageId = UUID.randomUUID().toString();
    // 将消息id和消息体一起发送
    rabbitTemplate.convertAndSend("exchange", "routingKey", message, new CorrelationData(messageId));
}

// 消息消费者服务
@RabbitListener(queues = "queue")
public void handleMessage(Message message, Channel channel) throws Exception {
    String messageId = message.getMessageProperties().getCorrelationId();
    // 先去查询这个id是否存在
    if (!redisTemplate.opsForValue().setIfAbsent(messageId, "")) {
        // 如果存在则丢弃此消息
        return;
    }
    // 正常消费消息
    // ...
}
复制代码

 多个消费者消费同一条数据--交换机fanout模式

配置示例:

复制代码
package com.zygh.environment.monitor.data.configure;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
//供平台使用
public static String QUEUE_NAME_PLAT = "environmentQueuePlat";
//供一标段使用
public static String QUEUE_NAME_1 = "environmentQueue1";
public static String EXCHANGE_NAME = "topicEnvironmentExchange";

//供平台使用
/**
* @params1 :队列名称
* @params2 :队列是否持久化(如果是,则重启服务不会丢失)
* @params3 :是否是独占队列(如果是,则仅限于此连接)
* @params4 :是否自动删除(最后一条消息消费完毕,队列是否自动删除)
*/
@Bean
public Queue topicQueuePlat() {
//声明一个耐用队列
return new Queue(QUEUE_NAME_PLAT, true,false,false);
}
/**
* @params1 :队列名称
* @params2 :队列是否持久化(如果是,则重启服务不会丢失)
* @params3 :是否是独占队列(如果是,则仅限于此连接)
* @params4 :是否自动删除(最后一条消息消费完毕,队列是否自动删除)
*/
//供一标段使用
@Bean
public Queue topicQueue1() {
//声明一个耐用队列
return new Queue(QUEUE_NAME_1, true,false,false);
}

/**
* @params1 :交换机名称
* @params2 :是否持久化
* @params4 :是否自动删除
*/
@Bean
public FanoutExchange topicExchange() {
return new FanoutExchange(EXCHANGE_NAME,true,false);
}
@Bean
public Binding topicQueueBind() {
return BindingBuilder.bind(topicQueuePlat()).to(topicExchange());
// .with("env.*");
}
@Bean
public Binding topicBinding1Bind() {
return BindingBuilder.bind(topicQueue1()).to(topicExchange());
// .with("env.*");
}

}
复制代码

 生产者示例:

 //因为是fanout广播模式,所以不用配置路由键。
                rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_NAME, "", sendData);

 

消费者示例:

复制代码
package com.zygh.hzhw.manage.consumer;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zygh.hzhw.manage.entity.clickhouse.EnvironmentData;
import com.zygh.hzhw.manage.service.EnvironmentDataService;
import com.zygh.hzhw.manage.vo.EnvironmentDataVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* 接收空气传感器数据
*/
@Component
public class EnvironmentDataConsumer {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private EnvironmentDataService environmentDataService;

// @RabbitListener(queues = "queue1")
@RabbitListener(queues = {"environmentQueuePlat"})
public void getData(Message message) {
try {
String result = new String(message.getBody(), "utf-8");
EnvironmentDataVO environmentDataVO = JSONObject.parseObject(result, EnvironmentDataVO.class);
//存数据库
EnvironmentData environmentData = new EnvironmentData();
environmentData.setAmmonia(environmentDataVO.getAmmonia()==null?0:Double.valueOf(environmentDataVO.getAmmonia()))
.setHumidity(environmentDataVO.getHumidity()==null?0:Double.valueOf(environmentDataVO.getHumidity()))
.setCode(environmentDataVO.getCode()).setHydrogen(environmentDataVO.getHydrogen()==null?0:Double.valueOf(environmentDataVO.getHydrogen()))
.setCreateTime(environmentDataVO.getCreateTime()).setPm25(environmentDataVO.getPm25()==null?0:Double.valueOf(environmentDataVO.getPm25()))
.setVoc(environmentDataVO.getVoc()==null?0:Double.valueOf(environmentDataVO.getVoc()))
.setTemperature(environmentDataVO.getTemperature()==null?0:Double.valueOf(environmentDataVO.getTemperature()));
environmentDataService.save(environmentData);
//最新数据更新到redis中
try {
String json = objectMapper.writeValueAsString(result);
if(StringUtils.isNoneBlank(environmentDataVO.getCode())){
stringRedisTemplate.opsForValue().set(environmentDataVO.getCode(), json);
}
//手动签收消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
} catch (Exception e) {
/**
* 有异常就拒收消息
* basicNack(long deliveryTag, boolean multiple, boolean requeue)
* requeue:true为将消息重返当前消息队列,重新发送给消费者;
* false将消息丢弃
*/
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);

e.printStackTrace();
}
}
}
复制代码

 rabbitmq和mqtt区别:

RabbitMQ和MQTT都有各自的使用场景。

RabbitMQ是一个开源的消息队列系统,它使用Erlang语言开发,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。RabbitMQ的适用场景包括解耦(为面向服务的架构提供基本的最终一致性实现)。例如,在用户下单后,订单系统需要通知库存系统,传统的做法是订单系统调用库存系统的接口,而使用RabbitMQ后,订单系统和库存系统可以各自处理自身的任务,通过RabbitMQ进行消息的传递,无需直接调用。

MQTT是一个轻量级的消息传递协议,常用于物联网领域。它的特点包括发布/订阅消息传递模式、基于TCP的可靠传输、轻量级和低功耗,适用于设备间的通信。因此,MQTT适用于需要长时间运行、低功耗和可靠的消息传递的物联网设备场景。

总结来说,RabbitMQ适用于需要高可靠性、安全性和灵活性的消息队列应用场景,而MQTT适用于需要轻量级、低功耗和可靠的消息传递的物联网设备场景。

 学习参考:https://www.bilibili.com/video/BV1cb4y1o7zz?p=39&spm_id_from=pageDriver&vd_source=f97080956039c326589b5b26607d960b

 

 

 

 实战教程参考:https://developer.aliyun.com/article/1228815?spm=a2c6h.28137022

概念参考:http://wed.xjx100.cn/news/165090.html?action=onClick

posted @   刘百会  阅读(13)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2021-07-30 java代码规范
点击右上角即可分享
微信分享提示