(四)RabbitMQ 官方的七種工作模式(含RPC模式)及可實操JAVA運行代碼(2020最新)

官網介紹:https://www.rabbitmq.com/getstarted.html

(1) 簡單模式:一個生產者,一個消費者

(2) work模式:一個生產者,多個消費者,每個消費者獲取到的消息唯一。

(3) 訂閱模式:一個生產者發送的消息會被多個消費者獲取。

(4) 路由模式:發送消息到交換機並且要指定路由key ,消費者將隊列綁定到交換機時需要指定路由key

(5) topic模式:將路由鍵和某模式進行匹配,此時隊列需要綁定在一個模式上,“#”匹配一個詞或多個詞,“*”只匹配一個詞

(6) RPC模式:使用RabbitMQ構建RPC系統:客戶端和可伸縮RPC服務器。

(7)發佈確認:與發佈者進行可靠的發佈確認

  

 

	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
	</dependency>
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbitadmin
spring.rabbitmq.password=123456
spring.rabbitmq.virtual-host=/vhost_rabbitmq

 

public class ConnectionUitls {
	
	public static Connection getConnection() throws IOException, TimeoutException{
		//定義連接工廠
		ConnectionFactory factory = new ConnectionFactory();
		//設置連接地址
		factory.setHost("127.0.0.1");
		//AMQP 5672
		factory.setPort(5672);
		//vhost
		factory.setVirtualHost("/vhost_jetsen");
		//username
		factory.setUsername("rabbitadmin");
		//password
		factory.setPassword("123456");
		Connection newConnection = factory.newConnection();
		return newConnection;
	}
}

(1) 簡單模式:一個生產者,一個消費者

package com.jetsen.mq.q1simplequeue;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
//Java操作simple簡單隊列
//1.耦合性高,生產者一一對應,(如果想有多個消費者 ,不可行)
//2.隊列名稱變更,生產者、消費者需同時變更。
public class Sender {
	
	private static final String QUEUE_NAME="test_simple_queueu";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		//獲取通道
		Channel channel = connection.createChannel();
		//隊列聲明
		//String queue, boolean durable, boolean exclusive, boolean autoDelete,  Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String message = "send message !";
		
		//String exchange, String routingKey, BasicProperties props, byte[] body
		channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
		
		System.out.println("===>send msg:"+message);
		
		channel.close();
		
		connection.close();
	}
}

隊列的申明

Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
                                 Map<String, Object> arguments) throws IOException;

queue:隊列名稱

durable:是否持久化, 隊列的聲明默認是存放到內存中的,如果rabbitmq重啓會丟失,如果想重啓之後還存在就要使隊列持久化,保存到Erlang自帶的Mnesia數據庫中,當rabbitmq重啓之後會讀取該數據庫。

exclusive:是否排外的,有兩個作用,一:當連接關閉時connection.close()該隊列是否會自動刪除;二:該隊列是否是私有的private,如果不是排外的,可以使用兩個消費者都訪問同一個隊列,沒有任何問題,如果是排外的,會對當前隊列加鎖,其他通道channel是不能訪問的,如果強制訪問會報異常

autoDelete:是否自動刪除,當最後一個消費者斷開連接之後隊列是否自動被刪除,可以通過RabbitMQ Management,查看某個隊列的消費者數量,當consumers = 0時隊列就會自動刪除

arguments: 隊列中的消息什麼時候會自動被刪除

 

 void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;

package com.jetsen.mq.q1simplequeue;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
//Java操作simple簡單隊列
public class Recv {
	
	private static final String QUEUE_NAME="test_simple_queueu";

	
	public static void main(String[] args) throws IOException, TimeoutException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		//創建頻道
		Channel channel = connection.createChannel();
		//QUEUE_NAME, durable, exclusive, autoDelete, arguments 隊列聲明
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		//事件模型
		DefaultConsumer consumerCallback = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer receive:=>"+messageStr);
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumerCallback);
		
	}
}

 

(2) work模式:一個生產者,多個消費者,每個消費者獲取到的消息唯一。

RabbitMq消費分發機制及主題消費分發(公平分發 + 輪訓分發)

公平分發 :現在有2個消費者,我們使用basicQos( prefetchCount = 1)方法,來限制RabbitMQ只發不超過1條的消息給同一個消費者。當消息處理完畢後,有了反饋,纔會進行第二次發送。

生產者每次發送間隔爲20ms,消費者1消費間隔爲2s,消費者2消費間隔爲1s,代表消費者2消費能力高。

公平分發

//每個消費者,在發送【確認消息】之前,消息隊列不發送下一個消息到消費者,一次只處理一個消息
//限制發送給同一個消費者不得超過1條消息
int prefetchCount = 1;
 channel.basicQos(prefetchCount);

//手動回執消息
channel.basicAck(envelope.getDeliveryTag(), false);

//公平隊列將自動應答修改爲false;
 boolean autoAck = false;
 channel.basicConsume(QUEUE_NAME,autoAck, consumer);

package com.jetsen.mq.q2workqueue.fairdispatch;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 工作隊列
 * 						  |----cl	 2s
 * p-----------queue------|
 * 						  |----c2	 1s
 */
public class Sender {
	
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		
		//獲取channel
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//每個消費者,在發送【確認消息】之前,消息隊列不發送下一個消息到消費者,一次只處理一個消息
		//限制發送給同一個消費者不得超過1條消息
		int prefetchCount = 1;
		channel.basicQos(prefetchCount);
		
		for (int i = 0; i < 50; i++) {
			String sendMsg = "Sender==>send message"+i;
			System.out.println("Sender==>send message"+i);
			channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
			Thread.sleep(i*20);
		}
		
		channel.close();
		connection.close();
	}
}
package com.jetsen.mq.q2workqueue.fairdispatch;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發

public class Recv1 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		//創建頻道
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//保證一次只分發一個
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
					//手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);

	}
}

 

package com.jetsen.mq.q2workqueue.fairdispatch;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發

public class Recv2 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		//創建頻道
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//保證一次只分發一個
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
					//手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);

	}
}

輪訓分發

手動改爲自動回執消息 channel.basicAck(envelope.getDeliveryTag(), true)

自動應答修改爲autoAck true   channel.basicConsume(QUEUE_NAME,true, consumer);

去掉流浪控制channel.basicQos(prefetchCount);

package com.jetsen.mq.q2workqueue.roundrobin;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 工作隊列
 * 						  |----cl	 2s
 * p-----------queue------|
 * 						  |----c2	 1s
 */
public class Sender {
	
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		
		//獲取channel
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		for (int i = 0; i < 50; i++) {
			String sendMsg = "Sender==>send message"+i;
			System.out.println("Sender==>send message"+i);
			channel.basicPublish("", QUEUE_NAME, null, sendMsg.getBytes());
			Thread.sleep(i*20);
		}
		
		channel.close();
		connection.close();
	}
}
package com.jetsen.mq.q2workqueue.roundrobin;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發

public class Recv1 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		//創建頻道
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumer);

	}
}
package com.jetsen.mq.q2workqueue.roundrobin;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
//work queues工作隊列之Round-robin:消費者1、消費者2處理的消息一樣多,進行的是輪詢分發

public class Recv2 {
	private static final String QUEUE_NAME="test_work_queue";

	public static void main(String[] args) throws IOException, TimeoutException {
		//獲取連接
		Connection connection = ConnectionUitls.getConnection();
		//創建頻道
		Channel channel = connection.createChannel();
		
		//申明隊列String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(1000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, consumer);

	}
}

 

(3) 訂閱模式:一個生產者發送的消息會被多個消費者獲取。

解讀:
1、1個生產者,多個消費者
2、每一個消費者都有自己的一個隊列
3、生產者沒有將消息直接發送到隊列,而是發送到了交換機
4、每個隊列都要綁定到交換機
5、生產者發送的消息,經過交換機,到達隊列,實現,一個消息被多個消費者獲取的目的
注意:一個消費者隊列可以有多個消費者實例,只有其中一個消費者實例會消費

 

package com.jetsen.mq.q3pubsub;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * publish_subscribe訂閱模式
 * 
 * 
 * ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)
 * 
 *
 * (1)fanout:(不處理路由鍵)
 * 				 		  |---queue1----c1   
 * p----exchange-Bindings-|
 * 						  |---queue2----c2	 

 */
public class Sender {
	
	private static final String EXCHANGE_NAME="test_exchage_fanout";

	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//聲明交換機
		channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分發類型
		
		//發送消息
		String message = "send pub sub message ....";
		
		//exchange, routingKey, props, body
		channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
		
		System.out.println("Send message:"+message);
		
		channel.close();
		
		connection.close();
	}
}
package com.jetsen.mq.q3pubsub;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME="test_exchage_fanout";

	private  static final String QUEUE_NAME="test_queue_fanout_email";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
		
		//保證一次只分發一個
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[1] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[1] done!");
					//手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);
		
	}
}

 

package com.jetsen.mq.q3pubsub;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME="test_exchage_fanout";

	private  static final String QUEUE_NAME="test_queue_fanout_msm";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
		
		//保證一次只分發一個
		channel.basicQos(1);
		
		Consumer consumer = new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[2] receive:=>"+messageStr);
				
				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally{
					System.out.println("Consumer[2] done!");
					//手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};
		//String queue, boolean autoAck, Consumer callback
		
		//公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME,autoAck, consumer);
		
	}
}

    //聲明交換機
    channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//分發類型 

(4) 路由模式:發送消息到交換機並且要指定路由key ,消費者將隊列綁定到交換機時需要指定路由key

 

package com.jetsen.mq.q4routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
/**
 * 
 * ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)'
 * 
 * 
 * (2)Direct:(處理路由鍵)
 * 				 	type=direct	 	   |---queue1(routing key)----c1   
 * p(routing key)----exchange-Bindings-|
 * 						  			   |---queue2----c2	
 * 									   |
 * 									   |---queue3----c3	 
 */
public class Sender {
	
	private static final String EXCHANGE_NAME="test_exchange_direct";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		//聲明交換機
		channel.exchangeDeclare(EXCHANGE_NAME, "direct");//分發類型
		
		//發送消息
		String message = "send  direct message ....";
		
		String routingKey="error";
		
		channel.basicPublish(EXCHANGE_NAME, routingKey, null, message.getBytes());
		
		System.out.println("Send message:"+message);
		
		channel.close();
		
		connection.close();
	}
}

 

package com.jetsen.mq.q4routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME = "test_exchange_direct";

	private static final String QUEUE_NAME = "test_queue_direct_1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "error";

		// 綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保證一次只分發一個
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[1] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[1] done!");
					// 手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}
package com.jetsen.mq.q4routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME = "test_exchange_direct";

	private static final String QUEUE_NAME = "test_queue_direct_2";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKeyInfo = "info";
		String routingKeyWarning = "warning";
		String routingKeyError = "error";


		// 綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyInfo);
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyWarning);
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKeyError);

		// 保證一次只分發一個
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[2] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[2] done!");
					// 手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

由於Exchange綁定的兩個隊列中, Bingdings四個鍾,有兩個是訂閱了errro的消息,所以兩個消費者都可以消費到消息

(5) topic模式:將路由鍵和某模式進行匹配,此時隊列需要綁定在一個模式上,“#”匹配一個詞或多個詞,“*”只匹配一個詞

這裏寫圖片描述

這裏寫圖片描述

 

package com.jetsen.mq.q5topic;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

/**
 * 
 * ""匿名轉發: channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
 * 
 * Exchange(一方面是接收生產者的消息,另一方面將消息推送至各自的隊列)'
 * 
 * 
 * (3)topic:(topic主題模式)
 * 		type=topic	 	   			   |---queue1----c1   
 * p----exchange-Bindings--------------|
 * 						  			   |---queue2----c2	
 * 									   |
 * 									   |---queue3----c3	 
 * 將路由和某個模式匹配(符號) *匹配一個  #匹配多個
 */
public class Sender {

	private static final String EXCHANGE_NAME="test_exchange_topic";

	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.exchangeDeclare(EXCHANGE_NAME, "topic");
		
		String msgString="商品....";
		
		channel.basicPublish(EXCHANGE_NAME, "goods.add", null, msgString.getBytes());
		
		System.out.println("----send" +msgString);
		
		channel.close();
		
		connection.close();
	}
}

 

package com.jetsen.mq.q5topic;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv1 {

	private static final String EXCHANGE_NAME = "test_exchange_topic";

	private static final String QUEUE_NAME = "test_queue_topic_1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "goods.add";

		// 綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保證一次只分發一個
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[2] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[2] done!");
					// 手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}
package com.jetsen.mq.q5topic;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv2 {

	private static final String EXCHANGE_NAME = "test_exchange_topic";

	private static final String QUEUE_NAME = "test_queue_topic_2";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();

		Channel channel = connection.createChannel();

		// queue, durable, exclusive, autoDelete, arguments
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);

		String routingKey = "goods.#";

		// 綁定隊列到交換機
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

		// 保證一次只分發一個
		channel.basicQos(1);

		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body, "UTF-8");
				System.out.println("Consumer[1] receive:=>" + messageStr);

				try {
					Thread.sleep(2000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					System.out.println("Consumer[1] done!");
					// 手動回執消息
					channel.basicAck(envelope.getDeliveryTag(), false);
				}
			}
		};

		// 公平隊列將自動應答修改爲false;
		boolean autoAck = false;
		channel.basicConsume(QUEUE_NAME, autoAck, consumer);
	}
}

 

       String routingKey = "goods.#";

        // 綁定隊列到交換機
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, routingKey);

(6) RPC模式:使用RabbitMQ構建RPC系統:客戶端和可伸縮RPC服務器。

RPC的處理流程

  • 當客戶端啓動時,創建一個匿名的回調隊列。
  • 客戶端爲RPC請求設置2個屬性:replyTo,設置回調隊列名字;correlationId,標記request。
  • 請求被髮送到請求隊列中。
  • RPC服務器端監聽請求隊列中的請求,當請求到來時,服務器端會處理並且把帶有結果的消息發送給客戶端。接收的隊列就是replyTo設定的回調隊列。
  • 客戶端監聽回調隊列,當有消息時,檢查correlationId屬性,如果與request中匹配,則返回。

package com.jetsen.mq.q11.rpc;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class RpcClient {

	private static final String RPC_QUEUE_NAME = "test_rpc_queueu";

	public static void main(String[] args) {
		Connection connection = null;
		// 獲取通道
		Channel channel = null;
		try {
			connection = ConnectionUitls.getConnection();
			// 獲取通道
			channel = connection.createChannel();
			// 創建回調隊列
			String callBackQueue = channel.queueDeclare().getQueue();
			// 創建帶有correlationId的消息屬性
			String correlationId = UUID.randomUUID().toString();
			AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().correlationId(correlationId)
					.replyTo(callBackQueue).build();
			// 消費者從回調隊列中接受服務端傳遞的消息
			DefaultConsumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
						byte[] body) throws IOException {
					String recvcorrelationId = properties.getCorrelationId();
					if (correlationId.equals(recvcorrelationId)) {
						System.out.println("客戶單接收消息:" + new String(body) + ",correlationId=" + correlationId);
					}
				}
			};
			// 監聽隊列
			channel.basicConsume(callBackQueue, true, consumer);
			String message = "rpc rabbitmq";
			channel.basicPublish("", RPC_QUEUE_NAME, basicProperties, message.getBytes());
			System.out.println("客戶端發送消息:" + message + ",correlationId=" + correlationId);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			
		}
	}
}
package com.jetsen.mq.q11.rpc;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class RpcServer {
	private static final String RPC_QUEUE_NAME="test_rpc_queueu";

	public static void main(String[] args) {
		try {
			Connection connection = ConnectionUitls.getConnection();
			//獲取通道
			Channel channel = connection.createChannel();
			//創建隊列
			channel.queueDeclare(RPC_QUEUE_NAME,false,false,false,null);
			
			//申明消費者預取數量
			channel.basicQos(1);
			//創建消費者
			// 消費者從隊列中接受服務端傳遞的消息
			DefaultConsumer consumer = new DefaultConsumer(channel) {
				@Override
				public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
						byte[] body) throws IOException {
					System.out.println("服務端接收消息:" + new String(body));
					channel.basicAck(envelope.getDeliveryTag(), false);
					
					String correlationId = properties.getCorrelationId();
					AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
							.correlationId(correlationId)
							.build();
					channel.basicPublish("", properties.getReplyTo(), basicProperties, "RPC SERVER:11111111".getBytes());
				}
			};
			// 監聽隊列
			channel.basicConsume(RPC_QUEUE_NAME, false, consumer);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}

}

 

(7)發佈確認:與發佈者進行可靠的發佈確認

Confirm發送方確認模式使用和事務類似,也是通過設置Channel進行發送方確認的。

package com.jetsen.mq.q7confirm;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;

public class Recv {

	private static final String QUEUE_NAME = "test_queue_confirm1";

	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = ConnectionUitls.getConnection();
		Channel channel = connection.createChannel();
		//String queue, boolean autoAck, Consumer callback
		channel.basicConsume(QUEUE_NAME,true, new DefaultConsumer(channel){
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String messageStr = new String(body,"UTF-8");
				System.out.println("Consumer[test_queue_confirm1] receive:=>"+messageStr);
			}
		});
		
	}
}

Confirm的三種實現方式:

方式一:channel.waitForConfirms()普通發送方確認模式;

package com.jetsen.mq.q7confirm;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;


/**
 * rabbitmq消息確認機制之confirm
 */
public class ConfirmSender {

	
	private static final String QUEUE_NAME = "test_queue_confirm1";

	
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String msgString =" send confirm message";
		
		//生產者調用將channel設爲confirm模式,注意
		channel.confirmSelect();
		channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
		System.out.println("----send" +msgString);
		
		if(!channel.waitForConfirms()){
			System.out.println("send confirm failed");
		}else{
			System.out.println("send confirm success");
		}
		channel.close();
		connection.close();
	}
}

我們只需要在推送消息之前,channel.confirmSelect()聲明開啓發送方確認模式,再使用channel.waitForConfirms()等待消息被服務器確認即可。

方式二:channel.waitForConfirmsOrDie()批量確認模式;

package com.jetsen.mq.q7confirm;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;


/**
 * rabbitmq消息確認機制之confirm
 */
public class ConfirmBatchSender {

	
	private static final String QUEUE_NAME = "test_queue_confirm1";

	
	public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		String msgString =" send confirm message";
		
		//生產者調用將channel設爲confirm模式,注意
		channel.confirmSelect();
		
		for (int i = 0; i < 10; i++) {
			channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
		}
		
//		if(!channel.waitForConfirms()){
//			System.out.println("send confirm failed");
//		}else{
//			System.out.println("send confirm success");
//		}
		channel.waitForConfirmsOrDie(); //直到所有信息都發布,只要有一個未確認就會IOException
		channel.close();
		connection.close();
	}
}

以上代碼可以看出來channel.waitForConfirmsOrDie(),使用同步方式等所有的消息發送之後纔會執行後面代碼,只要有一個消息未被確認就會拋出IOException異常。

方式三:channel.addConfirmListener()異步監聽發送方確認模式;

package com.jetsen.mq.q7confirm;

import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;

import com.jetsen.mq.util.ConnectionUitls;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;

/**
 * 異步模式
 * Channel 對象提供的ConfirmListener()回調方法之包含deliveryTag(當前Channel發出
 * 的消息序號),我們需要自己爲每一個Channel維護一個unconfirm的消息序號集合,每publish
 * 一條數據,集合中元素就+1,每回調一次handlerAck方法,unconfirm結合刪掉相應的一條(multiple=false)
 * 或者多條(multiple=true)記錄,從程序運行效率上看,這個unconfirm集合最好採用有序集合
 * SortedSet存儲結構
 * @author jetsen
 *
 */
public class ConfirmAysnSender {
	
	
	private static final String QUEUE_NAME = "test_queue_confirm3";

	
	public static void main(String[] args) throws IOException, TimeoutException {
		
		Connection connection = ConnectionUitls.getConnection();
		
		Channel channel = connection.createChannel();
		
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);
		
		//生產者調用confirmSelect將channel設置爲Confirm模式
		channel.confirmSelect();
		
		//未確認消息的標識
		final SortedSet<Long> confirmSet=Collections.synchronizedSortedSet(new TreeSet<Long>());
		
		channel.addConfirmListener(new ConfirmListener() {
			
			@Override
			public void handleNack(long deliveryTag, boolean multiple) throws IOException {
				if(multiple){
					System.out.println("----handlerNack-----multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("----handlerNack-----multiple----false");
					confirmSet.remove(deliveryTag);
				}
			}
			
			@Override
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				if(multiple){
					System.out.println("----handleAck-----multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("----handleAck-----multiple----false");
					confirmSet.remove(deliveryTag);
				}
			}
		});
		
		String msgString =" send confirm Aysn message";
		while(true){
			long segNo = channel.getNextPublishSeqNo();
			channel.basicPublish("", QUEUE_NAME, null, msgString.getBytes());
			confirmSet.add(segNo);
			
		}
	}
}

異步模式的優點,就是執行效率高,不需要等待消息執行完,只需要監聽消息即可。

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