1)、RabbitAdmin
配置类:
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.hand.rabbitmq")
public class RabbitMqConfig {
//
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.126.151:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
//ConnectionFactory形参名字和connectionFactory()方法名保持一致
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringbootRabbitmqApplication.class)
public class RabbitmqApplicationTests {
@Autowired
private RabbitAdmin rabbitAdmin;
@Test
public void testAdmin() {
//创建交换机
rabbitAdmin.declareExchange(new DirectExchange("test.direct", false, false));
rabbitAdmin.declareExchange(new TopicExchange("test.topic", false, false));
rabbitAdmin.declareExchange(new FanoutExchange("test.fanout", false, false));
//创建队列
rabbitAdmin.declareQueue(new Queue("test.direct.queue", false));
rabbitAdmin.declareQueue(new Queue("test.topic.queue", false));
rabbitAdmin.declareQueue(new Queue("test.fanout.queue", false));
//创建绑定关系 Binding构造函数的参数 队列名称、绑定类型、交换机名称、绑定键
rabbitAdmin.declareBinding(new Binding("test.direct.queue",
Binding.DestinationType.QUEUE,
"test.direct",
"direct",
new HashMap<>()));
rabbitAdmin.declareBinding(
BindingBuilder.
bind(new Queue("test.topic.queue", false))
.to(new TopicExchange("test.topic", false, false))
.with("user.#"));
rabbitAdmin.declareBinding(
BindingBuilder.
bind(new Queue("test.fanout.queue", false))
.to(new FanoutExchange("test.fanout", false, false)));
//清空队列数据
rabbitAdmin.purgeQueue("test.topic.queue", false);
}
}
RabbitAdmin源码分析:
@ManagedResource(description = "Admin Tasks")
public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, ApplicationEventPublisherAware,
BeanNameAware, InitializingBean {
RabbitAdmin实现了InitializingBean接口,在Bean加载之后做一些设置
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
@ManagedResource(description = "Admin Tasks")
public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, ApplicationEventPublisherAware,
BeanNameAware, InitializingBean {
@Override
public void afterPropertiesSet() {
synchronized (this.lifecycleMonitor) {
//如果是running或者autoStartup为false的话就直接return
if (this.running || !this.autoStartup) {
return;
}
if (this.retryTemplate == null && !this.retryDisabled) {
this.retryTemplate = new RetryTemplate();
this.retryTemplate.setRetryPolicy(new SimpleRetryPolicy(DECLARE_MAX_ATTEMPTS));
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(DECLARE_INITIAL_RETRY_INTERVAL);
backOffPolicy.setMultiplier(DECLARE_RETRY_MULTIPLIER);
backOffPolicy.setMaxInterval(DECLARE_MAX_RETRY_INTERVAL);
this.retryTemplate.setBackOffPolicy(backOffPolicy);
}
if (this.connectionFactory instanceof CachingConnectionFactory &&
((CachingConnectionFactory) this.connectionFactory).getCacheMode() == CacheMode.CONNECTION) {
this.logger.warn("RabbitAdmin auto declaration is not supported with CacheMode.CONNECTION");
return;
}
// Prevent stack overflow...
final AtomicBoolean initializing = new AtomicBoolean(false);
this.connectionFactory.addConnectionListener(connection -> {
if (!initializing.compareAndSet(false, true)) {
// If we are already initializing, we don't need to do it again...
return;
}
try {
if (this.retryTemplate != null) {
this.retryTemplate.execute(c -> {
//初始化
initialize();
return null;
});
}
else {
initialize();
}
}
finally {
initializing.compareAndSet(true, false);
}
});
this.running = true;
}
}
@Override // NOSONAR complexity
public void initialize() {
if (this.applicationContext == null) {
this.logger.debug("no ApplicationContext has been set, cannot auto-declare Exchanges, Queues, and Bindings");
return;
}
this.logger.debug("Initializing declarations");
//声明了Exchange、Queue、Binding三个集合
Collection<Exchange> contextExchanges = new LinkedList<Exchange>(
this.applicationContext.getBeansOfType(Exchange.class).values());
Collection<Queue> contextQueues = new LinkedList<Queue>(
this.applicationContext.getBeansOfType(Queue.class).values());
Collection<Binding> contextBindings = new LinkedList<Binding>(
this.applicationContext.getBeansOfType(Binding.class).values());
processLegacyCollections(contextExchanges, contextQueues, contextBindings);
//将Bean类型是Exchange、Queue、Binding的添加到集合中
processDeclarables(contextExchanges, contextQueues, contextBindings);
final Collection<Exchange> exchanges = filterDeclarables(contextExchanges);
final Collection<Queue> queues = filterDeclarables(contextQueues);
final Collection<Binding> bindings = filterDeclarables(contextBindings);
//将Exchange、Queue集合循环拼接成RabbitMQ能够识别的方式
for (Exchange exchange : exchanges) {
if ((!exchange.isDurable() || exchange.isAutoDelete()) && this.logger.isInfoEnabled()) {
this.logger.info("Auto-declaring a non-durable or auto-delete Exchange ("
+ exchange.getName()
+ ") durable:" + exchange.isDurable() + ", auto-delete:" + exchange.isAutoDelete() + ". "
+ "It will be deleted by the broker if it shuts down, and can be redeclared by closing and "
+ "reopening the connection.");
}
}
for (Queue queue : queues) {
if ((!queue.isDurable() || queue.isAutoDelete() || queue.isExclusive()) && this.logger.isInfoEnabled()) {
this.logger.info("Auto-declaring a non-durable, auto-delete, or exclusive Queue ("
+ queue.getName()
+ ") durable:" + queue.isDurable() + ", auto-delete:" + queue.isAutoDelete() + ", exclusive:"
+ queue.isExclusive() + ". "
+ "It will be redeclared if the broker stops and is restarted while the connection factory is "
+ "alive, but all messages will be lost.");
}
}
if (exchanges.size() == 0 && queues.size() == 0 && bindings.size() == 0) {
this.logger.debug("Nothing to declare");
return;
}
//调用rabbitTemplate.execute在RabbitMQ上创建Exchange、Queue、Binding
this.rabbitTemplate.execute(channel -> {
declareExchanges(channel, exchanges.toArray(new Exchange[exchanges.size()]));
declareQueues(channel, queues.toArray(new Queue[queues.size()]));
declareBindings(channel, bindings.toArray(new Binding[bindings.size()]));
return null;
});
this.logger.debug("Declarations finished");
}
private void processDeclarables(Collection<Exchange> contextExchanges, Collection<Queue> contextQueues,
Collection<Binding> contextBindings) {
Collection<Declarables> declarables = this.applicationContext.getBeansOfType(Declarables.class, false, true)
.values();
declarables.forEach(d -> {
d.getDeclarables().forEach(declarable -> {
if (declarable instanceof Exchange) {
contextExchanges.add((Exchange) declarable);
}
else if (declarable instanceof Queue) {
contextQueues.add((Queue) declarable);
}
else if (declarable instanceof Binding) {
contextBindings.add((Binding) declarable);
}
});
});
}
2)、RabbitTemplate(消息模板)
配置类:
//ConnectionFactory形参名字和connectionFactory()方法名保持一致
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
测试方法:
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendMessage() {
//创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put("desc", "信息描述");
messageProperties.getHeaders().put("type", "自定义消息类型");
Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
//发送消息
rabbitTemplate.convertAndSend("topic001", "spring.amqp", message, new MessagePostProcessor() {
//在执行消息转换后添加/修改标题或属性然后在进行发送
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述");
message.getMessageProperties().getHeaders().put("attr", "额外新添加的属性");
return message;
}
});
}
rabbitTemplate其他的方法:
@Test
public void testSendMessage2() {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("text/plain");
Message message = new Message("mq消息1".getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.amqp", message);
rabbitTemplate.convertAndSend("topic001", "spring.amqp", "mq消息2");
}
3)、SimpleMessageListenerContainer(简单消息监听容器)
注意:SimpleMessageListenerContainer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小、接收消息的模式等
配置类:
//ConnectionFactory形参名字和connectionFactory()方法名保持一致
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
/**
* 设置消息接收确认模式
* - AcknowledgeMode.NONE:不确认
* - AcknowledgeMode.MANUAL:自动确认
* - AcknowledgeMode.AUTO:手动确认
*/
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//设置消息监听
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;
}
4)、MessageListenerAdapter(消息监听适配器)
1)适配器方式一:自定义方法名称和参数类型
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
/**
* 设置消息接收确认模式
* - AcknowledgeMode.NONE:不确认
* - AcknowledgeMode.MANUAL:自动确认
* - AcknowledgeMode.AUTO:手动确认
*/
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//适配器方式
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
//MessageListenerAdapter自定义方法名
adapter.setDefaultListenerMethod("consumeMessage");
//添加转换器:从字节数组转换为String,到MessageDelegate的时候调用参数为String类型的方法
adapter.setMessageConverter(new TextMessageConverter());
container.setMessageListener(adapter);
return container;
}
public class MessageDelegate {
public void handleMessage(byte[] messageBody) {
System.out.println("默认方法, 消息内容:" + new String(messageBody));
}
public void consumeMessage(byte[] messageBody) {
System.out.println("字节数组方法, 消息内容:" + new String(messageBody));
}
public void consumeMessage(String messageBody) {
System.out.println("字符串方法, 消息内容:" + messageBody);
}
}
public class TextMessageConverter implements MessageConverter {
//Java对象转换成Message对象的方式
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
return new Message(object.toString().getBytes(), messageProperties);
}
//Message对象转换成Java对象的方式
@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 null;
}
}
2)适配器方式二:队列的名称和方法名称也可以进行一一的匹配
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//适配器方式:队列的名称和方法名称也可以进行一一的匹配
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
Map<String, String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queue001","method1");
queueOrTagToMethodName.put("queue002","method2");
adapter.setQueueOrTagToMethodName(queueOrTagToMethodName);
adapter.setMessageConverter(new TextMessageConverter());
container.setMessageListener(adapter);
return container;
}
MessageListenerAdapter源码分析:
public class MessageListenerAdapter extends AbstractAdaptableMessageListener {
//将队列名和方法名进行匹配,将指定队列适配到指定方法里
private final Map<String, String> queueOrTagToMethodName = new HashMap<String, String>();
public static final String ORIGINAL_DEFAULT_LISTENER_METHOD = "handleMessage";
//new MessageListenerAdapter(new MessageDelegate())时传入的委托对象,用于处理消息
private Object delegate;
//默认的监听方法的名字为handleMessage,如果要自定义MessageListenerAdapter方法名默认为handleMessage
private String defaultListenerMethod = ORIGINAL_DEFAULT_LISTENER_METHOD;
5)、MessageConverter(消息转换器)
在进行发送消息的时候,正常情况下消息体为二进制的数据方式进行传输,如果希望内部帮我们进行转换,或者指定自定义的转换器,就需要用到MessageConverter
自定义消息转换器,需要实现MessageConverter接口,重写toMessage(Java对象转换成Message对象的方式)和fromMessage(Message对象转换成Java对象的方式)方法
1)Jackson2JsonMessageConverter
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//支持json格式的转换器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
}
对应MessageDelegate中的方法:
public void consumeMessage(Map messageBody) {
System.out.println("map方法, 消息内容:" + messageBody);
}
测试方法:
@Test
public void testSendJsonMessage() throws Exception {
Order order = new Order("001","消息订单","描述信息");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(order);
MessageProperties messageProperties = new MessageProperties();
//这里一定要修改contentType为application/json
messageProperties.setContentType("application/json");
Message message = new Message(json.getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.order", message);
}
2)Jackson2JsonMessageConverter & DefaultJackson2JavaTypeMapper支持Java对象的转换
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//Jackson2JsonMessageConverter & DefaultJackson2JavaTypeMapper支持Java对象的转换
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
//否则会抛出异常The class '...' is not in the trusted packages: [java.util, java.lang]. If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).
//默认只支持java.util和java.lang包下的类
javaTypeMapper.setTrustedPackages("*");
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
}
对应MessageDelegate中的方法:
public void consumeMessage(Order order) {
System.out.println("order对象, 消息内容, id: " + order.getId() +
", name: " + order.getName() +
", content: " + order.getContent());
}
测试方法:
@Test
public void testSendJavaMessage() throws Exception {
Order order = new Order("001","消息订单","描述信息");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(order);
MessageProperties messageProperties = new MessageProperties();
//这里一定要修改contentType为application/json
messageProperties.setContentType("application/json");
//__TypeId__指定类的全路径
messageProperties.getHeaders().put("__TypeId__", "com.hand.rabbitmq.domain.Order");
Message message = new Message(json.getBytes(), messageProperties);
rabbitTemplate.send("topic001", "spring.order", message);
}
3)DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter支持java对象多映射转换
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
//DefaultJackson2JavaTypeMapper & Jackson2JsonMessageConverter支持java对象多映射转换
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
Map<String, Class<?>> idClassMapping = new HashMap<>();
idClassMapping.put("order", Order.class);
idClassMapping.put("packaged", Packaged.class);
javaTypeMapper.setIdClassMapping(idClassMapping);
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
return container;
}
对应MessageDelegate中的方法:
public void consumeMessage(Order order) {
System.out.println("order对象, 消息内容, id: " + order.getId() +
", name: " + order.getName() +
", content: " + order.getContent());
}
public void consumeMessage(Packaged pack) {
System.out.println("package对象, 消息内容, id: " + pack.getId() +
", name: " + pack.getName() +
", content: " + pack.getDescription());
}
测试方法:
@Test
public void testSendMappingMessage() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = new Order("001","消息订单","描述信息");
String json1 = mapper.writeValueAsString(order);
MessageProperties messageProperties1 = new MessageProperties();
//这里注意一定要修改contentType为 application/json
messageProperties1.setContentType("application/json");
messageProperties1.getHeaders().put("__TypeId__", "order");
Message message1 = new Message(json1.getBytes(), messageProperties1);
rabbitTemplate.send("topic001", "spring.order", message1);
Packaged pack = new Packaged("002","包裹消息","包裹描述信息");
String json2 = mapper.writeValueAsString(pack);
MessageProperties messageProperties2 = new MessageProperties();
//这里一定要修改contentType为application/json
messageProperties2.setContentType("application/json");
messageProperties2.getHeaders().put("__TypeId__", "packaged");
Message message2 = new Message(json2.getBytes(), messageProperties2);
rabbitTemplate.send("topic001", "spring.pack", message2);
}
4)ContentTypeDelegatingMessageConverter全局转换器
配置类:
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setQueues(queue001(), queue002(), queue003(), queue_image(), queue_pdf());
//当前消费者数量
container.setConcurrentConsumers(1);
//最大消费者数量
container.setMaxConcurrentConsumers(5);
//设置拒绝消息时的默认行为 true:则重新发送消息 false:则不会重新发送消息
container.setDefaultRequeueRejected(false);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//消费端的标签策略
container.setConsumerTagStrategy(new ConsumerTagStrategy() {
@Override
public String createConsumerTag(String queue) {
return queue + "_" + UUID.randomUUID();
}
});
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");
//全局的转换器
ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
TextMessageConverter textConvert = new TextMessageConverter();
convert.addDelegate("text", textConvert);
convert.addDelegate("html/text", textConvert);
convert.addDelegate("xml/text", textConvert);
convert.addDelegate("text/plain", textConvert);
Jackson2JsonMessageConverter jsonConvert = new Jackson2JsonMessageConverter();
convert.addDelegate("json", jsonConvert);
convert.addDelegate("application/json", jsonConvert);
ImageMessageConverter imageConverter = new ImageMessageConverter();
convert.addDelegate("image/png", imageConverter);
convert.addDelegate("image", imageConverter);
PDFMessageConverter pdfConverter = new PDFMessageConverter();
convert.addDelegate("application/pdf", pdfConverter);
adapter.setMessageConverter(convert);
container.setMessageListener(adapter);
return container;
}
1)生产端整合
核心配置:
spring.rabbitmq.addresses=192.168.126.151:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000
#开启Publisher Confirm机制
spring.rabbitmq.publisher-confirms=true
#开启Publisher Return机制
spring.rabbitmq.publisher-returns=true
#启用强制消息,设置为false收不到Publisher Return机制返回的消息
spring.rabbitmq.template.mandatory=false
import com.hand.rabbitmq.domain.Order;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
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
public class RabbitSender {
@Autowired
private RabbitTemplate rabbitTemplate;
final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("correlationData:" + correlationData);
System.out.println("ack:" + ack);
if (!ack) {
System.out.println("异常处理");
}
}
};
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) {
System.out.println("return exchange:" + exchange + ",routingKey:" + routingKey + ",replyCode:" + replyCode + ",replyText:" + replyText);
}
};
public void send(Object message, Map<String, Object> properties) {
MessageHeaders mhs = new MessageHeaders(properties);
Message msg = MessageBuilder.createMessage(message, mhs);
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//最好是id +时间戳 全局唯一
CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("exchange-1", "springboot.hello", msg, cd);
}
//Order需要实现序列化接口
public void sendOrder(Order order) {
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(returnCallback);
//最好是id +时间戳 全局唯一
CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("exchange-2", "springboot.hello", order, cd);
}
}
2)消费端整合
核心配置:
#确认模式为手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=1
spring.rabbitmq.listener.simple.max-concurrency=5
配置信息:
spring.rabbitmq.listener.order.queue.name=queue-2
spring.rabbitmq.listener.order.queue.durable=true
spring.rabbitmq.listener.order.exchange.name=exchange-2
spring.rabbitmq.listener.order.exchange.durable=true
spring.rabbitmq.listener.order.exchange.type=topic
spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.order.key=springboot.*
import com.hand.rabbitmq.domain.Order;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class RabbitReceiver {
//如果没有可以自动创建交换机、队列、绑定、路由
@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 {
System.out.println("消费端:" + message.getPayload());
Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
//手工ACK
channel.basicAck(deliveryTag, false);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "${spring.rabbitmq.listener.order.queue.name}",
durable = "${spring.rabbitmq.listener.order.queue.durable}"),
exchange = @Exchange(value = "${spring.rabbitmq.listener.order.exchange.name}",
durable = "${spring.rabbitmq.listener.order.exchange.durable}",
type = "${spring.rabbitmq.listener.order.exchange.type}",
ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
key = "${spring.rabbitmq.listener.order.key}"
))
@RabbitHandler
//Order需要实现序列化接口
public void onMessage(@Payload Order order, @Headers Map<String, Object> headers, Channel channel) throws Exception {
System.out.println("消费端:" + order);
Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
//手工ACK
channel.basicAck(deliveryTag, false);
}
}
Spring Cloud Stream核心架构图:
Spring Cloud Stream通过定义绑定器Binder作为中间层,完美地实现了应用程序与消息中间件之间的隔离。通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件的实现。当需要升级消息中间件,或是更换其他消息中间件产品时,只需要更换对应的Binder绑定器而不需要修改任何的应用逻辑
上图中黄色的为RabbitMQ的部分,绿色的部分为Spring Cloud Stream在生产者和消费者添加了一层中间件
使用Spring Cloud Stream不能实现可靠性的投递,会存在少量消息丢失的问题(为了兼顾Kafka)
添加依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-stream-rabbitartifactId>
<version>2.1.0.RELEASEversion>
dependency>
Spring Cloud Stream提供了Sink、Source和Processor三个默认实现消息通道的接口
public interface Sink {
String INPUT = "input";
@Input(Sink.INPUT)
SubscribableChannel input();
}
public interface Source {
String OUTPUT = "output";
@Output(Source.OUTPUT)
MessageChannel output();
}
public interface Processor extends Source, Sink {
}
1)生产端整合
server.port=8001
spring.application.name=producer
spring.cloud.stream.bindings.output_channel.destination=exchange-3
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.126.151: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=/
public interface MqMessageSource {
String OUTPUT_CHANNEL = "output_channel";
@Output(MqMessageSource.OUTPUT_CHANNEL)
MessageChannel output();
}
@EnableBinding(MqMessageSource.class)
@Service
public class RabbitmqSender {
@Autowired
private MqMessageSource mqMessageSource;
/**
* 发送消息
*/
public String sendMessage(Object message, Map<String, Object> properties) {
try {
MessageHeaders mhs = new MessageHeaders(properties);
Message msg = MessageBuilder.createMessage(message, mhs);
boolean sendStatus = mqMessageSource.output().send(msg);
System.out.println("发送数据:" + message + ",sendStatus: " + sendStatus);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
return null;
}
}
2)消费端整合
server.port=8002
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.126.151: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=/
public interface MqMessageSink {
String INPUT_CHANNEL = "input_channel";
@Input(MqMessageSink.INPUT_CHANNEL)
SubscribableChannel input();
}
@EnableBinding(MqMessageSink.class)
@Service
public class RabbitmqReceiver {
@StreamListener(MqMessageSink.INPUT_CHANNEL)
public void receiver(Message message) throws Exception {
Channel channel = (Channel) message.getHeaders().get(AmqpHeaders.CHANNEL);
Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
System.out.println("Input Stream 1 接受数据:" + message);
channel.basicAck(deliveryTag, false);
}
}