RabbitMQ

一、工作原理RabbitMQ工作原理圖

組成部分說明如下: Broker:消息隊列服務進程,此進程包括兩個部分:Exchange和Queue。 Exchange:消息隊列交換機,按一定的規則將消息路由轉發到某個隊列,對消息進行過慮。
Queue:消息隊列,存儲消息的隊列,消息到達隊列並轉發給指定的消費方。 Producer:消息生產者,即生產方客戶端,生產方客戶端將消息發送到MQ。 Consumer:消息消費者,即消費方客戶端,接收MQ轉發的消息。
消息發佈接收流程:
-----發送消息-----
1、生產者和Broker建立TCP連接。
2、生產者和Broker建立通道。
3、生產者通過通道消息發送給Broker,由Exchange將消息進行轉發。
4、Exchange將消息轉發到指定的Queue(隊列)
----接收消息-----
1、消費者和Broker建立TCP連接
2、消費者和Broker建立通道
3、消費者監聽指定的Queue(隊列)
4、當有消息到達Queue時Broker默認將消息推送給消費者。
5、消費者接收到消息。

二、工作模式

work queue

1.兩個或多個消費者消費同一個隊列中的消息
2.生產者多次發送消息,消費端採用輪詢的方式接收
3.消費者在處理完某條消息之後,纔會收到下一條消息
代碼步驟:
1.創建通道
2.聲明隊列
無需使用交換機

Publish/Subscribe

1.每個消費者監聽自己的隊列
2.生產者發送消息,由交換機將消息轉發到綁定此交換機的每個隊列,每個綁定到交換機的隊列都能接收消息
代碼步驟
1.創建通道
2.聲明交換機
3.聲明隊列(交換機類型爲fanout)
4.隊列綁定到交換機(綁定的時候routingkey設爲"“即可)
5.發送消息(發送消息時,routingkey設爲”")

Routing

1.每個消費者監聽自己的隊列,並且設置routingkey
2.生產者將消息發給交換機,由交換機根據routingkey來轉發消息到指定的隊列
代碼步驟:
1.創建通道
2.聲明交換機
3.聲明隊列(交換機類型爲direct)
4.隊列綁定到交換機上(綁定的時候設置routingkey的值)
5.發送消息(發送消息的時候,指定要發送的routingkey)

Topic

1.每個消費者監聽自己的隊列,並且設置帶有通配符的routingkey
2.生產者將消息發送給broker(exchange+queue),由交換機根絕routingkey來轉發到指定的隊列
案例:
根據用戶的通知設置去通知用戶,設置接收Email的用戶只接收Email,設置接收sms的用戶只接收sms,設置兩種 通知類型都接收的則兩種通知都有效。
代碼步驟:
1.創建通道
2.聲明交換機(指定topic類型)
3.聲明隊列
4.生產端無需交換機綁定隊列,發送消息的時候指定routingkey

channel.basicPublish(EXCHANGR_TOPIC_INFORM, "sms.email", null, message.getBytes());

5.消費端隊列綁定到交換機上,使用通配符

注意問題

1.在隊列綁定交換機的時候,可使用

channel.queueBind(String queue,String exchange,String routingKey);

也可以使用

channel.exchangeBind(String exchange,String queue,String routingKey);

參數順序要注意!!
2.工作模式還有Header、RPC兩種模式

總結

Work queue 相當於多個消費者共同監聽一個個隊列,消息輪詢消費
Publish/Describe多個消費者有監聽各自的隊列,消息發送到各個隊列,一個消息被多個消費者消費
Routing多個消費者監聽各自的隊列,交換機根據路由轉發到指定的某個隊列
Topic多個消費者監聽各自的隊列,交換機根據路由轉發到指定的某些隊列

/**隊列綁定交換機指定通配符: 統配符規則: 中間以“.”分隔。 符號#可以匹配多個詞,
符號*可以匹配一個詞語
*/
channel.exchangeBind(QUEUE_INFORM_EMAIL, EXCHANGR_TOPIC_INFORM, "inform.#.email.#");
//僞代碼
--生產者
/*1.定義隊列名、交換機名*/
/*2.通過連接工廠建立Connection,創建Channel(即一個會話)*/

/*3.通過通道聲明交換機*/
/**
 * 參數明細
 * 1.交換機名稱
 * 2.交換機類型 fanout、topic、direct、headers
 */
channel.exchangeDeclare(String exchange, BuiltinExchangeType type);

/*4.通過通道聲明隊列*/
/**
 * 參數明細
 * 1.隊列名稱
 * 2.是否持久化
 * 3.是否獨佔此隊列
 * 4.隊列是否自動刪除
 * 5.參數
 */
channel.queueDeclare(String queue, boolean durable, boolean exclusive,
				boolean autoDelete, Map<String, Object> arguments);

/*5.交換機和隊列進行綁定*/
/**
 * 參數明細
 * 1.隊列名稱
 * 2.交換機名稱
 * 3.routingKey
 */
channel.queueBind(String queue, String exchange, String routingKey);

/*6.發送消息*/
/**
 * 參數明細
 * 1.交換機名稱
 * 2.routingKey根據key名稱將消息轉發到具體的隊列,表示消息將發到此隊列
 * 3.消息屬性
 * 4.消息體
 */
channel.basicPublish(String exchange, String routingKey, 
					BasicProperties props,byte[] body);

---消費者
/*1.定義隊列名、交換機名*/
/*2.通過連接工廠建立Connection,創建Channel(即一個會話)*/
/*3.通過通道聲明交換機*/
/**
 * 參數明細
 * 1.交換機名稱
 * 2.交換機類型 fanout、topic、direct、headers
 */
channel.exchangeDeclare(String exchange, BuiltinExchangeType type);

/*4.通過通道聲明隊列*/
/**
 * 參數明細
 * 1.隊列名稱
 * 2.是否持久化
 * 3.是否獨佔此隊列
 * 4.隊列是否自動刪除
 * 5.參數
 */
channel.queueDeclare(String queue, boolean durable, boolean exclusive,
				boolean autoDelete, Map<String, Object> arguments);

/*5.交換機和隊列進行綁定*/
/**
 * 參數明細
 * 1.隊列名稱
 * 2.交換機名稱
 * 3.routingKey
 */
channel.queueBind(String queue, String exchange, String routingKey);

/*6.定義消費消息的方法*/
//定義消費方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
	@Override
	public void handleDelivery(String consumerTag,
                    	Envelope envelope,
                   		AMQP.BasicProperties properties,
                    	byte[] body) throws IOException {
		long deliveryTag = envelope.getDeliveryTag();
		String exchange = envelope.getExchange();
		//接收到的消息內容
		String message = new String(body,"utf-8");
		System.out.println(message);
	}
};

/*7.監聽隊列,接收消息*/
/** 參數明細
 * 1.監聽的隊列名稱
 * 2.是否自動回覆,設置爲true爲表示消息接收到自動向mq回覆接收到了,
 * 				mq接收到回覆會刪除消息,設置 爲false則需要手動回覆	
 * 3.消費者接收到消息後調用的方法
 */
channel.basicConsume(String queue, boolean autoAck,Consumer callback)
//消息生產者
public class Producer01 {

	//隊列名稱
	private static final String QUEUE = "helloworld";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		Connection connection = null;
		Channel channel = null;
		try {
			ConnectionFactory factory = new ConnectionFactory();
			factory.setHost("localhost");
			factory.setPort(5672);
			factory.setUsername("guest");
			factory.setPassword("guest");
			factory.setVirtualHost("/");//rabbitmq默認虛擬機名稱爲“/”,虛擬機相當於一個獨立的mq服務器
			
			//創建與rabbitmq服務的tcp連接
			connection = factory.newConnection();
			//創建Exchange通道,每個連接可以創建多個通道,每個通道代表一個會話
			channel = connection.createChannel();
			/**
			 * 聲明隊列,如果rabbitmq中沒有此隊列將自動創建
			 * param1:隊列名稱
			 * param2:是否持久化
			 * param3:隊列是否獨佔此連接
			 * param4:隊列不再使用時是否自動刪除此隊列
			 * param5:隊列參數
			 */
			channel.queueDeclare(QUEUE, true, false, false, null);
			String message = "helloworld小明" + System.currentTimeMillis();
			/**
			 * 消息發佈
			 * param1:Exchange名稱,如果沒有指定交換機,消息將會發給默認的交換機,每個隊列也會綁定那個默認的交換機,
			 * 		    但是不能顯示綁定或者解除綁定
			 * param2:routingKey,消息的路由key,用於Exchange將消息轉發到指定的隊列  
			 * 		  使用默認交換機時,routingKey使用
			 * param3:消息包含的屬性
			 * param4:消息體
			 */
			channel.basicPublish("", QUEUE, null, message.getBytes());
			System.out.println("send Message is '" + message +"'");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TimeoutException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally {
			if (channel != null) {
				channel.close();
			}
			if (connection != null) {
				connection.close();
			}
		}
	}
}
//消費者
public class Consumer01 {

	//隊列名稱
	private static final String QUEUE = "helloworld";
	
	public static void main(String[] args) throws IOException, TimeoutException {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("127.0.0.1");
		factory.setPort(5672);
		Connection connection = factory.newConnection();
		Channel channel = connection.createChannel();
		//聲明隊列
		channel.queueDeclare(QUEUE, true, false, false, null);
		//自定義消費方法 繼承DefaultConsumer
		DefaultConsumer consumer = new DefaultConsumer(channel) {
			/**
			 * 消費者接受消息調用此方法
			 * @param consumerTag 消費者的標籤,在channel.basicConsume()去指定
			 * @param envelope 消息包的內容,可從中獲取消息id,消息routingKey,交換機,消息和
			 * 		     重傳表示(收到消息失敗後是否需要重新發送)
			 * @param properties 
			 * @param body 消息體
			 */
			@Override
			public void handleDelivery(String consumerTag,
                    Envelope envelope,
                    AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
				//交換機
				String exchange = envelope.getExchange();
				//路由key
				String routingKey = envelope.getRoutingKey();
				//消息id
				long deliveryTag = envelope.getDeliveryTag();
				//消息內容
				String msg = new String(body,"utf-8");
				System.out.println("receive message.."+ msg);
			}
		};
		/**
		 * 監聽隊列String queue,boolean autoAsk,Consumer callback
		 * 參數明細
		 * 1.隊列名稱
		 * 2.是否自動回覆,設置爲true表示消息接收到自動向mq回覆接收到了,mq接收到回覆消息會刪除消息,
		 * 	設置爲false則需要手動回覆
		 * 3.消費者接收消息後調用此方法
		 */
		channel.basicConsume(QUEUE, true,consumer);
	}
}

先啓動消息的生產者,發送一條消息到mq中,然後再啓動消費者,監聽並拿到這條消息,此時消費者仍會持續監聽,再使用生產者發送一條消息,消費者會立馬接收到

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