RabbitMQ整合Spring AMQP实践:
RabbitAdmin:
RabbitAdmin类可以很好的操作RabbitMQ,在Spring中直接进行注入即可,RabbitAdmin底层实现就是从Spring容器中获取Exchange,Bingding,RoutingKey以及Queue的@Bean声明,然后使用RabbitTemplate的execute方法执行对应的声明,修改,删除等一系列RabbitMQ基础功能操作。
添加POM文件:
<dependency>
<groupId>com.rabbit</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.bfxy.spring.*"})
public class RabbitMQConfig{
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//注意:autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/*--------------------@Bean注入模式使用-------------------*/
/**
*针对消费者配置
*1. 设置交换机类型
*2. 将队列绑定到交换机
* FanoutExchange:将消息分发到所有的绑定队列,无routingkey的概念
* HeadersExhchange:通过添加属性key-value匹配
* DirectExchange:按照routingkey分发到指定队列
* TopicExchange:多关键字匹配
*/
@Bean
public TopicExchange exchange(){
return new TopicExchange("topic",true,false);
}
@Bean
public Queue queue(){
return new Queue("queue",true);
}
@Bean
public Binding binding(){
return BindingBuilder.bind(queue()).to(exchange()).with("spring.*");
}
@Bean
public TopicExchange exchange(){
return new TopicExchange("topic",true,false);
}
}
启动rabbitMQ服务,编写应用代码:
rabbitmq-server start &
lsof -i:5672
public static void main(){
@Autowired
private RabbitAdmin rabbitAdmin;
public void admin()throws Exception{
//声明交换机
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,false));
rabbitAdmin.declareQueue(new Queue("test.topic.queue",false,false));
rabbitAdmin.declareQueue(new Queue("test.fanout.queue",false,false));
//设置绑定关系
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.#")); //指定路由Key
}
rabbitAdmin.declareBinding(BindingBuilder
.bind(new Queue("test.fanout.queue",false))
.to(new FanoutExchange("test.fanout",false,false))
//队列操作
rabbitAdmin.purgeQueue("队列名",false); // 清空队列
}
}
RabbitTemplate:
消息模板,与SpringAMQP整合时进行发送消息的关键类,该类提供了丰富的发送消息方法,包括可靠性投递消息方法,回调监听消息接口ConfirmCallback,返回值确认接口ReturnCallback等等。同样我们需要进行注入到Spring容器中,然后直接使用。
添加配置文件:
@Configuration
@ComponentScan({"com.bfxy.spring.*"})
public class RabbitMQConfig{
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
/**具体队列信息在上个代码块**/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//注意:autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
}
编写测试类:
public static void main(){
@Autowired
private RabbitTemplate rabbitTemplate;
public void SendMessage() throws Rxception{
//创建消息
MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeahers().put("key","value");
Message message = new Message("Hello Wolrd".getBytes(),messageProperties);
//发送
rabbitTemplate.convertAndSend("topic","spring.*",message,new MessagePostProcessor(){
@Override
public Message postProcessMessage(Message message) throws AmqpException{
System.err.println("-------添加额外配置");
message.getMessageProperties().getHeaders().put("desc","额外修改的信息描述");
return message;
}
});
rabbitTemplate.converAndSend("topic","rabbit.abc","hello world");
}
}
SimpleMessageListenerContainer:
简单消息监听容器:
这个类非常强大,可以对他进行很多设置,对于消费者的配置项,这个类都可以满足。
设置事务特性,事务管理器,事务属性,事务容量(并发),是否开启事务,回滚消息等。
设置消费者数量,最大最小数量,批量消费。
设置消息确认和自动确认模式,是否重回队列,异常捕获handler函数。
设置消费者标签生成策略,是否独占模式,消费者属性等。
设置具体的监听器,消息转换器等。
注意:SimpleMessageListenerContainer可以进行动态设置,比如在运行中的应用可以动态的修改其消费者数量的大小,接收消息的模式等。
@Configuration
@ComponentScan({"com.bfxy.spring.*"})
public class RabbitMQConfig{
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//注意:autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/**具体队列信息在上个代码块**/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
//container可以监听多个队列
container.setQueue(queue1(),queue2(),queue3());
//设置消费者数量
container.setConcirrentConsumers(1);
container.setMaxConcirrentConsumers(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());
}
});
return container;
}
}
SimpleMessageListenerContainer为什么可以动态感知配置变更?
MessageListenerAdapter:
消息监听适配器:
defaultListenerMethod默认监听方法名称:用于设置监听方法名称。
Delegate委托对象:实际真实的委托对象,用于处理消息。
queueOrTagToMenthodName:队列标识与方法名称组成的集合
可以一一进行队列与方法名称的匹配。
队列和方法名称绑定,即指定队列里的消息会被绑定的方法所接收处理。
@Configuration
@ComponentScan({"com.bfxy.spring.*"})
public class RabbitMQConfig{
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses("192.168.11.76:5672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
connectionFactory.setVirtualHost("/");
return connectionFactory;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
//注意:autoStartup必须要设置为true,否则Spring容器不会加载RabbitAdmin类
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
/**具体队列信息在上个代码块**/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
//container可以监听多个队列
container.setQueue(queue1(),queue2(),queue3());
//设置消费者数量
container.setConcirrentConsumers(1);
container.setMaxConcirrentConsumers(5);
//设置重回队列
container.setDefaultRequeueRejected(false);
//设置签收模式(自动签收)
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//创建消费策略
container.setConsumerTagStrategy(new ConsumerTagStrategy(){
@Override
public String createConsumerTag(String queue){
return queue+"-"+UUID.randomUUID().toString();
}
});
//适配器方式:队列名称和方法名称也可以进行一一的匹配
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
Map<String,String> queueOrTagToMethodName = new HashMap<>();
queueOrTagToMethodName.put("queue1","method1");
adapter.setDefaultListenerMethod("consumeMessage");
container.setMessageListener(adapter);
return container;
}
}
/**
* 需和下面的MessageConverter配合使用
*/
public class MessageDelegate{
//默认方法
public void handleMessage(byte[] messageBody){
System.err.printLn("默认方法,消息内容":+new String(messageBody));
}
//自定义方法1
public void consumerMessage(byte[] messageBody){
System.err.printLn("字节数组方法,消息内容":+new String(messageBody));
}
//自定义方法2
public void consumerMessage(byte[] messageBody){
System.err.printLn("字符串方法,消息内容":+messageBody);
}
}
MessageConverter:
消息转换器:
我们进行发送消息的时候,正常情况下消息体分为二进制的数据方式进行传输,如果希望内部帮我们进行转换,或者指定自定义的转换器,就需要用到MessageConverter
重写下面两个方法:
1.toMessage:java对象转换为Message
2.fromMessage:Message对象转换为java对象
自定义常用转换器:MessageConverter,一般来讲都需要实现这个接口
public class TextMessageConverter implements MessageConverter{
@Override
public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException{
return new Message(object.toString().getBytes(),messageProperties);
}
@Override
public Object fromMessage(Message message) throws MessageConversionException{
String contentType = message.getMessageProperties().getContentType();
//消息包含Text的时候才能转换
if(null != contentType && contentType.contains("text")){
return new String(message.getBody());
}
return message.getBody();
}
}
Json转换器:Jackson2JsonMessageConverter:可以进行java对象的转换功能(支持java对象转换):
DefaultJackson2JavaTypeMapper映射器:可以进行java对象的映射关系:
config:
MessageListenerAdapeter adapter = new MessageListenerAdapter(new MesssageDelegate());
adapter.setDefaultListenerMethod("consumerMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
/*add java*/
DefaultJacksonvilleJavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
jackson2JsonMessageConverter.setJavaTypeMapper(javaTypeMapper);
/*finish*/
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
/*支持java对象多映射转换*/
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDafaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
Map<String,Class<?>> idClassMapping = new HashMap<String,Class<?>>();
idClassMapping.put("order",com.bfxy.spring.entity.Order.class);
idClassMapping.put("packaged",com.bfxy.spring.entity.Packaged.class);
javaTypeMapper.setIdClassMapping(idClassMapping);
jackson2JasonMessageConverter.setJavaTypeMapper(javaTypeMapper);
adapter.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter);
MessageDelegate:
public class MessageDelegate{
public void handleMessage(byte[] messageBody){
System.err.printLn("map方法,消息内容":+messageBody);
}
public void consumerMessage(Order order){
System.err.printLn("order对象,消息内容":+order.getId()+order.getName()+order.getContent());
}
public void consumerMessage(Packaged pack){
System.err.printLn("order对象,消息内容":+pack.getId()+pack.getName()+pack.getContent());
}
}
测试类:
public void sendMappingMessage() throws Exception{
ObjectMapper mapper = new ObjectMapper();
Order order = new Order();
order.setId("001");
oder.setName("订单消息");
order.setContent("订单描述消息");
String json = mapper.writeValueAsString(order);
System.err.println(json);
MessageProperties messageProperties = new MessageProperties();
//注意一定要修改contentType为application/json
messageProperties.setContentType("application/json");
messageProperties.getHeaders().puy("__TypeId__","order");
Message message = new Message(json.getBytes(),messageProperties);
rabbitTemplate.send("topic","spring.order",message);
Packaged pack = new Packaged();
pack .setId("001");
pack .setName("包裹消息");
pack .setDescription("包裹描述消息");
String json1 = mapper.writeValueAsString(order);
System.err.println(json1);
MessageProperties messageProperties1 = new MessageProperties();
//注意一定要修改contentType为application/json
messageProperties1.setContentType("application/json");
messageProperties1.getHeaders().puy("__TypeId__","packaged");
Message message1 = new Message(json1.getBytes(),messageProperties1);
rabbitTemplate.send("topic","spring.order",message1);
}
自定义二进制转换器:比如图片类型,PDF,PPT,流媒体
config:
/**
* ext convert
*/
MessageListenerAdapter adapter = new MessageListenerAdapter();
adapter.setDafaultListenerMethod("consumerMessage");
//全局转换器:
ContentTypeDelegatingMessageConverter convert = new ContentTypeDelegatingMessageConverter();
TextMessageConverter textConverter = new TextMessageConverter();
convert.addDelegate("text",textConverter);
convert.addDelegate("html/text",textConverter);
convert.addDelegate("xml/text",textConverter);
convert.addDelegate("text/plain",textConverter);
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.setMessgeConverter(convert);
container.setMessageListener(adapter);
return container;
converter:
public class ImageMessageConverter implements MessageConverter{
@Override
public Message toMessage(Obejct object,MessageProperties messageProperties) throws MessageConverterException{
throw new MessageConverterException("Error!");
}
@Override
public Object fromMessage(Message message)throws MessageConverterException{
sout("-------Image MessageConverter");
Obejct _extName = message.getMessageProperties().getHeaders().get("extName");
String extName = _extName ==null ? "png" :_extName.toString();
byte[] body = message.getBody();
String fileName = UUID.randomUUID().toString();
String path = "c:/test/"+fileName+"."+extName;
File f = new File(path);
try{
Files.copy(new ByteArrayInputStream(body),f.toPath());
}catch (IOException e){
e.printStackTrace();
}
return f;
}
}
public class PDFMessageConverter implements MessageConverter{
@Override
public Message toMessage(Obejct object,MessageProperties messageProperties) throws MessageConverterException{
throw new MessageConverterException("Error!");
}
@Override
public Object fromMessage(Message message)throws MessageConverterException{
sout("-------PDF MessageConverter");
byte[] body = message.getBody();
String fileName = UUID.randomUUID().toString();
String path = "c:/test/"+fileName+"."+.pdf;
File f = new File(path);
try{
Files.copy(new ByteArrayInputStream(body),f.toPath());
}catch (IOException e){
e.printStackTrace();
}
return f;
}
}
测试类:
public static void main(){
public void sendExtConverterMessage() throws Exception{
//image
byte[] body = new Files.readAllBytes(Paths.get("c:/text","picture.png"));
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("image/png");
messageProperties.getHeaders().put("extName","png");
Message message = new Message(body,messageProperties);
rabbitTemplate.send("","image_queue",message);
//pdf
byte[] body = new Files.readAllBytes(Paths.get("c:/text","picture.png"));
MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType("applicatoin/pdf");
Message message = new Message(body,messageProperties);
rabbitTemplate.send("","image_queue",message);
}
}