RabbitMQ学习——整合Spring AMQP、SpringBoot以及Spring Cloud Stream

RabbitMQ整合Spring AMQP

RabbitAdmin

RabbitAdmin可以很好的操作RabbitMQ,在Spring中直接注入即可。

添加SpringBoot工程(只利用Spring特性)
配置依赖:


		<dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>3.6.5</version>
		</dependency>

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

添加配置类:

@Configuration
@ComponentScan("com.learn.rabbitmqspring.*")
public class RabbitMQConfig {

    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses("192.168.222.101:5672");//host:port
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        connectionFactory.setVirtualHost("/");
        return connectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin() {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory());
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}

编写测试类:


@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqSpringApplicationTests {

	@Test
	public void contextLoads() {
	}

	@Autowired
	private RabbitAdmin rabbitAdmin;

	@Test
	public void testRabbitAdmin() {
		rabbitAdmin.declareExchange(new DirectExchange("test.direct",false,false));
		rabbitAdmin.declareExchange(new TopicExchange("test.topic",false,false));

		// 删除如果返回true,则说明创建成功
		//Assert.assertTrue(rabbitAdmin.deleteExchange("test.direct"));
		//Assert.assertTrue(rabbitAdmin.deleteExchange("test.topic"));

		rabbitAdmin.declareQueue(new Queue("test.direct.queue",false));
		rabbitAdmin.declareQueue(new Queue("test.topic.queue",false));

		rabbitAdmin.declareBinding(new Binding(
				"test.direct.queue",/*要绑定的队列*/
				Binding.DestinationType.QUEUE, /*绑定类型*/
				"test.direct", /*绑定的交换机*/
				"direct",/*配置路由key*/
				new HashMap<>()

		));

		/**
		 * 另外一种方式
		 */
		rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue("test.direct.queue",false)) //可以在这里直接创建队列(上面的队列申明可以不要)
								.to(new TopicExchange("test.topic",false,false)) //可以在这里创建Exchange
								.with("user.#")); //指定路由键

	}

}

direct:

在这里插入图片描述

topic:

在这里插入图片描述

下面测试清空队列:
在这里插入图片描述

// 清空队列
rabbitAdmin.purgeQueue("dlx.queue",false);
rabbitAdmin.purgeQueue("test_ack_queue",false);

在这里插入图片描述

RabbitTemplate

  • 消息模板,在于SpringAMQP整合的时进行发送消息的类
  • 提供了丰富的发送消息方法:可靠性投递消息方法、回调监听消息接口ConfirmCallback、返回值确认接口ReturnCallback等。
  • 注入到Spring容器直接使用
  • 与Spring整合时需要实例化,与SpringBoot整合时,只需要在配置文件中添加相关配置
	@Test
	public void testSendMessage() {
		//1.创建消息
		MessageProperties messageProperties = new MessageProperties();
		//	添加两个自定义属性
		messageProperties.getHeaders().put("desc","信息描述");
		messageProperties.getHeaders().put("type","自定义消息类型");
		Message message = new Message("Hello RabbitMQ".getBytes(),messageProperties);
		//2.发送消息
		rabbitTemplate.convertAndSend("test.topic", "user.test", message, new MessagePostProcessor() {
			@Override
			public Message postProcessMessage(Message message) throws AmqpException {
				System.out.println("添加额外设置");
				message.getMessageProperties().getHeaders().put("test","自定义新值");
				return message;
			}
		});
	}

在这里插入图片描述

SimpleMessageListenerContainer

简单消息监听容器

  • 监听多个队列、自动启动、自动声明
  • 设置事务特性、事务管理器、事务属性、事务容量(并发量),是否开启事务、回滚消息
  • 设置消费者数量、批量消费
  • 设置消息确认和自动确认模式。是否重回队列、异常捕获handler函数
  • 设置消费者标签生成策略、是否独占模式、消费者属性
  • 设置具体的监听器、消息转换器
  • 可以进行动态设置,比如在运行时可以动态修改消费者数量大小、接收消息的模式等

在Config类中添加如下配置:


    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("topic001",true,false);
    }

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

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with("user.#");
    }
    
    @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue());
        container.setConcurrentConsumers(1);//当前消费者数量
        container.setMaxConcurrentConsumers(5);//最大消费者数量
        container.setDefaultRequeueRejected(false);//重回队列
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);//签收模式
        //消费端标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID().toString();
            }
        });
        container.setMessageListener(new ChannelAwareMessageListener() {
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                String msg = new String(message.getBody());
                System.out.println("消费:"+msg);
            }
        });
        return container;
    }

然后启动SpringBoot项目,可以看到刚才建好的消费者:

在这里插入图片描述

发送消息测试函数:

	@Test
	public void testSendMessage2() {
		//1.创建消息
		MessageProperties messageProperties = new MessageProperties();
		//	添加两个自定义属性
		messageProperties.getHeaders().put("desc","信息描述");
		messageProperties.getHeaders().put("type","自定义消息类型");
		Message message = new Message("Hello RabbitMQ".getBytes(),messageProperties);
		//2.发送消息
		rabbitTemplate.convertAndSend("topic001", "user.test", message, new MessagePostProcessor() {
			@Override
			public Message postProcessMessage(Message message) throws AmqpException {
				System.out.println("添加额外设置");
				message.getMessageProperties().getHeaders().put("test","自定义新值");
				return message;
			}
		});
	}

然后可以看到SpringBoot Console打印:

消费:Hello RabbitMQ

MessageConverter消息转换器

实现MessageConverter接口

  • 重写下面两个方法:
    • toMessage:java对象转换为Message
    • fromMessage:Message对象转换为java对象

比如TextMessageConverter:

public class TextMessageConverter implements MessageConverter {
    @Override
    public Message toMessage(Object o, MessageProperties messageProperties) throws MessageConversionException {
        return new Message(o.toString().getBytes(),messageProperties);
    }

    @Override
    public Object fromMessage(Message message) throws MessageConversionException {
        String contentType = message.getMessageProperties().getContentType();
        if (contentType != null &&contentType.contains("text")) {
            return new String(message.getBody());
        }
        return message.getBody();
    }
}

下面我们测试一下Jackson2JsonMessageConverter,是官方定义的:

   @Bean
    public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setQueues(queue());
        container.setConcurrentConsumers(1);//当前消费者数量
        container.setMaxConcurrentConsumers(5);//最大消费者数量
        container.setDefaultRequeueRejected(false);//重回队列
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);//签收模式
        //消费端标签策略
        container.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String queue) {
                return queue + "_" + UUID.randomUUID().toString();
            }
        });
        /*container.setMessageListener(new ChannelAwareMessageListener() {
            @Override
            public void onMessage(Message message, Channel channel) throws Exception {
                String msg = new String(message.getBody());
                System.out.println("消费:"+msg);
            }
        });*/

        /**
         * 支持Json的MessageConverter
         */
        MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
        adapter.setDefaultListenerMethod("consumeMessage");//配置listener 方法
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        adapter.setMessageConverter(jackson2JsonMessageConverter);
        container.setMessageListener(adapter);


        return container;
    }

MessageDelegate:


public class MessageDelegate {
    public void handleMessage(byte [] messageBody) {
        System.out.println("默认方法,消息内容:"+new String(messageBody));
    }

    public void consumeMessage(Map messageBody) {
        System.err.println("map方法,消息内容:" + messageBody);
    }
}

测试方法:


	@Test
	public void testSendJsonMessage() throws JsonProcessingException {
		Order order = new Order("001","消息订单","订单描述信息");
		ObjectMapper mapper = new ObjectMapper();
		String json = mapper.writeValueAsString(order);
		System.out.println("order 4 json: " + json);

		MessageProperties messageProperties = new MessageProperties();
		messageProperties.setContentType("application/json");//一定要设置
		Message message = new Message(json.getBytes(),messageProperties);
		rabbitTemplate.send("topic001","user.test",message);

	}

添加Order:

package com.learn.rabbitmqspring.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
    private String id;
    private String name;
    private String content;
}

RabbitMQ整合Spring Boot

  • publisher-confirms:实现一个监听器用于监听Broker端给我们返回的确认请求:RabbitTemplate.ConfirmCallback
  • publisher-return:保证消息对Broker端是可达的,如果出现路由键不可达的情况,则使用监听器对不可达的消息进行后续的处理,保证消息的路由成功:RabbitTemplate.ReturnCallback

spring.rabbitmq.template.mandatory=true 保证监听有效

生产端配置

添加SpringBoot工程

maven配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.18.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learn</groupId>
	<artifactId>rabbitmq-springboot-producer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rabbitmq-springboot-producer</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

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

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

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

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

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件配置:

spring.rabbitmq.addresses=192.168.222.101:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000

spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true

logging.level.com.learn.springboot=debug

新建一个发送消息类:

package com.learn.springboot.producer;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.UUID;

@Component
@Slf4j
public class Sender {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void send(Object message,Map<String,Object> properties) throws Exception {
        MessageHeaders mhs = new MessageHeaders(properties);
        Message msg = MessageBuilder.createMessage(message,mhs);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        CorrelationData cd = new CorrelationData();
        cd.setId(UUID.randomUUID().toString());//ID+时间戳 需要全局唯一,是实际消息ID
        rabbitTemplate.convertAndSend("exchange-1","springboot.hello",msg,cd);
    }

    final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            log.debug("correlationData: {}",correlationData);
            log.debug("ack:{}",ack);
            if (!ack) {
                log.error("异常处理");
            }
        }
    };

    final RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
        @Override
        public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode,
                                    String replyText, String exchange, String routingKey) {
            log.debug("return exchange:{},routingKey:{},replyCode:{},replyText:{}",exchange,routingKey,replyCode,replyText);

        }
    };

}

编写测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqSpringbootProducerApplicationTests {

	@Autowired
	private Sender sender;

	@Test
	public void testSender() throws Exception {
		Map<String,Object> properties = new HashMap<>();
		properties.put("number","123456");
		properties.put("send_time", LocalDateTime.now());
		sender.send("Hello Spring boot",properties);
	}
}

慢着,还需要新建交换机和队列

在这里插入图片描述

然后启动,可以看到如下打印:

2018-12-13 20:35:30.440 DEBUG 236 --- [68.222.101:5672] com.learn.springboot.producer.Sender     : correlationData: CorrelationData [id=5ce42ec1-0e36-44bc-83a2-e56eab37bc26]
2018-12-13 20:35:30.441 DEBUG 236 --- [68.222.101:5672] com.learn.springboot.producer.Sender     : ack:true

如果将send()函数里的路由改成路由不到的路由,再进行发送

        rabbitTemplate.convertAndSend("exchange-1","spring.hello",msg,cd);

此时日志如下:

2018-12-13 20:41:29.082 DEBUG 6628 --- [68.222.101:5672] com.learn.springboot.producer.Sender     : return exchange:exchange-1,routingKey:spring.hello,replyCode:312,replyText:NO_ROUTE
2018-12-13 20:41:29.083 DEBUG 6628 --- [68.222.101:5672] com.learn.springboot.producer.Sender     : correlationData: CorrelationData [id=7f191e8a-4122-4069-8c61-28cac0b77566]
2018-12-13 20:41:29.083 DEBUG 6628 --- [68.222.101:5672] com.learn.springboot.producer.Sender     : ack:true

消费端配置

消费端核心配置:

spring.rabbitmq.listener.simple.acknowledge-mode=manual #手动签收
spring.rabbitmq.listener.simple.concurrency=1  #设置消费端监听个数
spring.rabbitmq.listener.simple.max-concurrency=5 #设置最大监听个数

设置消费端监听个数用于控制消费端的并发量

@RabbitListener注解的使用

  • 消费端监听该注解,非常好用
  • 是一个组合注解,里面可以配置队列绑定,队列,交换机信息

maven配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.18.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learn</groupId>
	<artifactId>rabbitmq-springboot-consumer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rabbitmq-springboot-consumer</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

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

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

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件配置:

spring.rabbitmq.addresses=192.168.222.101:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000

logging.level.com.learn.springboot=debug

spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=1
spring.rabbitmq.listener.simple.max-concurrency=5

新建消费类:

@Component
@Slf4j
public class Receiver {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "queue-1",durable = "true"),
            exchange = @Exchange(
                    value = "exchange-1",
                    durable = "true",
                    type = "topic",
                    ignoreDeclarationExceptions = "true"),
            key = "springboot.*")
    )
    @RabbitHandler
    public void onMessage(Message message, Channel channel) throws Exception {
        log.debug("消费 payload:{}",message.getPayload());
        Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        //手动ACK
        channel.basicAck(deliveryTag,false);
    }
}

因为,上个生产端生成的消息还没有消费,我们直接启动这个应用,可以看到如下信息:

2018-12-13 21:03:39.198 DEBUG 3400 --- [cTaskExecutor-1] com.learn.springboot.consumer.Receiver   : 消费 payload:Hello Spring boot

上面的方式是通过写死代码的,不太优雅也不好修改,下面介绍基于配置的方式:

    /**
     * 基于配置的方式
     * @param order
     * @param channel
     * @param headers
     * @throws Exception
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "${order.queue.name}",durable = "${order.queue.durable}"),
            exchange = @Exchange(
                    value = "${order.exchange.name}",
                    durable = "${order.exchange.durable}",
                    type = "${order.exchange.type}",
                    ignoreDeclarationExceptions = "${order.exchange.ignoreDeclarationExceptions}"),
            key = "${order.key}")
    )
    @RabbitHandler
    public void onMessage(@Payload Order order,
                          Channel channel,
                          @Headers Map<String,Object> headers) throws Exception {
        //把Message拆分为Payload和Headers两个对象

        log.debug("消费 order:{}",order);
        Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
        //手动ACK
        channel.basicAck(deliveryTag,false);
    }

新增的配置文件如下:

order.queue.name=queue-2
order.queue.durable=true
order.exchange.name=exchange-2
order.exchange.durable=true
order.exchange.type=topic
order.exchange.ignoreDeclarationExceptions=true
order.key=springboot.*

RabbitMQ整合Spring Cloud (Stream)

架构图:

在这里插入图片描述

inputs:消息接收端
outputs:消息发送端
Application Core:核心应用
Binder:协调者
Middleware:消息中间件(RabbitMQ或Kafka)

在这里插入图片描述

  • @Output:输出注解,用于定义发送消息接口
  • @Input:输入注解,用于定义消息的消费者接口
  • @StreamListener:用于定义监听方法的注解

使用Spring Cloud Stream非常简单,只要使用好这3个注解即可,在实现高性能消息的生产和消费的场景非常合适,但是最大的问题就是不能实现可靠性的投递,无法保证消息的100%可靠性,会存在少量消息丢失的问题。这是为了和Kafka兼容,所以在实际工作中使用它的目的就是针对高性能的消息通信。

消费端

Maven配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.18.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learn</groupId>
	<artifactId>rabbitmq-springcloudstream-consumer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rabbitmq-springcloudstream-consumer</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

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

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
			<version>1.3.4.RELEASE</version>
		</dependency>

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

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

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件:

server.port=8002
server.context-path=/consumer

spring.application.name=consumer
spring.cloud.stream.bindings.input_channel.destination=exchange-3
spring.cloud.stream.bindings.input_channel.group=queue-3
spring.cloud.stream.bindings.input_channel.binder=rabbit_cluster
spring.cloud.stream.bindings.input_channel.consumer.concurrency=1
spring.cloud.stream.rabbit.bindings.input_channel.consumer.requeue-rejected=false
spring.cloud.stream.rabbit.bindings.input_channel.consumer.acknowledge-mode=MANUAL
spring.cloud.stream.rabbit.bindings.input_channel.consumer.recovery-interval=3000
# 持久化订阅
spring.cloud.stream.rabbit.bindings.input_channel.consumer.durable-subscription=true
spring.cloud.stream.rabbit.bindings.input_channel.consumer.max-concurrency=5

spring.cloud.stream.binders.rabbit_cluster.type=rabbit
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.222.101:5672
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/

定义Barista类:

/**
 * 这里的Barista接口是定义来作为后面类的参数,这一接口定义来通道类型和通道名称。
 * 通道名称是作为配置用,通道类型则决定了app会使用这一通道进行发送消息还是从中接收消息。
 */

public interface Barista {
    String INPUT_CHANNEL = "input_channel";

    //注解@Input声明了它是一个输入类型的通道,名字是Barista.INPUT_CHANNEL一致
    @Input(Barista.INPUT_CHANNEL)  
    SubscribableChannel loginput();  
}

消息接收类:

@EnableBinding(Barista.class)
@Service
public class Receiver {
    @StreamListener(Barista.INPUT_CHANNEL)
    public void receive(Message message) throws Exception {
        Channel channel = (com.rabbitmq.client.Channel) message.getHeaders().get(AmqpHeaders.CHANNEL);
        Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
        System.out.println("Input Stream 1 接受数据:" + message);
        System.out.println("消费完毕------------");
        channel.basicAck(deliveryTag, false);
    }

}

生成端

Maven配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.18.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learn</groupId>
	<artifactId>rabbitmq-springcloudstream-producer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rabbitmq-springcloudstream-producer</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

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

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

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
			<version>1.3.4.RELEASE</version>
		</dependency>

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

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

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

配置文件:

server.port=8001
server.servlet.context-path=/producer

spring.application.name=producer
# output_channel为Barista.OUTPUT_CHANNEL的值
spring.cloud.stream.bindings.output_channel.destination=exchange-3
# 这里的destination.group是队列的名称
spring.cloud.stream.bindings.output_channel.group=queue-3
spring.cloud.stream.bindings.output_channel.binder=rabbit_cluster

spring.cloud.stream.binders.rabbit_cluster.type=rabbit
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.222.101:5672
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest
spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/

添加Barista类:

/**
 * 这里的Barista接口是定义来作为后面类的参数,这一接口定义来通道类型和通道名称。
 * 通道名称是作为配置用,通道类型则决定了app会使用这一通道进行发送消息还是从中接收消息。
 *
 * 在MQ与应用服务之间添加的中间件,这样可以修改随意修改MQ
 */
public interface Barista {
    String OUTPUT_CHANNEL = "output_channel";

    @Output(Barista.OUTPUT_CHANNEL)
    MessageChannel logoutput();
}

添加消息发送类:


@EnableBinding(Barista.class)
@Service
public class Sender {
    @Autowired
    private Barista barista;

    public String sendMessage(Object message, Map<String, Object> properties) throws Exception {
        try{
            MessageHeaders mhs = new MessageHeaders(properties);
            Message msg = MessageBuilder.createMessage(message, mhs);
            //发送消息
            boolean sendStatus = barista.logoutput().send(msg);
            System.err.println("--------------sending -------------------");
            System.out.println("发送数据:" + message + ",sendStatus: " + sendStatus);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
        return null;
    }
}

编写测试类,发送消息:

@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitmqSpringcloudstreamProducerApplicationTests {
    @Autowired
    private Sender rabbitmqSender;


    @Test
    public void sendMessageTest1() {
        for(int i = 0; i < 1; i ++){
            try {
                Map<String, Object> properties = new HashMap<String, Object>();
                properties.put("SERIAL_NUMBER", "12345");
                properties.put("BANK_NUMBER", "abc");
                properties.put("PLAT_SEND_TIME", LocalDateTime.now());
                rabbitmqSender.sendMessage("Hello, I am amqp sender num :" + i, properties);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

运行测试,消费端打印:

Input Stream 1 接受数据:GenericMessage [payload=Hello, I am amqp sender num :0, headers={amqp_receivedDeliveryMode=PERSISTENT, amqp_receivedExchange=exchange-3, amqp_deliveryTag=1, BANK_NUMBER=abc, amqp_consumerQueue=exchange-3.queue-3, amqp_channel=Cached Rabbit Channel: AMQChannel(amqp://guest@192.168.222.101:5672/,1), conn: Proxy@3f6bf8aa Shared Rabbit Connection: SimpleConnection@e784320 [delegate=amqp://guest@192.168.222.101:5672/, localPort= 27181], amqp_redelivered=false, SERIAL_NUMBER=12345, amqp_receivedRoutingKey=exchange-3, PLAT_SEND_TIME=2018-12-13T22:13:26.686, id=d37d1004-cd03-83e2-f7cd-b9120695a248, amqp_consumerTag=amq.ctag-qy3Q7ixBW1c6VxmegZdDqQ, contentType=text/plain, timestamp=1544710406775}]
消费完毕------------

项目代码

下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愤怒的可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值