前言 基于XML配置RabbitMQ

为了方便了解基于SpringBoot配置,我们首先熟悉在传统的Spring基于XML的配置,后续我们会更方便了解基于SpringBoot的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/rabbit
          http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <context:property-placeholder location="rabbitmq.properties"/>

  <!--扫描 rabbit 包 自动声明交换器、队列、绑定关系-->
    <context:component-scan base-package="com.jetsen.config.rabbit"/>

    <!--1.声明连接工厂,在此我们设置rabbitmq的连接,用户名、密码、虚拟主机等信息-->
    <rabbit:connection-factory id="connectionFactory"
                               addresses="${rabbitmq.addresses}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtualhost}"/>

    <!--2.创建一个管理器(org.springframework.amqp.rabbit.core.RabbitAdmin),用于管理交换,队列和绑定。
    auto-startup 指定是否自动声明上下文中的队列,交换和绑定, 默认值为 true。-->
    <rabbit:admin connection-factory="connectionFactory" auto-startup="true" />

   <!-- 3.消息对象json转换类 -->

  <bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />

    <!--4.声明 template 的时候需要声明 id 不然会抛出异常-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory  message-converter="jsonMessageConverter" exchange="remoting.exchange"/>

   <!--5.声明 队列-->
<!--定义消息队列,durable:是否持久化,如果想在RabbitMQ退出或崩溃的时候,不会失去所有的queue和消息,需要同时标志队列(queue)和交换机(exchange)是持久化的,即rabbit:queue标签和rabbit:direct-exchange中的durable=true,而消息(message)默认是持久化的可以看类org.springframework.amqp.core.MessageProperties中的属性public static final MessageDeliveryMode DEFAULT_DELIVERY_MODE = MessageDeliveryMode.PERSISTENT;
exclusive: 仅创建者可以使用的私有队列,断开后自动删除;
auto_delete: 当所有消费客户端连接断开后,是否自动删除队列 -->
<rabbit:queue name="remoting.queue" auto-declare="true"  durable="true" auto-delete="false" exclusive="false"/>

  <!--6.声明交换机、队列、绑定管理 -->

<rabbit:direct-exchange name="remoting.exchange" durable="true" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="remoting.queue" key="remoting.binding"/>
        </rabbit:bindings>
    </rabbit:direct-exchange>

 <!-- 7.定义消费者,需实现implements MessageListener,实现 public void onMessage(Message msg)方法--> 

<bean name="queuehandler" class="com.jetsen.config.rabbitmq.RecvHandler" />

<!-- 配置监听acknowledeg="manual"设置手动应答,它能够保证即使在一个worker处理消息的时候用CTRL+C来杀掉这个worker,或者一个consumer挂了(channel关闭了、connection关闭了或者TCP连接断了),也不会丢失消息。
因为RabbitMQ知道没发送ack确认消息导致这个消息没有被完全处理,将会对这条消息做re-queue处理。如果此时有另一个consumer连接,消息会被重新发送至另一个consumer会一直重发,直到消息处理成功,监听容器acknowledge="auto" concurrency="30"设置发送次数,最多发送30次 -->

<!-- 8.定义消费者监听队列 -->
     <rabbit:listener-container
         connection-factory="connectionFactory">
         <rabbit:listener ref="queuehandler" queues="remoting.queue" />
     </rabbit:listener-container>

</beans>
public class RecvHandler implements MessageListener {
 
 	private static final ObjectMapper MAPPER = new ObjectMapper();
 
 	public void onMessage(Message msg) {
 		try {
 			// msg就是rabbitmq传来的消息,需要的同学自己打印看一眼
 			// 使用jackson解析
 			JsonNode jsonData = MAPPER.readTree(msg.getBody());
 			System.out.println("接收消息:+jsonData.toString());
 
 		} catch (IOException e) {
 			e.printStackTrace();
 		}
 	}
 
 }

 当然消费者可以在JAVA代码中实现

@Bean
    public Queue remotingQueue() {
        // 创建一个持久化的队列 1
        return new Queue("remoting.queue", true);
    }    



    @Bean
    public SimpleMessageListenerContainer simpleMessageQueueLister(ConnectionFactory connectionFactory) {

        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        // 设置监听的队列
        container.setQueues(remotingQueue());
        // 指定要创建的并发使用者数。
        container.setConcurrentConsumers(1);
        // 设置消费者数量的上限
        container.setMaxConcurrentConsumers(5);
        // 设置是否自动签收消费 为保证消费被成功消费,建议手工签收
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        container.setMessageListener(new ChannelAwareMessageListener() {
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                // 可以在这个地方得到消息额外属性
                MessageProperties properties = message.getMessageProperties();
                //得到消息体内容
                byte[] body = message.getBody();
                System.out.println(remotingQueue().getName() + "收到消息:" + new String(body));
                /*
                 * DeliveryTag 是一个单调递增的整数
                 * 第二个参数 代表是否一次签收多条,如果设置为 true,则所有 DeliveryTag 小于该 DeliveryTag 的消息都会被签收
                 */
                channel.basicAck(properties.getDeliveryTag(), false);
            }
        });
        return container;
    }

一、在POM文件中添加AMQP的依赖 

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

二、在主类添加@EnableRabbit 注解 

  自动配置

  1.  RabbitAutoConfiguration
  2.  有自动配置了连接工厂ConnectionFactory;
  3. RabbitProperties 封装了 RabbitMQ的配置
  4.  RabbitTemplate :给RabbitMQ发送和接受消息;
  5. AmqpAdmin : RabbitMQ系统管理功能组件;
  6.  AmqpAdmin:创建和删除 Queue,Exchange,Binding
  7.  @EnableRabbit +  @RabbitListener 监听消息队列的内容
@EnableRabbit  //开启基于注解的RabbitMQ模式
@ComponentScan("com.jetsen.mq")
@SpringBootApplication
public class SpringRabbitMqApplication {

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

}

springboot rabbitmq发送和接收消息 rabbitmqtemplate发送消息_spring

springboot rabbitmq发送和接收消息 rabbitmqtemplate发送消息_spring_02

三、配置连接信息

spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbitadmin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/vhost_rabbitmq

四、发送消息 

// Message需要自己构造一个;定义消息体内容和消息头
 // rabbitTemplate.send(exchange,routeKey,message);

// object默认当成消息体,只需要传入要发送的对象,自动序列化发送给rabbitmq;
// rabbitTemplate.convertAndSend(exchange,routeKey,object);默认采用的是JAVA的序列化

@Override
public void convertAndSend(String exchange, String routingKey, final Object object) throws AmqpException {
		convertAndSend(exchange, routingKey, object, (CorrelationData) null);
}

springboot rabbitmq发送和接收消息 rabbitmqtemplate发送消息_xml_03

五、 接收消息

rabbitTemplate.receiveAndConvert("default.queue")

六、序列化机制

    private volatile MessageConverter messageConverter = new SimpleMessageConverter();

    在RabbitMQ中默认采用的序列化

SerializationUtils.deserialize(
                            createObjectInputStream(new ByteArrayInputStream(message.getBody()), this.codebaseUrl));

     修改序列化机制

@Configuration
public class RabbitMQConfig {

	@Bean
	public MessageConverter messageConverter() {
		return new Jackson2JsonMessageConverter();
	}
}

springboot rabbitmq发送和接收消息 rabbitmqtemplate发送消息_xml_04

七、监听机制@RabbitListener

@Component
@RabbitListener(queues = "default.queue")
public class RabbitMqConsumer {
	
	private static final Logger log= LoggerFactory.getLogger(RabbitMqConsumer.class);
	
	 @RabbitHandler
     public void process(MessageBean messageBean,Channel channel, Message message) throws IOException {
        log.info("HelloReceiver收到  : " + messageBean +"收到时间"+DateUtil.formatDateByFormat(new Date(), "yyyyMMddHHmmss"));
        try {
        	
            //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
        	//消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
        	 deliveryTag: 对于每个Channel来说,每个消息都会有一个DeliveryTag,一般用接收消息的顺序(index)来表示,一条消息就为1
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
            log.info("receiver success");
        } catch (IOException e) {
			log.info("接收到消息之后的处理发生异常.", e);
        }
	 

}}

同时注解可以申明交换机、队列和Binding

@RabbitListener(
		bindings = @QueueBinding( 
				value    = @Queue(value = "default.queue1", durable = "true"),
				exchange = @Exchange(value = "default.exchange1", 
        					 durable = "true",
        					 type = "topic",
                             ignoreDeclarationExceptions = "true"),
				key = "default_routing_key.#")
)

申明后会自动创建队列,交换机和Binding

springboot rabbitmq发送和接收消息 rabbitmqtemplate发送消息_rabbitmq_05

收消息方法可以有两种

1、使用实体 

@RabbitListener(queues = "default.queue")
	@RabbitHandler
	public void process(MessageBean messageBean, Channel channel, Message message) throws IOException {
		log.info("HelloReceiver收到【MessageBean】  : " + messageBean + "收到时间"
				+ DateUtil.formatDateByFormat(new Date(), "yyyyMMddHHmmss"));
		try {

			// 告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
			// 消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
			 deliveryTag:
			// 对于每个Channel来说,每个消息都会有一个DeliveryTag,一般用接收消息的顺序(index)来表示,一条消息就为1
			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			log.info("receiver success");
		} catch (IOException e) {
			log.info("接收到消息之后的处理发生异常.", e);
		}

	}

2、使用Message

@RabbitListener(queues = "default.queue")
	@RabbitHandler
	public void process(Channel channel, Message message) throws IOException {
		byte[] body = message.getBody();
		String messageStr = new String(body,"UTF-8");
		log.info("HelloReceiver收到【Message】  : " + messageStr + "收到时间"
				+ DateUtil.formatDateByFormat(new Date(), "yyyyMMddHHmmss"));
		try {

			// 告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了 否则消息服务器以为这条消息没处理掉 后续还会在发
			// 消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
			 deliveryTag:
			// 对于每个Channel来说,每个消息都会有一个DeliveryTag,一般用接收消息的顺序(index)来表示,一条消息就为1
			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
			log.info("receiver success");
		} catch (IOException e) {
			log.info("接收到消息之后的处理发生异常.", e);
		}

	}