25.RabiitMQ

原文链接

基本介绍

 

Message

消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成, 这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可 能需要持久性存储)等。

Publisher

消息的生产者,也是一个向交换器发布消息的客户端应用程序。

Exchange

交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。

Exchange有4种类型:direct(默认),fanout,topic,和headers,不同类型的Exchange转发消息的策略有所区别

Queue

消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直 在队列里面,等待消费者连接到这个队列将其取走。

Binding

绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交 换器理解成一个由绑定构成的路由表。

Exchange和Queue的绑定可以是多对多的关系。

Connection

网络连接,比如一个TCP连接。

Channel

信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过信道 发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁TCP都 是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。

Consumer

消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

Virtual Host

虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加 密环境的独立服务器域。每个 vhost 本质上就是一个mini版的RabbitMQ 服务器,拥 有自己的队列、交换器、绑定和权限机制。vhost是AMQP概念的基础,必须在连接时 指定,RabbitMQ 默认的vhost是/。

Broker

表示消息队列服务器实体

RabbitMQ安装

4369,25672(Erlang发现&集群端口)

5672,5671(AMQP端口)

15672 (web管理后台端口)

61613,61614(STOMP协议端口)

1883,8883(MQTT协议端口)

docker命令

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p  25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

自动重启

docker update rabbitmq --restart=always

账号密码默认为guest

Exchange类型

三种类型的交换机(header因为效率低下不使用不讲)

direct:点对点,一对一精确匹配

fanout:广播模式-》不分路由键

topic:发布订阅-》区分路由键,可以实现部分广播,topic交换机将路由键和绑定键的字符串切分成单词,这些单词用.来隔开,他识别两个通配符,#匹配0个或者多个单词,*号匹配一个单词

SpringBoot整合rabbitMQ

在order模块里导入依赖

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

 主类上开启rabbitMQ

@EnableDiscoveryClient
@SpringBootApplication
@EnableRabbit//开启RabbitMQ
public class GulimallOrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallOrderApplication.class, args);
    }

}

配置文件

 amqpAdmin,rabbitTemplate的使用

@Slf4j
@SpringBootTest
class GulimallOrderApplicationTests {
    @Autowired
    AmqpAdmin amqpAdmin;//增删改查交换机,队列,绑定关系
    @Autowired
    RabbitTemplate rabbitTemplate;//收发消息
    //创建交换机
    @Test
    void createExchange() {
        //名字-是否持久化-是否自动删除
        DirectExchange directExchange = new DirectExchange("hello-java-exchange",true,false);
        amqpAdmin.declareExchange(directExchange);
        log.info("交换机创建完成");
    }
    //创建队列
    @Test
    void createQueue() {
        //名字-是否持久化-是否排它(只能被一个连接使用)-自动删除
        Queue queue = new Queue("hello-java-queue",true,false,false
        );
        amqpAdmin.declareQueue(queue);
        log.info("队列创建成功");
    }
    //创建绑定关系
    @Test
    void createBindingRelation() {
        //目的地(队列)-目的地类型-交换机-路由键-绑定的参数
        Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE,"hello-java-exchange","hello.java",null);
        amqpAdmin.declareBinding(binding);
        log.info("绑定创建成功");
    }
    //如果需要使用json序列化机制需要在配置类里进行配置
    //发送消息
    @Test
    void sendMessage() {
        //经过测试对象不能是内部类
        OrderEntity orderEntity=new OrderEntity();
        orderEntity.setId(1L);
        orderEntity.setCreateTime(new Date());
         //交换机-路由键-消息内容(也可以发送对象-》需要序列化对象)-消息的唯一ID
        rabbitTemplate.convertAndSend("hello-java-exchange","hello.java",orderEntity,new CorrelationData(UUID.randomUUID().toString()));//转换并且发送
    }
}

配置类

@Configuration
public class MyRabbitConfig {
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();//转成json
    }
}

@RabbitListener和@RabbitHandler配合接受消息

//可以放在类位置和方法位置
@RabbitListener(queues = {"hello-java-queue"})//声明需要监听的队列 使用此注解该类需要在spring容器里注册
@Service("mqMessageService")
public class MqMessageServiceImpl extends ServiceImpl<MqMessageDao, MqMessageEntity> implements MqMessageService {
    @RabbitHandler//只能放在方法上
    //channel是传输数据的通道
    // Queue可以有很多人来监听,同一个消息只能有一个收到,只有一个消息的方法处理完了,才能继续处理下一条消息
    public void receiveMessage(Message message, OrderEntity orderEntity, Channel channel){//可以直接获取消息体的对象
        byte[] body=message.getBody();//消息体 我们发送的对象
        MessageProperties messageProperties = message.getMessageProperties();//消息头
        System.out.println("收到消息:"+message+"==>Type:"+message.getClass()+"内容:"+orderEntity);
    }
    @RabbitHandler//一个队列可能有多个消息对象--可以区分不同的消息
    public void receiveMessage2(Message message, OrderSettingEntity orderSettingEntity, Channel channel){//可以直接获取消息体的对象
        byte[] body=message.getBody();//消息体 我们发送的对象
        MessageProperties messageProperties = message.getMessageProperties();//消息头
        System.out.println("收到消息:"+message+"==>Type:"+message.getClass()+"内容:"+orderSettingEntity);
    }

}

可靠投递

保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍,为此引入确认机制

  • publisher: confirmCallback 确认模式  p->b:消息只要被broker接收到就会执行,如果是集群(cluster)模式需要所有的broker接收到才会调用
  • publisher: returnCallback 未投递到queue退回模式 e->q: queue收到就会执行,可以异步调回
  • consumer: ack机制  q->c: 消费者收到消息默认会自动ack

生产者丢消息给服务器(p-b)--》服务器交给交换机----》交换机交给queue(e->q)-》queue交还给消费者(q->c)

yml配置

spring:
  rabbitmq:
    host: 192.168.116.128
    port: 5672 #高级协议端口
    virtual-host: /
    publisher-confirm-type: correlated #开启发送端的确认
    publisher-returns: true #开启发送端抵达队列的确认
    template:
      mandatory: true #只要抵达队列,以异步发送优先回调
    listener:
      simple:
        acknowledge-mode: manual #手动ack消息

confirmCallback

确认回调配置--》配置类

@Autowired
    RabbitTemplate rabbitTemplate;
    //确认回调配置
    @PostConstruct//在MyRabbitConfig对象创建完之后,才会执行这个方法
    public void initRabbitTemplate(){
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 当前消息的唯一关联数据(消息的唯一id)
             * @param b 消息是否成功收到 只要消息抵达broker就等于true
             * @param s 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("confirm...correlationData["+correlationData+"]==>ack["+b+"]==>cause["+s+"]");
            }
        });
    }

returnCallback 配置

//设置消息抵达队列的确认回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             *
             * @param message 投递失败的消息详细信息
             * @param i 回复的状态码 312对应No_Route 没有路由信息
             * @param s 回复的文本内容
             * @param s1 当时这个消息发送给那个交换机
             * @param s2 当时这个消息用的哪个路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("message:"+message+",code:"+i+",content:"+s+",exchange:"+s1+",route-key:"+s2);
            }
        });

ack

默认是自动确认,消费端只要收到消息,那么就会直接删除掉队列里的消息,如果在处理前几个消息的时候,服务器宕机,后续消息就会全部丢失

为了防止这样的情况我们可以手动确认,只要我们不确认,消息就会一直放在队列里,等到下次服务上线,会继续接收

 @RabbitHandler//只能放在方法上
    //channel是传输数据的通道
    // Queue可以有很多人来监听,同一个消息只能有一个收到,只有一个消息的方法处理完了,才能继续处理下一条消息
    public void receiveMessage(Message message, OrderEntity orderEntity, Channel channel){//可以直接获取消息体的对象
        byte[] body=message.getBody();//消息体 我们发送的对象
        MessageProperties messageProperties = message.getMessageProperties();//消息头
        System.out.println("收到消息:"+message+"==>Type:"+message.getClass()+"内容:"+orderEntity);
        //消费者处理完消息
        long deliveryTag = message.getMessageProperties().getDeliveryTag();//这个tag是正在channel里按顺序自增的
        try {
            //传入tag-是否批量签收
            channel.basicAck(deliveryTag,false);//签收
            //channel.basicNack(deliveryTag,false,true);//拒签 是否批量签收-最后一个参数表示是否重新入队,为false直接丢弃了
            //channel.basicReject(deliveryTag,false);//拒签 没有批量参数 最后一个参数表示是否重新入队,为false直接丢弃了
        }catch (Exception e){
            //网络中断
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值