RabbitMQ入門(二) —— direct交換器

在RabbitMQ入門(一)裏我們講到exchange有三種最主要的類型:direct、fanout和topic。

這裏我們先來看看最簡單的direct交換器的使用。

wKiom1cMWZOC4_2LAAEtryR2zDM706.png

下面是測試代碼:

package com.jaeger.exchange.direct;

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

import org.junit.Test;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer {
	private static final String MY_EXCHANGE_NAME = "MyExchange";
	private static final String MY_ROUTING_KEY = "MyRoutingKey";
	private static final String MY_QUEUE_NAME = "MyQueue";
	private static final String DIRECT = "direct";
	private static final String HOST = "172.19.64.21";
	private static final String USER = "jaeger";
	private static final String PASSWORD = "root";
	private static final int PORT = 5672;

	@Test
	public void createExchangeAndQueue() throws IOException, TimeoutException {
		ConnectionFactory connectionFactory = new ConnectionFactory();
		connectionFactory.setHost(HOST);
		connectionFactory.setUsername(USER);
		connectionFactory.setPassword(PASSWORD);
		connectionFactory.setPort(PORT);
		Connection connection = connectionFactory.newConnection();
		Channel channel = connection.createChannel();
		// 創建一個direct類型的exchange
		channel.exchangeDeclare(MY_EXCHANGE_NAME, DIRECT);
		// 創建一個queue
		channel.queueDeclare(MY_QUEUE_NAME, false, false, false, null);
		// 創建一個routing key,把exchange和queue綁定到一起
		channel.queueBind(MY_QUEUE_NAME, MY_EXCHANGE_NAME, MY_ROUTING_KEY);
		channel.close();
		connection.close();
	}

	@Test
	public void produce() throws IOException, TimeoutException {
		ConnectionFactory connectionFactory = new ConnectionFactory();
		connectionFactory.setHost(HOST);
		connectionFactory.setUsername(USER);
		connectionFactory.setPassword(PASSWORD);
		connectionFactory.setPort(PORT);
		Connection connection = connectionFactory.newConnection();
		Channel channel = connection.createChannel();
		String message = "Hello 世界!";
		/*
		向RabbitMQ發送消息。我們這裏指定了exchange和routing key的名稱,RabbitMQ會去找有沒有叫這個名稱的exchange,
		如果找到了,就會再查看在該exchange上是否綁定一個跟我們指定名稱一樣的routing key,找到了就把消息放到routing key
		對應的queue裏面
		*/
		channel.basicPublish(MY_EXCHANGE_NAME, MY_ROUTING_KEY, null, message.getBytes("utf-8"));
		System.out.println("Sent '" + message + "'");
		channel.close();
		connection.close();
	}
	
	@Test
	public void consume() throws IOException, TimeoutException, InterruptedException{
		ConnectionFactory connectionFactory = new ConnectionFactory();
		connectionFactory.setHost(HOST);
		connectionFactory.setUsername(USER);
		connectionFactory.setPassword(PASSWORD);
		connectionFactory.setPort(PORT);
		Connection connection = connectionFactory.newConnection();
		Channel channel = connection.createChannel();
		Consumer consumer = new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
					byte[] body) throws IOException {
				String message = new String(body, "UTF-8");
				System.out.println("Received '" + message + "'");
			}
		};
		channel.basicConsume(MY_QUEUE_NAME, true, consumer);
		Thread.sleep(1000);
	}
}

在講direct交換器之前先簡單介紹下RabbitMQ的connection和channel。

客戶端到RabbitMQ的connection是通過TCP建立的,爲什麼不像數據庫一樣直接使用connection來操作呢?因爲TCP的建立和銷燬都非常消耗資源,對於一個消息服務器來說,它的任務就是處理海量的消息,如果每個消息的產生和消費都建立TCP連接的話顯然不合適。

爲了解決這個問題,引入了channel,channel相當於一個TCP連接內的虛擬連接。消息的產生和消費都是通過channel完成的,每個channel都會被指定一個唯一的ID用於區分不同的channel,以避免互相干擾。就好像光纖網線一樣,網線中的每條光纖束都可以用來傳遞消息,而不會出現互相干擾


下面我們就來看看direct交換器的效果,先執行createExchangeAndQueue方法:

wKioL1cMYNqCNHxsAAC0IOiF9BI184.png

wKiom1cMYCaRR3PqAABuSjXk7dM619.png

wKioL1cMYNqyn6qJAABgBBDMLuY417.png

從後臺可以看到,我們指定的exchange、routing key和queue都正確創建了,並且名稱都是我們指定的名稱。

接下來我們在運行produce方法向RabbitMQ發送消息:

wKiom1cMYdKj6DaGAABDckAmzDQ957.png

可以看到MyQueue隊列裏面已經添加了一條消息。最後運行consume方法去消費這條消息:

wKioL1cMZvbwQeUQAAAiuQLk4Vc540.png

對於消費端來說,只用知道queue的名稱就可以了。而對於發送端,則需要知道exchange和routing key的名稱,相對而言queue的名稱就不那麼重要了。




最後我們來分析下RabbitMQ入門(一)裏面的send方法,該方法裏面貌似並沒有指定exchange和routing key的名稱,也沒有進行queue和exchange的綁定操作,爲什麼也能發送成功呢?下面是代碼片段:

public void send() {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost(HOST);
    connectionFactory.setUsername(USER);
    connectionFactory.setPassword(PASSWORD);
    connectionFactory.setPort(PORT);

    try {
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null); //A
        String message = "Hello 世界!";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("utf-8")); //B
        System.out.println("Sent '" + message + "'");
        channel.close();
        connection.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (TimeoutException e) {
        e.printStackTrace();
    }
}

在A處我們創建了一個名稱爲QUEUE_NAME的queue,沒有讓其和任何的routing key對應,也沒有聲明任何exchange。

在B處我們發送消息的時候,指定的exchange是一個空字符串,routing key居然是queue的名稱,不是說exchange是根據routing key來決定放入哪個queue的麼,這裏怎麼用queue的名稱?


對於上面的問題,下面我們來說明下:

RabbitMQ裏面有一個默認的exchange,他的名稱就是一個空字符串。我們創建的每一個queue都會跟這個exchange進行綁定(所以我們在通過自定義的routing key綁定exchange和queue時,實際上這個queue還偷偷綁定到了這個默認的exchange),而中間的routing key的名稱就是queue的名稱。所以上面B處的方法中的exchange就是這個默認的exchange,而routing key看起來好像就是queue的名稱,實際上是因爲routing key的名稱跟queue名稱一樣而已。我們把上面produce方法修改下:

@Test
public void produce() throws IOException, TimeoutException {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost(HOST);
    connectionFactory.setUsername(USER);
    connectionFactory.setPassword(PASSWORD);
    connectionFactory.setPort(PORT);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    String message = "Hello 世界!";
    // 這裏我們把消息發送給默認的exchange,routing key名稱就是queue的名稱
    channel.basicPublish("", MY_QUEUE_NAME, null, message.getBytes("utf-8"));
    System.out.println("Sent '" + message + "'");
    channel.close();
    connection.close();
}

消息成功通過默認的exchange和routing key放到了MyQueue隊列裏面。

wKioL1cMitvCWDhlAAHPGuuHgrI050.png

wKiom1cMiieyXZhXAAFPbYVGCK0442.png

wKioL1cMitzRsSQFAABTHsxwagY388.png


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