在RabbitMQ的基礎使用 —— Direct模式(一)中,我們已經瞭解了基礎的Direct模式的使用,其中我們發現一個隊列不僅僅可以對應一個路由鍵,它可以同時綁定多個路由鍵。
這裏我們繼續來了解下Rabbit中信道問題,之前RabbitMQ的基礎使用 —— Direct模式(一)中代碼中就是一個連接一個信道,不過我們之前AMQP及RabbitMQ概論中介紹到過,一個連接是可以有很多很多的信道的,這裏我們就來在代碼中看一看。
一個連接多個信道怎麼來設置呢,這裏我們可以簡單的把利用連接Connection創建信道Channel後的代碼,在拷貝一份即可
或者可以利用稍微優雅的方式,利用一個實現Runnable的內部類,調用時把連接Connection傳入進去創建信道Channel,這樣有幾個線程就代表一個連接有幾個信道,如下:
public class LogConsumer {
public static final String EXCHANGE_NAME = "logs";
private static class ConsumerWorker implements Runnable {
final Connection connection;
ConsumerWorker(Connection connection) {
this.connection = connection;
}
@Override
public void run() {
try {
//創建信道
Channel channel = connection.createChannel();
//可不創建,由生產者進行創建
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//聲明一個隨機隊列
String queueName = channel.queueDeclare().getQueue();
//將隊列和交換器通過路由鍵進行綁定
channel.queueBind(queueName, EXCHANGE_NAME, "error");
channel.queueBind(queueName, EXCHANGE_NAME, "warn");
channel.queueBind(queueName, EXCHANGE_NAME, "info");
//聲明瞭一個消費者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
StringBuffer buffer = new StringBuffer();
buffer.append(Thread.currentThread().getName()).append(", ")
.append(envelope.getExchange()).append(", ")
.append(envelope.getRoutingKey()).append(", ")
.append(new String(body, "UTF-8"));
System.out.println(buffer.toString());
}
};
//消費者正式開始在指定隊列上消費消息
channel.basicConsume(queueName, true, consumer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
//創建連接,連接到RabbitMQ,與發送端一樣
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
Connection connection = connectionFactory.newConnection();
//一個連接多個信道
for (int i = 0; i < 2; i++) {
//將連接作爲參數,傳遞給每個線程
Thread worker = new Thread(new ConsumerWorker(connection));
worker.start();
}
}
}
上述我們信道中的隊列名不能重複了,我們可以傳入不同的隊列名稱進去,或者使用channel.queueDeclare().getQueue()
去獲取不同的隨機隊列。
另外由於在多線程的情況下,我們像之前一樣分開打印可能會比較錯亂,這裏我們將其拼接在一起進行輸出,並且我們將其線程的名稱也打印了出來,這樣更加能幫助我們理解。
其消息的生成者和RabbitMQ的基礎使用 —— Direct模式(一)中的一致,這裏就不贅述了,直接進行測試,結果如下:
在AMQP及RabbitMQ概論中不僅說到一個連接有多個信道,還說過一個隊列可以有多個消費者。
這裏如果想讓一個隊列有多個消費者,我們只需將上述代碼進行修改即可,上述代碼使用了隨機隊列,這裏我們就將其修改爲傳入一個固定的隊列,然後每個線程都會消費這一個隊列,如下:
public class LogConsumer {
public static final String EXCHANGE_NAME = "logs";
private static class ConsumerWorker implements Runnable {
final Connection connection;
final String queueName;
public ConsumerWorker(Connection connection,String queueName) {
this.connection = connection;
this.queueName = queueName;
}
@Override
public void run() {
try {
//創建信道
Channel channel = connection.createChannel();
//可不創建,由生產者進行創建
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//創建一個隊列
channel.queueDeclare(queueName, false, false, false, null);
//將隊列和交換器通過路由鍵進行綁定
channel.queueBind(queueName, EXCHANGE_NAME, "error");
channel.queueBind(queueName, EXCHANGE_NAME, "warn");
channel.queueBind(queueName, EXCHANGE_NAME, "info");
//聲明瞭一個消費者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
StringBuffer buffer = new StringBuffer();
buffer.append(Thread.currentThread().getName()).append(", ")
.append(envelope.getExchange()).append(", ")
.append(envelope.getRoutingKey()).append(", ")
.append(new String(body, "UTF-8"));
System.out.println(buffer.toString());
}
};
//消費者正式開始在指定隊列上消費消息
channel.basicConsume(queueName, true, consumer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
//創建連接,連接到RabbitMQ,與發送端一樣
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
Connection connection = connectionFactory.newConnection();
//一個隊列多個消費者
String queueName = "allLog";
for (int i = 0; i < 2; i++) {
//將連接作爲參數,傳遞給每個線程
Thread worker = new Thread(new ConsumerWorker(connection, queueName));
worker.start();
}
}
}
這裏消息的生產者還是和之前一樣,無需進行修改,所以我們直接進行測試,測試結果如下: