之前生產者的消息是發個一個消費者的,如果想多個消費者都收到消息(系統告知所有用戶今天促銷)就需要使用rabbitmq中交換機(Exchange)功能了。
簡示圖:
代碼:
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生產者
* 訂閱模式 Publish/Subscribe
* 通過交換機把消息發送給所有綁定該交換機的隊列
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//確認每次只發送一條信息
channel.basicQos(1);
//聲明一個交換機,該類型交換機對生產者的消息不做任何處理,所有綁定該交換機的隊列都會收到生產者的消息
channel.exchangeDeclare(exchange,"fanout");
for (int i = 0; i < 50; i++) {
String msg = "This is test_exchange_fanout:"+i;
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交換機名稱
* routingKey:隊列綁定的交換機的key:不指定的時候所有綁定該交換機的隊列都能收到消息
* body: 信息載體
*/
channel.basicPublish(exchange,"",null,msg.getBytes());
}
System.out.println("發送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消費者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_fanout";
private static String queue = "test_exchange_fanout_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"");
// 定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
/**
* 消費者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_fanout";
private static String queue = "test_exchange_fanout_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"");
// 定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
PS:生產者將消息發送到交換機中,哪個消費者的隊列綁定了這個交換機,就把消息發送到哪個隊列,這樣一個消息就會發送個不同的隊列讓多個消費者接收到了。
fanout:交換機的類型,該類型時所有綁定該交換機的隊列都會收到該交換機發出的消息
channel.exchangeDeclare(exchange,"fanout");
QA:若想生產者第一條消息消費者都能接收,第二條消息只讓部分消費者接收,應該如何實現?
此時需要使用Exchange的另一種類型:direct,同時隊列與交換機使用routingKey進行綁定。
簡示圖:
此時生產者向exchange推送消息時指定發送給哪個routingKey所綁定的隊列。
如指定發送個routingA綁定的隊列時兩個服務都能收到消息,發送消息給routingC綁定的隊列時則只有服務B能收到消息。
/**
* 生產者
* 路由模式
* 交換機的信息發送到指定的key,隊列綁定交換機的時候指定的該key才能收到信息
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//確認每次只發送一條信息
//prefetchSize:設置傳輸過來的消息的大小
//prefetchCount:一次傳輸多少消息
//global:是否是全局消息,true:整個channel都使用該設置,false只應用於當前的customer
//PS:global:目前設置爲false;
channel.basicQos(1);
//聲明一個交換機,該類型交換機對生產者的消息進行處理,綁定該交換機的隊列中存在和交換機發送信息時使用的key的隊列才能收到信息
//交換機名稱,交換機類型,是否持久化:消息未完成消費時是否寫到磁盤裏防止數據丟失
channel.exchangeDeclare(exchange,"direct",true);
for (int i = 0; i < 50; i++) {
String msg = "This is test_exchange_direct:"+i;
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交換機名稱
* routingKey:隊列綁定的交換機的key:指定的時候那個隊列有這個key哪個隊列就會收到交換機的信息
* body: 信息載體
*/
//todo routingKey:rabbit==》兩個隊列都綁定了rabbit所以交換機的信息兩個隊列都能收到
channel.basicPublish(exchange,"rabbit",null,msg.getBytes());
//todo routingKey:monkey==》隊列2綁定了monkey所以交換機的信息只有隊列2能收到
//channel.basicPublish(exchange,"monkey",null,msg.getBytes());
}
System.out.println("發送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消費者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_direct";
private static String queue = "test_exchange_direct_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
//rabbit:綁定的路由key
channel.queueBind(queue,exchange,"rabbit");
//定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
//消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消費者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_direct";
private static String queue = "test_exchange_direct_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"rabbit");
channel.queueBind(queue,exchange,"cat");
channel.queueBind(queue,exchange,"dog");
channel.queueBind(queue,exchange,"bird");
channel.queueBind(queue,exchange,"monkey");
// 定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
// 消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
//將交換機和隊列通過routingKey:rabbit進行綁定 ==》隊列和交換機之間可綁定多個routingKey
channel.queueBind(queue,exchange,"rabbit");
PS:生產者發送消息時寫的routingKey要和消費者綁定的routingKey完全一致,才能接收到消息
QA:每個routingKey都要完全一致,假設服務A是訂單模塊,服務B是庫存模塊,那按照上述的方式我們要綁定海量的routingKey纔可以,直接增大了開發量和業務的複雜性,如何簡化這樣的模式呢?
使用Exchange的topic模式,topic主要是實現對routingKey的模糊匹配,從而更加靈活的指定我們的消息發送給哪臺服務器。
簡示圖:
#:表示0個或多個
例:服務A推送信息的routingKey
order.delete.yesterday queueB可以接收
order.list 可以接收 queueB可以接收
*:表示一個
warehouse.query queueA可以接收
warehouse.delete.yesterday queueA無法接收
服務A發送信息推送至routingA 時,隊列queueA和隊列queueB都可以接收到信息
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 生產者
* Topic Exchange
* 此時隊列需要綁定要一個模式上。
* 符號“#”匹配一個或多個詞,符號“*”匹配一個詞。
* 因此“audit.#”能夠匹配到“audit.irs.corporate”
* 但是“audit.*” 只會匹配到“audit.irs”。
* Created by py
* 2020/4/20
*/
public class SendRabbit {
private static String exchange = "test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
//確認每次只發送一條信息
channel.basicQos(1);
//聲明一個交換機,隊列中的key可以和交換機發送信息的key進行匹配時就能收到消息
channel.exchangeDeclare(exchange,"topic",true);
String msg = "This is test_exchange_topic";
/**
* basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
* exchange:交換機名稱
* routingKey:隊列綁定的交換機的key:指定的時候那個隊列有這個key哪個隊列就會收到交換機的信息
* body: 信息載體
* */
//todo routingKey:rabbit==》兩個隊列都綁定了rabbit所以交換機的信息兩個隊列都能收到
channel.basicPublish(exchange,"order.aaa.bbb",null,msg.getBytes());
System.out.println("發送完成");
channel.close();
instance.close();
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消費者1
* Created by py
* 2020/4/20
*/
public class FristConsumer {
private static String exchange = "test_exchange_topic";
private static String queue = "test_exchange_topic_queue1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"order.list");
channel.queueBind(queue,exchange,"order.add");
channel.queueBind(queue,exchange,"order.#");
channel.queueBind(queue,exchange,"audit.*");
//定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
//消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("FristConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}
import com.example.springcloud.eurekaclinet1demo.uitl.RabbitUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 消費者2
* Created by py
* 2020/4/20
*/
public class SecondConsumer {
private static String exchange = "test_exchange_topic";
private static String queue = "test_exchange_topic_queue2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection instance = RabbitUtils.getInstance();
Channel channel = instance.createChannel();
channel.basicQos(1);
//設置讓rabbit消息持久化
boolean durable = true;
channel.queueDeclare(queue,durable,false,false,null);
channel.queueBind(queue,exchange,"order.del");
channel.queueBind(queue,exchange,"audit.#");
channel.queueBind(queue,exchange,"order.*");
//定義一個消費者
Consumer consumer = new DefaultConsumer(channel) {
//消息到達 觸發這個方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
try {
String msg = new String(body,"utf-8");
System.out.println("SecondConsumer:"+msg);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
boolean autoAck = false;
channel.basicConsume(queue, autoAck, consumer);
}
}