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);

 

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