e2

滴滴侠,fai抖

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

1.Windows下安装RabbitMQ的步骤详解+图解(erlang+RabbitMQ)

2.SpringBoot集成RabbitMQ参考文章

 

 

1.RabbitMQ介绍

RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。RabbitMQ主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层。保存这个数据。

AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。

RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

2.AmqpTemplate,RabbitTemplate

 

Spring AMQP提供了一个发送和接收消息的操作模板类AmqpTemplate。 AmqpTemplate它定义包含了发送和接收消息等的一些基本的操作功能。RabbitTemplate是AmqpTemplate的一个实现。

RabbitTemplate支持消息的确认与返回,为了返回消息,RabbitTemplate 需要设置mandatory 属性为true,并且CachingConnectionFactory 的publisherReturns属性也需要设置为true。返回的消息会根据它注册的RabbitTemplate.ReturnCallback setReturnCallback 回调发送到给客户端,

一个RabbitTemplate仅能支持一个ReturnCallback 。

为了确认Confirms消息, CachingConnectionFactory 的publisherConfirms 属性也需要设置为true,确认的消息会根据它注册的RabbitTemplate.ConfirmCallback setConfirmCallback回调发送到给客户端。一个RabbitTemplate也仅能支持一个ConfirmCallback.

3.SpringBoot集成RabbitMQ

pom

  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3.  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4.  
    <modelVersion>4.0.0</modelVersion>
  5.  
     
  6.  
    <groupId>com.example</groupId>
  7.  
    <artifactId>demo</artifactId>
  8.  
    <version>0.0.1-SNAPSHOT</version>
  9.  
    <packaging>jar</packaging>
  10.  
     
  11.  
    <name>rabbitMQ</name>
  12.  
    <description>Demo project for Spring Boot</description>
  13.  
     
  14.  
    <parent>
  15.  
    <groupId>org.springframework.boot</groupId>
  16.  
    <artifactId>spring-boot-starter-parent</artifactId>
  17.  
    <version>2.0.2.RELEASE</version>
  18.  
    <relativePath/> <!-- lookup parent from repository -->
  19.  
    </parent>
  20.  
     
  21.  
    <properties>
  22.  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  23.  
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  24.  
    <java.version>1.8</java.version>
  25.  
    </properties>
  26.  
     
  27.  
    <dependencies>
  28.  
    <dependency>
  29.  
    <groupId>org.springframework.boot</groupId>
  30.  
    <artifactId>spring-boot-starter-amqp</artifactId>
  31.  
    </dependency>
  32.  
     
  33.  
    <dependency>
  34.  
    <groupId>org.springframework.boot</groupId>
  35.  
    <artifactId>spring-boot-starter-web</artifactId>
  36.  
    </dependency>
  37.  
     
  38.  
    <dependency>
  39.  
    <groupId>org.springframework.boot</groupId>
  40.  
    <artifactId>spring-boot-starter-test</artifactId>
  41.  
    <scope>test</scope>
  42.  
    </dependency>
  43.  
    </dependencies>
  44.  
     
  45.  
    <build>
  46.  
    <plugins>
  47.  
    <plugin>
  48.  
    <groupId>org.springframework.boot</groupId>
  49.  
    <artifactId>spring-boot-maven-plugin</artifactId>
  50.  
    </plugin>
  51.  
    </plugins>
  52.  
    </build>
  53.  
     
  54.  
     
  55.  
    </project>

自动配置信息  这里我开启ACK消息确认

  1.  
    server.port=8083
  2.  
    #服务器配置
  3.  
    spring.application.name=rabbitmq-hello-sending
  4.  
    #rabbitmq连接参数
  5.  
    spring.rabbitmq.host=localhost
  6.  
    spring.rabbitmq.port=5672
  7.  
    spring.rabbitmq.username=linpeng
  8.  
    spring.rabbitmq.password=123456
  9.  
    # 开启发送确认
  10.  
    spring.rabbitmq.publisher-confirms=true
  11.  
    # 开启发送失败退回
  12.  
    spring.rabbitmq.publisher-returns=true
  13.  
    # 开启ACK
  14.  
    spring.rabbitmq.listener.direct.acknowledge-mode=manual
  15.  
    spring.rabbitmq.listener.simple.acknowledge-mode=manual

创建消息队列  队列名:hello 和 helloObj

  1.  
    package com.example.demo;
  2.  
     
  3.  
    import com.rabbitmq.client.Channel;
  4.  
    import org.springframework.amqp.core.*;
  5.  
    import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
  6.  
    import org.springframework.amqp.rabbit.connection.ConnectionFactory;
  7.  
    import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
  8.  
    import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
  9.  
    import org.springframework.context.annotation.Bean;
  10.  
    import org.springframework.context.annotation.Configuration;
  11.  
     
  12.  
    @Configuration
  13.  
    public class RabbitConfig {
  14.  
     
  15.  
    @Bean
  16.  
    public Queue QueueA() {
  17.  
    return new Queue("hello");
  18.  
    }
  19.  
     
  20.  
    @Bean
  21.  
    public Queue QueueB() {
  22.  
    return new Queue("helloObj");
  23.  
    }
  24.  
     
  25.  
    /**
  26.  
    * Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。
  27.  
    * @return
  28.  
    */
  29.  
    @Bean
  30.  
    FanoutExchange fanoutExchange() {
  31.  
    return new FanoutExchange("ABExchange");
  32.  
    }
  33.  
     
  34.  
     
  35.  
    @Bean
  36.  
    Binding bindingExchangeA(Queue QueueA, FanoutExchange fanoutExchange) {
  37.  
    return BindingBuilder.bind(QueueA).to(fanoutExchange);
  38.  
    }
  39.  
     
  40.  
    @Bean
  41.  
    Binding bindingExchangeB(Queue QueueB, FanoutExchange fanoutExchange) {
  42.  
    return BindingBuilder.bind(QueueB).to(fanoutExchange);
  43.  
    }
  44.  
    }

消息发送者 Sender  使用 RabbitTemplate  不采用 AmqpTemplate

  1.  
    package com.example.demo;
  2.  
     
  3.  
    import org.springframework.amqp.core.AmqpTemplate;
  4.  
    import org.springframework.amqp.core.Message;
  5.  
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
  6.  
    import org.springframework.amqp.rabbit.support.CorrelationData;
  7.  
    import org.springframework.beans.factory.annotation.Autowired;
  8.  
    import org.springframework.stereotype.Component;
  9.  
    import org.springframework.stereotype.Service;
  10.  
     
  11.  
    import java.util.Date;
  12.  
    //RabbitTemplate.ConfirmCallback
  13.  
    @Service
  14.  
    public class HelloSender implements RabbitTemplate.ReturnCallback {
  15.  
     
  16.  
    @Autowired
  17.  
    // private AmqpTemplate rabbitTemplate;
  18.  
    private RabbitTemplate rabbitTemplate;
  19.  
    public void send() {
  20.  
    String context = "你好现在是 " + new Date() +"";
  21.  
    System.out.println("HelloSender发送内容 : " + context);
  22.  
    // this.rabbitTemplate.setConfirmCallback(this);
  23.  
    this.rabbitTemplate.setReturnCallback(this);
  24.  
    this.rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
  25.  
    if (!ack) {
  26.  
    System.out.println("HelloSender消息发送失败" + cause + correlationData.toString());
  27.  
    } else {
  28.  
    System.out.println("HelloSender 消息发送成功 ");
  29.  
    }
  30.  
    });
  31.  
    this.rabbitTemplate.convertAndSend("hello", context);
  32.  
    }
  33.  
     
  34.  
    public void sendObj() {
  35.  
    MessageObj obj = new MessageObj();
  36.  
    obj.setACK(false);
  37.  
    obj.setId(123);
  38.  
    obj.setName("zhangsan");
  39.  
    obj.setValue("data");
  40.  
    System.out.println("发送 : " + obj);
  41.  
    this.rabbitTemplate.convertAndSend("helloObj", obj);
  42.  
    }
  43.  
     
  44.  
    @Override
  45.  
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
  46.  
    System.out.println("sender return success" + message.toString()+"==="+i+"==="+s1+"==="+s2);
  47.  
    }
  48.  
     
  49.  
    // @Override
  50.  
    // public void confirm(CorrelationData correlationData, boolean b, String s) {
  51.  
    // System.out.println("sender success");
  52.  
    // }
  53.  
     
  54.  
    }

消息接受者 Receiver 注解方式接受消息 

  1.  
    package com.example.demo;
  2.  
     
  3.  
    import com.rabbitmq.client.Channel;
  4.  
    import org.springframework.amqp.core.Message;
  5.  
    import org.springframework.amqp.core.Queue;
  6.  
    import org.springframework.amqp.rabbit.annotation.RabbitHandler;
  7.  
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
  8.  
    import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
  9.  
    import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
  10.  
    import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
  11.  
    import org.springframework.amqp.support.AmqpHeaders;
  12.  
    import org.springframework.beans.factory.annotation.Configurable;
  13.  
    import org.springframework.context.annotation.Bean;
  14.  
    import org.springframework.messaging.handler.annotation.Headers;
  15.  
    import org.springframework.stereotype.Component;
  16.  
     
  17.  
    import java.io.IOException;
  18.  
    import java.util.Date;
  19.  
    import java.util.Map;
  20.  
     
  21.  
    @Component
  22.  
    @RabbitListener(queues = "hello")
  23.  
    public class HelloReceiver {
  24.  
     
  25.  
    @RabbitHandler
  26.  
    public void process(String hello,Channel channel, Message message) throws IOException {
  27.  
    System.out.println("HelloReceiver收到 : " + hello +"收到时间"+new Date());
  28.  
    try {
  29.  
    //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
  30.  
    channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
  31.  
    System.out.println("receiver success");
  32.  
    } catch (IOException e) {
  33.  
    e.printStackTrace();
  34.  
    //丢弃这条消息
  35.  
    //channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
  36.  
    System.out.println("receiver fail");
  37.  
    }
  38.  
     
  39.  
    }
  40.  
    }

备注:我们用注解的方式来接受消息 就不要用 自己创建对象实现ChannelAwareMessageListener的方式来接受消息 这种方式还要去全局里面配置 麻烦,直接用@RabbitListener(queues = "hello")最简单

消息确认  因为我在属性配置文件里面开启了ACK确认 所以如果代码没有执行ACK确认 你在RabbitMQ的后台会看到消息会一直留在队列里面未消费掉 只要程序一启动开始接受该队列消息的时候 又会收到

 channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

//消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息

channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

//ack返回false,并重新回到队列,api里面解释得很清楚

channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);

//拒绝消息

channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);

TestController测试

  1.  
    @Autowired
  2.  
    private HelloSender helloSender;
  3.  
     
  4.  
    /**
  5.  
    * 单生产者-单个消费者
  6.  
    */
  7.  
    @RequestMapping("/test")
  8.  
    public void hello() throws Exception {
  9.  
    helloSender.send();
  10.  
    }

RabbitMQ后台 两个队列

发送消息

ACK场景测试

我们把HelloReceiver的ACK确认代码注释掉 那消息就算程序收到了 但是未确认ACK导致消息服务器以为他是未成功消费的 后续还会再发


重启程序


 

posted on 2019-09-06 21:07  纯黑Se丶  阅读(476)  评论(0编辑  收藏  举报