rabbitMQ

rabbitMQ

安装

1.手动安装

官网下载-需要手动安装Erlang环境

2.docker安装

brew install --cask --appdir=/Applications docker

$ docker run -d --hostname my-rabbit --name some-rabbit rabbitmq:3

rabbitMq管理界面

在这里插入图片描述

什么是AMQP

全称:高级消息队列协议,由摩根大通集团联合其他公司共同设计。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。

特性:
1:分布式事务支持。
2:消息的持久化支持。
3:高性能和高可靠的消息处理优势。

消息分发策略的机制对比

ActieveMQRabbitMQkafkaRockeetMQ
发布订阅支持支持支持支持
轮询分发支持支持支持/
公平分发/支持支持/
重发支持支持/支持
消息拉取/支持支持支持

RabbitMQ核心组件

在这里插入图片描述

Connection :连接,应用程序与Broker的网络连接TCP/IP三次握手和四次握手

Channel :网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道,客户端可以建立多个Channel。每个Channel代表一个会话任务

Virtual Host:虚拟地址,用于进行逻辑隔离,最上层的消息路由,一个虚拟主机里有若干个Exchange和queue,同一个虚拟主机里面不能有相同名字的Exchange

bindings:Exchange和Queue之间的虚拟连接,bingding中可以保护多个routing key

routing key:是一个路由规则,虚拟机可以用它来确定如何路由一个特定消息

RabbitMq支持消息的工作模式

fanout模式

发布与订阅

在这里插入图片描述

原生代码

交换机和队列在可视化界面手动绑定情况下

public class Producer {


    public static void main(String[] args) {

        //所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp

        //1.创建连接工程
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("");
        factory.setPort(5672);
        factory.setUsername("");
        factory.setPassword("");
        factory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;
        //2.创建连接Connection
        try {
            connection = factory.newConnection("生产者");
            //3.通过连接获取通道Channel
            connection.createChannel();
            //4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,和接受消息
            String queueName = "queue1";
            /**
             * @Params1 队列名称
             * @Params2 是否要持久化durable =false 所谓持久化消息是否存盘
             * @Params3 排他性,是否是独占独立
             * @Params4 是否自动删除,随着最后一个消费者消息完毕以后是否把队列自动删除
             * @Params5 携带附属参数
             */
            channel.queueDeclare(queueName, false, false, false, null);
            //5.准备消息内容
            String message = "Hello kun";
            //6.准备交换机
            String exchange ="fanout-exchange";
            //7.定义路由key
            String routingkey = "";
            //8.指定交换机的类型
            String exchangeType ="direct";
            channel.basicPublish(exchange, queueName, null, message.getBytes());

        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
            //7.关闭连接
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭通道
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public class Consumer {

    public static void main(String[] args) {

        //所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp

        //1.创建连接工程
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("");
        factory.setPort(5672);
        factory.setUsername("");
        factory.setPassword("");
        factory.setVirtualHost("/");

        Connection connection = null;
        final Channel channel = null;
        //2.创建连接Connection
        try {
            connection = factory.newConnection("生产者");
            //3.通过连接获取通道Channel
            connection.createChannel();
            //4.通过创建交换机,声明队列,绑定关系,路由key,发送消息,和接受消息

            channel.basicConsume("queue1", true, new DeliverCallback() {
                public void handle(String consumerTag, Delivery message) throws IOException {
                    System.out.println("收到消息是" + new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                public void handle(String consumerTag) throws IOException {
                    System.out.println("接受消息失败");
                }
            });

        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
            //7.关闭连接
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            //8.关闭通道
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

绑定关系代码实现----原生代码

//声明交换机
channel.exchangeDeclare(exchange,exchangeType,true);
//声明队列
channel.queueDeclare("queue",true,false,false,null);
//绑定队列
channel.queueBind("queue",exchange,exchangeType);

direct模式

路由模式

在这里插入图片描述

topic模式

主题模式

在这里插入图片描述

lazy.#. #代表0个或者一个或者多个

*.orange. * *代表至少要满足一个

#.order.#

#.user.*

work模式

轮训分发

公平分发

//指标定义出来,qos=1
xxxx.basicQos("")

在这里插入图片描述

RabbitMq使用场景

解耦、削峰、异步

springboot整合

引入依赖

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

交换机和队列的配置绑定

fanout模式

package com.kun.order.config;

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

@Configuration
public class RabbitmqConfiguration {

    //1.声明交换机
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("direct_order_exchange", true, false);
    }

    //2.声明队列 sms.fanout.queue/email.fanout.queue
    @Bean
    public Queue smsQueue() {
        return new Queue("smsQueue", true);
    }

    @Bean
    public Queue emailQueue() {
        return new Queue("emailQueue", true);
    }

    //3.绑定关系
    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
    }
}

direct 模式

//3.绑定关系
    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue()).to(directExchange()).with("sms");
    }

    @Bean
    public Binding emailBinding() {
        return BindingBuilder.bind(smsQueue()).to(directExchange()).with("email");
    }

注解配置

package com.kun.order.topic;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;


@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "sms.topic.queue",durable = "true",autoDelete = "false"),
        exchange = @Exchange(value = "topic_order_exchange",type = ExchangeTypes.TOPIC),
        key = "#.sms.*"
))
public class TopicEmailConsumer {


    @RabbitHandler
    public void reviceMessage(String message) {
        System.out.println(message);
    }
}

生产者

package com.kun.order.service;


import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 模拟用户下单
     *
     * @param userId
     * @param productId
     * @param num
     */
    public void makeOrder(String userId, String productId, int num) {
        String orderId = UUID.randomUUID().toString();
        String exchangeName = "direct";
        String routingKey = "";

        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
    }
}

消费者

package com.kun.order.service;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


@Component
@RabbitListener(queues = "email.fanout.queue")
public class FanoutEmailConsumer {


    @RabbitHandler
    public void reviceMessage(String message) {
        System.out.println(message);
    }
}

TTL

过期时间TTL表示可以对消息设置预期的时间,目前有两种方法可以设置

1.通过队列属性设置,队列中所有消息都有相同的过期时间

2.通过消息进行单独设置,每条消息TTL可以不同

如果两种方法同时使用,则消息的过期时间以两者之间TTL较小的为准

TTL队列过期时间

@Bean
    public Queue smsQueue() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("x-message-ttl",5000);
        return new Queue("smsQueue", true,false,false,map);
    }

TTL消息过期时间

 public void makeOrder(String userId, String productId, int num) {
        String orderId = UUID.randomUUID().toString();
        String exchangeName = "direct";
        String routingKey = "";

        MessagePostProcessor processor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("5000");
                message.getMessageProperties().setContentEncoding("UTF-8");
                return message;
            }
        };

        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId,processor);
    }

DLX-死信交换机

@Bean
    public Queue smsQueue() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("x-message-ttl",5000);
        //x-dead-letter-exchange  -固定写法
      	//x-dead-letter-routing-key
      	map.put("x-dead-letter-exchange","exchangeName");
      	map.put("x-dead-letter-routing-key","routingName");
        return new Queue("smsQueue", true,false,false,map);
    }

RabbitMq的内存控制

参考帮助文档

RabbitMq分布式事务

具体实现

可靠生产和可靠消费的示意图

在这里插入图片描述

基于MQ的分布式事务消息的可靠生产问题-定时重发

在这里插入图片描述

基于MQ的分布式事务消息的消息重发

在这里插入图片描述

@Service
public class OrderMQService {

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //@PostConstruct注解好多人以为是Spring提供的。其实是Java自己的注解。
    //Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,
    // 并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
    @PostConstruct
    public void regCallback() {
        // 消息发送成功以后,给予生产者的消息回执,来确保生产者的可靠性
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("cause:"+cause);
                // 如果ack为true代表消息已经收到
                String orderId = correlationData.getId();

                if (!ack) {
                    // 这里可能要进行其他的方式进行存储
                    System.out.println("MQ队列应答失败,orderId是:" + orderId);
                    return;
                }

                try {
                    String updatesql = "update ksd_order_message set status = 1 where order_id = ?";
                    int count = jdbcTemplate.update(updatesql, orderId);
                    if (count == 1) {
                        System.out.println("本地消息状态修改成功,消息成功投递到消息队列中...");
                    }
                } catch (Exception ex) {
                    System.out.println("本地消息状态修改失败,出现异常:" + ex.getMessage());
                }
            }
        });
    }

基于MQ分布式事务消息的死信队列消息转移+人工处理

可靠消费问题
在这里插入图片描述

如果死信队列报错就进行人工处理
在这里插入图片描述

@Service
public class DeadMqConsumer {

    @Autowired
    private DispatchService dispatchService;



    // 解决消息重试的集中方案:
    // 1: 控制重发的次数 + 死信队列
    // 2: try+catch+手动ack
    // 3: try+catch+手动ack + 死信队列处理 + 人工干预
    @RabbitListener(queues = {"dead.order.queue"})
    public void messageconsumer(String ordermsg, Channel channel,
                                CorrelationData correlationData,
                                @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
        try {
            // 1:获取消息队列的消息
            System.out.println("收到MQ的消息是: " + ordermsg );
            //  2: 获取订单服务的信息
            Order order = JsonUtil.string2Obj(ordermsg, Order.class);
            // 3: 获取订单id
            String orderId = order.getOrderId();
            // 幂等性问题
            //int count = countOrderById(orderId);
            // 4:保存运单
            //if(count==0)dispatchService.dispatch(orderId);
            //if(count>0)dispatchService.updateDispatch(orderId);
             dispatchService.dispatch(orderId);
            // 3:手动ack告诉mq消息已经正常消费
            channel.basicAck(tag, false);
        } catch (Exception ex) {
            System.out.println("人工干预");
            System.out.println("发短信预警");
            System.out.println("同时把消息转移别的存储DB");
            channel.basicNack(tag, false,false);
        }
    }

}
### RabbitMQ 使用指南和教程 #### 安装 RabbitMQ RabbitMQ 是一种基于 AMQP 协议的消息中间件,用于实现分布式系统的可靠消息传递。以下是安装 RabbitMQ 的基本流程: 1. **安装 Erlang** RabbitMQ 基于 Erlang 编程语言开发,因此需要先安装 Erlang 运行环境。可以通过包管理器或者下载官方二进制文件完成安装。 2. **安装 RabbitMQ Server** 下载并安装 RabbitMQ Server 软件包。对于 Linux 用户,可以使用以下命令: ```bash sudo apt-get install rabbitmq-server ``` 3. **启动服务** 启动 RabbitMQ 服务后,默认监听端口为 `5672`(AMQP 协议),Web 管理界面默认运行在 `15672` 端口上。 ```bash sudo systemctl start rabbitmq-server ``` 4. **启用 Web 管理插件** 可通过以下命令启用 RabbitMQ 提供的 Web 管理工具: ```bash sudo rabbitmq-plugins enable rabbitmq_management ``` --- #### 配置用户与权限 为了安全访问 RabbitMQ 实例,通常需要创建自定义用户并分配相应权限。 - 创建新用户: ```bash rabbitmqctl add_user rabbitmq 211314 ``` 此操作会新增名为 `rabbitmq` 的用户,并将其密码设为 `211314`[^1]。 - 设置用户角色: ```bash rabbitmqctl set_user_tags rabbitmq administrator ``` 将该用户的标签设定为管理员角色,使其拥有完全控制权[^1]。 - 授予用户权限: ```bash rabbitmqctl set_permissions -p "/" rabbitmq ".*" ".*" ".*" ``` 上述命令授予用户对根虚拟主机 `/` 中所有资源的操作权限[^1]。 - 查看现有用户及其角色: ```bash rabbitmqctl list_users ``` --- #### 集群配置 RabbitMQ 支持多种集群模式来提升可用性和性能。主要分为两类:普通模式和镜像模式。 - **普通模式** 在这种模式下,各节点独立存储队列中的数据和其他元信息(如交换机)。当客户端尝试消费某个不在当前连接节点上的消息时,目标节点会被请求转发所需的数据[^2]。 - **镜像模式** 对比之下,在镜像模式中,指定队列的内容将在多个节点间保持一致副本。即使部分成员失效,剩余存活节点仍能继续提供完整的服务功能[^2]。 > 注意事项:尽管镜像模式提高了可靠性,但也带来了额外开销——网络流量增加以及写入延迟上升等问题需被充分考虑进去。 --- #### 应用集成示例 假设要在一个 Java 或 Python 应用程序里利用 RabbitMQ 来发送/接收消息,则可能涉及以下几个步骤: 1. **声明交换器 (Exchange)** 和绑定关系: ```java channel.exchangeDeclare(exchangeName, "direct", true); channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, exchangeName, routingKey); ``` 如此一来便完成了持久化队列及路由键关联工作[^3]。 2. 发布一条测试消息至上述已建立好的通道路径之中; 3. 订阅对应主题下的事件流以便实时捕获最新动态更新情况; --- #### 总结 以上涵盖了从基础安装到高级特性使用的整个过程概述。希望这些指导能够帮助您快速掌握如何部署与维护属于自己的 RabbitMQ 平台实例! 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值