RabbitMQ小结

Rabbit常用命令

关闭应用
rabbitmqctl stop_app
启动应用
rabbitmqctl start_app
节点状态
rabbitmqctl status
添加用户
rabbitmqctl add_user username password
删除用户
rabbitmqctl delete_user username
列出所有用户
rabbitmqctl list_users
列出用户权限
rabbitmqctl list_user_permissions username 
设置用户权限
rabbitmqctl set_permissions -p vhostpath username ".*" ".*" ".*"
清除用户权限
rabbitmqctl clear_permissions -p vhostpath username
修改密码
rabbitmqctl change_password username newpassword

创建虚拟主机
rabbitmqctl add_vhost vhostpath
列出虚拟主机
rabbitmqctl list_vhosts
列出虚拟主机上权限
rabbitmqctl list_permissions -p vhostpath
删除虚拟主机
rabbitmqctl delete_vhost vhostpath

产看所有队列
rabbitmqctl list_queues
清除队列里信息
rabbitmqctl -p vhostpath purge_queue blue

移除所有数据(rabbitmqctl stop_app之后)
rabbitmqctl reset
组成集群命令
rabbitmqctl join_cluster<clusternode>[--ram]
查看集群状态
rabbitmqctl cluster_status
修改集群节点存储形式
rabbitmqctl change_cluster_node_type disc|ram
摘除节点
rabbitmqctl forget_cluster_node[--offline] 
修改节点名称
rabbitmqctl rename_cluster_node oldnode1 newnode1[oldnode2] [newnode2] 

RabbitMQ组成

Broker(消息代理)
    消息服务器实体。
Exchange(交换机)
    用来发送消息的AMQP实体,它指定消息按什么路由规则,路由到哪个队列。
Queue(消息队列)
    每个消息都会被投入到一个或多个队列。
Binding(绑定)
    它的作用就是把交换机(Exchange)和队列(Queue)按照路由规则绑定起来。
Routing Key(路由关键字)
    路交换机(Exchange)根据这个关键字进行消息投递。
vhost(虚拟主机)
    虚拟主机,一个消息代理(Broker)里可以开设多个虚拟主机(vhost),用作不同用户的权限分离。
Connection(连接)
    AMQP连接通常是长连接,Producer和Consumer都是通过TCP连接到RabbitMQ Server的。
Channel(通道)
    AMQP通过通道(channels)来处理多连接,可以把通道理解成共享一个TCP连接的多个轻量化连接。

channel.exchangeDeclare(很多重载方法)

Exchange.DeclareOk exchangeDeclare(String exchange,String type,boolean durable,boolean autoDelete,boolean internal,Map<String, Object> arguments)throws IOException;

exchange: 
交换器名称
type: 
交换器类型 DIRECT("direct"), FANOUT("fanout"), TOPIC("topic"), HEADERS("headers");
durable: 
是否持久化,设为true表示持久化,将交换器存盘,服务器重启不会丢信息.
autoDelete:
是否自动删除,设为TRUE则自动删除,自删除前提是至少有一个队列或者交换器与这交换器绑定,
之后所有与这个交换器绑定的队列或者交换器都解绑,一般设为fase.
internal:
是否内置,设为true,则为内置的交换器,客户端无法直接发送消息到这个交换器中,只能通过交换器路由的方式.
arguments:
其它一些结构化参数比如:alternate-exchange

Java代码链接RabbitMQ

public static Channel getConnel()throws Exception{
      //1 创建ConnectionFactory
	  ConnectionFactory connectionFactory = new ConnectionFactory() ;
	  connectionFactory.setHost("127.0.0.1");
	  connectionFactory.setPort(5672);
	  connectionFactory.setVirtualHost("/");
	  
	  connectionFactory.setAutomaticRecoveryEnabled(true);
	  connectionFactory.setNetworkRecoveryInterval(3000);
	  //2 创建Connection
	  Connection connection = connectionFactory.newConnection();
	  //3 创建Channel
	  Channel channel = connection.createChannel();
	  return channel;
}

public class MyConsumer extends DefaultConsumer {
	public MyConsumer(Channel channel) {
		super(channel);
	}
	@Override
	public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
		System.err.println("-----------consume message----------");
		System.err.println("consumerTag: " + consumerTag);
		System.err.println("envelope: " + envelope);
		System.err.println("properties: " + properties);
		System.err.println("body: " + new String(body));
	}
}

Exchanges 

接收消息,按特定的策略转发到 Queue 进行存储。类似交换机,将各个消息分发到相应的队列中。根据消息携带的路由键(routing key)将消息投递给对应队列的。

Exchange类型

Direct Exchange:

一对一完全匹配,将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。

public class ProducerDirectExchange {
	public static void main(String[] args) throws Exception {
	    Channel channel = getConnel();
		//声明 
		String exchangeName = "test_direct_exchange";
		String routingKey = "test.direct";
		//发送 
		String msg = "Hello World RabbitMQ  Direct Exchange Message 111 ... ";
		channel.basicPublish(exchangeName, routingKey , null , msg.getBytes());
	}
}
public class ConsumerDirectExchange {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel();
		//声明
		String exchangeName = "test_direct_exchange";
		String exchangeType = "direct";
		String queueName = "test_direct_queue";
		String routingKey = "test.direct";
		//表示声明了一个交换机
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		//表示声明了一个队列
		channel.queueDeclare(queueName, false, false, false, null);
		//建立一个绑定关系:
		channel.queueBind(queueName, exchangeName, routingKey);
        //参数:队列名称、是否自动ACK、Consumer     
		channel.basicConsume(queueName, true, new MyConsumer(channel));	
	}
}

Fanout Exchange:

一对多完全匹配。将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。像网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。

public class ConsumerFanoutExchange {
	public static void main(String[] args) throws Exception {
        Channel channel = getConnel(); 
		//声明
		String exchangeName = "test_fanout_exchange";
		String exchangeType = "fanout";
		String queueName = "test_fanout_queue";
		
		String routingKey = "";	//不设置路由键
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		channel.queueDeclare(queueName, false, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);
		
        //参数:队列名称、是否自动ACK、Consumer     
		channel.basicConsume(queueName, true, new MyConsumer(channel));	
	}
}
public class ProducerFanoutExchange {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel();  
		//声明
		String exchangeName = "test_fanout_exchange";
		//发送
		for(int i = 0; i < 10; i ++) {
			String msg = "Hello World RabbitMQ 4 FANOUT Exchange Message ...";
			channel.basicPublish(exchangeName, "", null , msg.getBytes()); 			
		}
		channel.close();  
        connection.close();  
	}
}

Topic Exchange:

多对多正则匹配。此时队列需要绑定要一个模式上。符号"#"匹配一个或多个词,符号"*"匹配一个词。因此"audit.#"能够匹配到"audit.irs.corporate",但是"audit.*" 只会匹配到"audit.irs"。

public class ConsumerTopicExchange {
	public static void main(String[] args) throws Exception {
        Channel channel = getConnel();
		//声明
		String exchangeName = "test_topic_exchange";
		String exchangeType = "topic";
		String queueName = "test_topic_queue";
		//String routingKey = "user.*";
		String routingKey = "user.*";
		
		// 1 声明交换机 
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		// 2 声明队列
		channel.queueDeclare(queueName, false, false, false, null);
		// 3 建立交换机和队列的绑定关系:
		channel.queueBind(queueName, exchangeName, routingKey);
		
        //参数:队列名称、是否自动ACK、Consumer     
		channel.basicConsume(queueName, true, new MyConsumer(channel));	
	}
}
public class ProducerTopicExchange {
	public static void main(String[] args) throws Exception {
        Channel channel = getConnel();  
		//声明
		String exchangeName = "test_topic_exchange";
		String routingKey1 = "user.save";
		String routingKey2 = "user.update";
		String routingKey3 = "user.delete.abc";
		//发送
		String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
		channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes()); 
		channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes()); 	
		channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes()); 
		channel.close();  
        connection.close();  
	}
}

headers Exchange:

headers交换器允许你匹配AMQP消息的header而非路由键。除此之外,headers交换器和direct交换器完全一致,性能会差很多。
因此它并不太实用,几乎用不到了。

Confirm(确认消息)

生产者投递消息后, Broker收到消息, 会给我们产生一个应答。

public class Consumer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		String exchangeName = "test_confirm_exchange";
		String routingKey = "confirm.#";
		String queueName = "test_confirm_queue";
		
		//声明交换机和队列 然后进行绑定设置, 最后制定路由Key
		channel.exchangeDeclare(exchangeName, "topic", true);
		channel.queueDeclare(queueName, true, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);
		
		//创建消费者;  参数:队列名称、是否自动ACK、Consumer     
		channel.basicConsume(queueName, true, new MyConsumer(channel));	
	}
}
public class Producer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		//指定我们的消息投递模式: 消息的确认模式 
		channel.confirmSelect();
		String exchangeName = "test_confirm_exchange";
		String routingKey = "confirm.save";
		//发送一条消息
		String msg = "Hello RabbitMQ Send confirm message!";
		channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
		//添加一个确认监听
		channel.addConfirmListener(new ConfirmListener() {
            @Override
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				System.err.println("-------ack Success-----------");
			}
			@Override
			public void handleNack(long deliveryTag, boolean multiple) throws IOException {
				System.err.println("-------ack Failed!-----------");
			}
		});
	}
}

ReturnListener

发送消息的时,当前的exchange不存在或路由key路由不到,这时需要监听这种不可达的消息。 mandatory 设为 true 监听器会接收到路由不可达的消息,进行处理;设为false, broker 自动删除该消息。

public class Consumer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		String exchangeName = "test_return_exchange";
		String routingKey = "return.#";
		String queueName = "test_return_queue";
		
		channel.exchangeDeclare(exchangeName, "topic", true, false, null);
		channel.queueDeclare(queueName, true, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);
		
		//创建消费者;  参数:队列名称、是否自动ACK、Consumer     
		channel.basicConsume(queueName, true, new MyConsumer(channel));	
	}
}

public class Producer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		String exchange = "test_return_exchange";
		String routingKey = "return.save";
		String routingKeyError = "abc.save";
		String msg = "Hello RabbitMQ Return Message";
		channel.addReturnListener(new ReturnListener() {
			@Override
			public void handleReturn(int replyCode, String replyText, String exchange,
									 String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
				System.err.println("---------handle  return----------");
				System.err.println("replyCode: " + replyCode);
				System.err.println("replyText: " + replyText);
				System.err.println("exchange: " + exchange);
				System.err.println("routingKey: " + routingKey);
				System.err.println("properties: " + properties);
				System.err.println("body: " + new String(body));
			}
		});
		// mandatory 设为 true 监听器会接收到路由不可达的消息
		channel.basicPublish(exchange, routingKeyError, true, null, msg.getBytes());
	}
}

消费端限流

void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;

prefetchSize:
单条消息大小限制,0代表不限制。
prefetchCount:
一次性消费的消息数量。
global:
true、false 是否将上面设置应用于 channel,限制是 channel 级别的还是 consumer 级别。

步骤

1.关闭自动 ack,将 autoAck 设为 false
channel.basicConsume(queueName, false, consumer);
2.设置具体的限流大小以及数量。
channel.basicQos(0, 15, false);
3.消费者的 handleDelivery 消费方法中手动 ack,并且设置批量处理 ack 回应为 true
channel.basicAck(envelope.getDeliveryTag(), true);

public class MyConsumer extends DefaultConsumer {
	private Channel channel ;
	public MyConsumer(Channel channel) {
		super(channel);
		this.channel = channel;
	}
	@Override
	public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
		System.err.println("-----------consume message----------");
		System.err.println("consumerTag: " + consumerTag);
		System.err.println("envelope: " + envelope);
		System.err.println("properties: " + properties);
		System.err.println("body: " + new String(body));
		//消费者的 handleDelivery 消费方法中手动 ack,并且设置批量处理 ack 回应为 true
		channel.basicAck(envelope.getDeliveryTag(), false);
	}
}

public class Consumer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		
		String exchangeName = "test_qos_exchange";
		String queueName = "test_qos_queue";
		String routingKey = "qos.#";
		
		channel.exchangeDeclare(exchangeName, "topic", true, false, null);
		channel.queueDeclare(queueName, true, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);
		//关闭自动 ack,将 autoAck 设为 false
		channel.basicConsume(queueName, false, new MyConsumer(channel));
		//设置具体的限流大小以及数量。
		channel.basicQos(0, 1, false);
	}
}

public class Producer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		
		String exchange = "test_qos_exchange";
		String routingKey = "qos.save";
		String msg = "Hello RabbitMQ QOS Message";
		
		for(int i =0; i<5; i ++){
			channel.basicPublish(exchange, routingKey, true, null, msg.getBytes());
		}
	}
}

ack 手工签收

public class MyConsumer extends DefaultConsumer {
	private Channel channel;
	public MyConsumer(Channel channel) {
		super(channel);
		this.channel = channel;
	}
	@Override
	public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
		System.err.println("-----------consume message----------");
		System.err.println("body: " + new String(body));
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if((Integer)properties.getHeaders().get("num") == 0) {
			// 拒消费 返给队列
			channel.basicNack(envelope.getDeliveryTag(), false, true);
		} else {
			//消费
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
		
	}
}

public class Consumer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		
		String exchangeName = "test_ack_exchange";
		String queueName = "test_ack_queue";
		String routingKey = "ack.#";
		
		channel.exchangeDeclare(exchangeName, "topic", true, false, null);
		channel.queueDeclare(queueName, true, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);
		
		// 手工签收 必须要关闭 autoAck = false
		channel.basicConsume(queueName, false, new MyConsumer(channel));
	}
}

public class Producer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		String exchange = "test_ack_exchange";
		String routingKey = "ack.save";
		for(int i =0; i<5; i ++){
			Map<String, Object> headers = new HashMap<String, Object>();
			headers.put("num", i);
			AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
					.deliveryMode(2)
					.contentEncoding("UTF-8")
					.headers(headers)
					.build();
			String msg = "Hello RabbitMQ ACK Message " + i;
			channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
		}
	}
}

死信队列处理(无消费者处理)

public class MyConsumer extends DefaultConsumer {
	public MyConsumer(Channel channel) {
		super(channel);
	}
	@Override
	public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
		System.err.println("-----------consume message----------");
		System.err.println("consumerTag: " + consumerTag);
		System.err.println("envelope: " + envelope);
		System.err.println("properties: " + properties);
		System.err.println("body: " + new String(body));
	}
}

public class Consumer {
	public static void main(String[] args) throws Exception {
		Channel channel = getConnel(); 
		// 普通的交换机 和 队列 以及路由
		String exchangeName = "test_dlx_exchange";
		String routingKey = "dlx.#";
		String queueName = "test_dlx_queue";
		channel.exchangeDeclare(exchangeName, "topic", true, false, null);
		Map<String, Object> agruments = new HashMap<String, Object>();
		agruments.put("x-dead-letter-exchange", "dlx.exchange");
		//这个agruments属性,要设置到声明队列上
		channel.queueDeclare(queueName, true, false, false, agruments);
		channel.queueBind(queueName, exchangeName, routingKey);
		//要进行死信队列的声明:
		channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
		channel.queueDeclare("dlx.queue", true, false, false, null);
		channel.queueBind("dlx.queue", "dlx.exchange", "#");
		channel.basicConsume(queueName, true, new MyConsumer(channel));
	}
}
public class Producer {
	public static void main(String[] args) throws Exception {
        Channel channel = getConnel(); 
		String exchange = "test_dlx_exchange";
		String routingKey = "dlx.save";
		String msg = "Hello RabbitMQ DLX Message";
		for(int i =0; i<1; i ++){
			AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
					.deliveryMode(2)
					.contentEncoding("UTF-8")
					.expiration("10000")
					.build();
			channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
		}
	}
}

Spring AMQP

主要对象类
Queue:
对应RabbitMQ中Queue。

AmqpTemplate:
用于向RabbitMQ发送和接收Message的接口。
RabbitTemplate:
AmqpTemplate的实现类。

@RabbitListener:
指定消息接收方,可以配置在类和方法上。

@RabbitHandler:
指定消息接收方,只能配置在方法上,可与@RabbitListener一起使用。

Message:
RabbitMQ消息的封装类。

Exchange:
对RabbitMQ的Exchange的封装,子类有TopicExchange、FanoutExchange和DirectExchange等。

Binding:
将一个Queue绑定到某个Exchange,本身只是一个声明,不做实际绑定操作。

AmqpAdmin: 
用于Exchange和Queue的管理的接口。
RabbitAdmin: 
AmqpAdmin的实现类。

ConnectionFactory: 
创建Connection的工厂类,Spring ConnectionFactory 对RabbitMQ ConnectionFactory的封装。
CachingConnectionFactory: 
Spring ConnectionFactory 的实现类,可以用于缓存Channel和Connection。
Connection: 
Spring中用于创建Channel的连接类。
SimpleConnection: 
Spring Connection的实现类。

MessageListenerContainer: 
消费端负责与RabbitMQ服务器保持连接并将Message传递给实际的@RabbitListener/@RabbitHandler处理的接口。 
RabbitListenerContainerFactory: 
用于创建 MessageListenerContainer 的接口。
SimpleRabbitListenerContainerFactory: 
RabbitListenerContainerFactory 的实现类。
SimpleMessageListenerContainer : 
MessageListenerContainer 的实现类。

RabbitProperties: 
用于配置Spring AMQP的Property类。

RabbitAdmin

@Autowired
private RabbitAdmin rabbitAdmin;

//创建交换机,无则创建有则跳过,均为持久化,不自动删除。
rabbitAdmin.declareExchange(new FanoutExchange("test.exchange.fanout", true, false));
rabbitAdmin.declareExchange(new DirectExchange("test.exchange.direct", true, false));
rabbitAdmin.declareExchange(new TopicExchange("test.exchange.topic", true, 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));

//绑定队列
rabbitAdmin.declareBinding(new Binding("test.direct.queue", Binding.DestinationType.QUEUE, 
 "test.exchange.topic", 
 "#", new HashMap<String, Object>()));        
rabbitAdmin.declareBinding(BindingBuilder
				.bind(new Queue("test.topic.queue", false))		//直接创建队列
				.to(new TopicExchange("test.topic", false, false))	//直接创建交换机 建立关联关系
				.with("user.#"));	//指定路由Key
				
//清空队列数据
rabbitAdmin.purgeQueue("test.direct.queue", false);
//删除队列
rabbitAdmin.deleteQueue("test.direct.queue");

//删除交换机
rabbitAdmin.deleteExchange("test.exchange.fanout");
rabbitAdmin.deleteExchange("test.exchange.direct");
rabbitAdmin.deleteExchange("test.exchange.topic");

配置相关的

RabbitTemplate 消息模板

@Autowired
private RabbitTemplate rabbitTemplate;

发送消息
1.send (自定义消息 Message)
Message message = new Message("hello".getBytes(),new MessageProperties());
// 发送消息到默认的交换器,默认的路由键
rabbitTemplate.send(message);
// 发送消息到指定的交换器,指定的路由键
rabbitTemplate.send("test.exchange.direct","key.1",message);
// 发送消息到指定的交换器,指定的路由键
rabbitTemplate.send("test.exchange.direct","key.1",message,new CorrelationData(UUID.randomUUID().toString()));
2.convertAndSend(把 Java 对象包装成 Message 对象,Java对象需实现 Serializable 序列化接口)
User user = new User("JunSouth");
// 发送消息到默认的交换器,默认的路由键
rabbitTemplate.convertAndSend(user);
// 发送消息到指定的交换器,指定的路由键,设置消息 ID
rabbitTemplate.convertAndSend("test.exchange.direct","key.1",user,new CorrelationData(UUID.randomUUID().toString()));
// 发送消息到指定的交换器,指定的路由键,在消息转换完成后,通过 MessagePostProcessor 来添加属性
rabbitTemplate.convertAndSend("test.exchange.direct","key.1",user,mes -> {
    mes.getMessageProperties().setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
        return mes;
});
MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put("desc", "信息描述..");
messageProperties.getHeaders().put("type", "自定义消息类型..");
Message message = new Message("Hello RabbitMQ".getBytes(), messageProperties);
rabbitTemplate.convertAndSend("test.exchange.direct", "spring.amqp", message, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
    System.err.println("------添加额外的设置---------");
    message.getMessageProperties().getHeaders().put("desc", "额外修改的信息描述");
    message.getMessageProperties().getHeaders().put("attr", "额外新加的属性");
    return message;
    }
});

接收消息
1.receive(返回 Message 对象)
// 接收来自指定队列的消息,并设置超时时间
Message msg = rabbitTemplate.receive("debug",3000);
2.receiveAndConvert(将返回 Message 转换成 Java 对象)
User user = (User) rabbitTemplate.receiveAndConvert();

SimpleMessageListenerContainer(主要为消费端使用)

监听队列(或者多个队列)、自动启动、自动声明。
设置事物特性、事物回滚、事物属性、事物容量(并发)、是否开启事物、回滚事物等!
设置消费者数量、最小最大数量、批量消费。
设置消息确认和自动确认模式、是否重回队列、异常捕获handler函数。
设置消费者标签生成策略、是否独占模式、消费者属性等!

常见的API:
setQueues: 设置监听的队列
setConcurrentConsumers: 设置当前消费者的数量
setMaxConcurrentConsumers: 设置最大的消费者的数量
setDefaultRequeueRejected: 是否重回队列
setAcknowledgeMode: 设置签收模式
setConsumerTagStrategy: 消费端的标签生成策略
setMessageListener: 设置消息的监听

@Bean
public SimpleMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
	SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
	// 设置需要监听的队列
	// 可以放入多个 queue 实例
	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();
		}
	});
	return container;
}

// 监听消息
container.setMessageListener(new ChannelAwareMessageListener() {
	/**
	 * 如果有消息传递过来,就会进入这个 onMessage 方法
	 * @param message 消息
	 * @param channel 消息管道
	 */
	@Override
	public void onMessage(Message message, Channel channel) throws Exception {
		String msg = new String(message.getBody(), "UTF-8");
		System.err.println("-----消费者接收到消息:" + msg);
	}
});

MessageListenerAdapter(消息监听适配器)

// 自定义的委托人(方法名是固定的)
public class MessageDelegate {
	//没有设置默认的处理方法的时候,方法名是handleMessage
	public void handleMessage(byte[] messageBody) {
		System.err.println("默认方法, 消息内容:" + new String(messageBody));
	}
	
	public void consumeMessage(byte[] messageBody) {
		System.err.println("字节数组方法, 消息内容:" + new String(messageBody));
	}
	
	public void consumeMessage(String messageBody) {
		System.err.println("字符串方法, 消息内容:" + messageBody);
	}
	
	public void method1(String messageBody) {
		System.err.println("method1 收到消息内容:" + new String(messageBody));
	}
	
	public void method2(String messageBody) {
		System.err.println("method2 收到消息内容:" + new String(messageBody));
	}
	
	public void consumeMessage(Map messageBody) {
		System.err.println("map方法, 消息内容:" + messageBody);
	}

	public void consumeMessage(Order order) {
		System.err.println("order对象, 消息内容, id: " + order.getId() + 
				", name: " + order.getName() + 
				", content: "+ order.getContent());
	}
	
	public void consumeMessage(Packaged pack) {
		System.err.println("package对象, 消息内容, id: " + pack.getId() + 
				", name: " + pack.getName() + 
				", content: "+ pack.getDescription());
	}
	
	public void consumeMessage(File file) {
		System.err.println("文件对象 方法, 消息内容:" + file.getName());
	}
}

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(null != contentType && contentType.contains("text")) {
			return new String(message.getBody());
		}
		return message.getBody();
	}

}

//图片转换器
public class ImageMessageConverter implements MessageConverter {
	@Override
	public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
		throw new MessageConversionException(" convert error ! ");
	}
	@Override
	public Object fromMessage(Message message) throws MessageConversionException {
		System.err.println("-----------Image MessageConverter----------");
		Object _extName = message.getMessageProperties().getHeaders().get("extName");
		String extName = _extName == null ? "png" : _extName.toString();
		byte[] body = message.getBody();
		String fileName = UUID.randomUUID().toString();
		String path = "d:/010_test/" + fileName + "." + extName;
		File f = new File(path);
		try {
			Files.copy(new ByteArrayInputStream(body), f.toPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return f;
	}
}

//PDF转换器
public class PDFMessageConverter implements MessageConverter {
	@Override
	public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException {
		throw new MessageConversionException(" convert error ! ");
	}
	@Override
	public Object fromMessage(Message message) throws MessageConversionException {
		System.err.println("-----------PDF MessageConverter----------");
		byte[] body = message.getBody();
		String fileName = UUID.randomUUID().toString();
		String path = "d:/" + fileName + ".pdf";
		File f = new File(path);
		try {
			Files.copy(new ByteArrayInputStream(body), f.toPath());
		} catch (IOException e) {
			e.printStackTrace();
		}
		return f;
	}
}
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
适配器方式
1.默认是有自己的方法名字的:handleMessage
MessageListenerAdapter adapter01 = new MessageListenerAdapter(new MessageDelegate());
//指定方法
adapter01.setDefaultListenerMethod("consumeMessage");
//添加转换器: 从字节数组转换为String
adapter01.setMessageConverter(new TextMessageConverter());
container.setMessageListener(adapter01);

2.指定队列名 和 方法名
MessageListenerAdapter adapter02 = new MessageListenerAdapter(new MessageDelegate());
adapter02.setMessageConverter(new TextMessageConverter());
Map<String, String> queueOrTagToMethodName = new HashMap<>();
//指定队列名 和 方法名 需修改
queueOrTagToMethodName.put("queue001", "method1");
queueOrTagToMethodName.put("queue002", "method2");
adapter02.setQueueOrTagToMethodName(queueOrTagToMethodName);
container.setMessageListener(adapter02);

转换器
1.json格式的转换器
MessageListenerAdapter adapter03 = new MessageListenerAdapter(new MessageDelegate());
adapter03.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
adapter03.setMessageConverter(jackson2JsonMessageConverter);
container.setMessageListener(adapter03);
2.java对象转换
MessageListenerAdapter adapter04 = new MessageListenerAdapter(new MessageDelegate());
adapter04.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter04= new Jackson2JsonMessageConverter();
// 加一层 Java 转换
DefaultJackson2JavaTypeMapper javaTypeMapper = new DefaultJackson2JavaTypeMapper();
jackson2JsonMessageConverter04.setJavaTypeMapper(javaTypeMapper);
adapter04.setMessageConverter(jackson2JsonMessageConverter04);
container.setMessageListener(adapter04);
3.java对象多映射转换
MessageListenerAdapter adapter05 = new MessageListenerAdapter(new MessageDelegate());
adapter05.setDefaultListenerMethod("consumeMessage");
Jackson2JsonMessageConverter jackson2JsonMessageConverter05 = new Jackson2JsonMessageConverter();
DefaultJackson2JavaTypeMapper javaTypeMapper05 = new DefaultJackson2JavaTypeMapper();

Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
//设置类信息
idClassMapping.put("User", User.class);
idClassMapping.put("Car", Car.class);
javaTypeMapper05.setIdClassMapping(idClassMapping);

jackson2JsonMessageConverter05.setJavaTypeMapper(javaTypeMapper05);
adapter05.setMessageConverter(jackson2JsonMessageConverter05);
container.setMessageListener(adapter05);

4.全局转换器
MessageListenerAdapter adapter = new MessageListenerAdapter(new MessageDelegate());
adapter.setDefaultListenerMethod("consumeMessage");

// convert (全局的转换器)
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);
// json转换器
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);
// PDF转换器
PDFMessageConverter pdfConverter = new PDFMessageConverter();
convert.addDelegate("application/pdf", pdfConverter);

adapter.setMessageConverter(convert);
container.setMessageListener(adapter);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章