交換器(exchange)的最大作用一方面在於接收生產者的信息,另一方面在於發送消息到不同的隊列,RabbitMQ中交換器被分爲三類:fanout,topic,director
RabbitMQ消息模型的核心理念:發佈者是將消息直接發送給交換機,由交換機來決定消息是發送到哪個隊列,或者是忽略消息。發佈者(producer)只需要把消息發送給一個交換機(exchange)。交換機非常簡單,它一邊從發佈者方接收消息,一邊把消息推送到隊列。
1.廣播模式(fanout)
廣播模式:生產的每一條消息,由所有消費者進行處理操作
圖片參考自網絡
消費者程序
生產者交換器核心代碼
//信道綁定交換器
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
poducer.class
public class MessageProducerfount {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange";
public static void main(String[] args) throws IOException, TimeoutException {
//創建連接工廠
ConnectionFactory factory=new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection=factory.newConnection();
//獲取信道,可以有多個信道
Channel channel=connection.createChannel();
//信道進行交換器類型指定
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//在開始前獲取一下當前時間,方便統計消息全部進入隊列所需的時間
long start=System.currentTimeMillis();
for (int i=0;i<10;i++){
String message="sjw"+i;
//basicPublish(exchange,隊列名稱,屬性,參數.getbyte())
channel.basicPublish(EXCHANGE_NAME,"", MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());
}
//結束時間
long end=System.currentTimeMillis();
//輸出所需時間
System.out.println("進入隊列總共耗時:"+(end-start));
//關閉信道
channel.close();
//關閉連接
connection.close();
}
}
消費者程序
消費者核心代碼
//信道綁定交換器
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//信道交換器綁定,綁定相應的隊列,消費者從隊列中取出數據進行處理
//第一個參數是隊列名稱,第二個是交換器名稱,第三個rountingkey
channel.exchangeBind(queuename,EXCHANGE_NAME,"");
consumer.class
public class MessageConsumer1 {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//創建連接工廠
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection = factory.newConnection();
//獲取信道,可以有多個信道
Channel channel = connection.createChannel();
//從信道中尋找隊列名稱
String queuename=channel.queueDeclare().getQueue();
//信道設置,必須與要對應接收的隊列設置一模一樣,有差別則無法接收你想要的信道
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//交換器Bind隊列,綁定相應的隊列,消費者從隊列中取出數據進行處理
//第一個參數是隊列名稱,第二個是交換器名稱,第三個rountingkey
channel.queueBind(queuename,EXCHANGE_NAME,"");
QueueingConsumer consumer = new QueueingConsumer(channel);
//信道交給consumer進行內容接收處理
channel.basicConsume(queuename,consumer);
while (true) { //消費者程序運行開着 如果生產者新增了數據會自動獲取
// nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("[消費者A:]"+"[消息]" + message);
}
}
}
消息生產,觀察exchange
打開三個消費者
發現三個消費者都在同時處理生產者產生的消息,廣播模式測試成功!!!
2.直連模式(direct)
直連模式:任何發送到Direct Exchange的消息都會被轉發到RouteKey中指定的Queue。只有key匹配上了,這個隊列的消費者才能進行消費操作
生產者程序
核心代碼
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
channel.basicPublish(EXCHANGE_NAME,"sjw-key", MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());
puducer.class
public class MessageProducerfount {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange.direct";
public static void main(String[] args) throws IOException, TimeoutException {
//創建連接工廠
ConnectionFactory factory=new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection=factory.newConnection();
//獲取信道,可以有多個信道
Channel channel=connection.createChannel();
//信道進行交換器類型指定
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
//在開始前獲取一下當前時間,方便統計消息全部進入隊列所需的時間
long start=System.currentTimeMillis();
for (int i=0;i<10;i++){
String message="sjw"+i;
//basicPublish(exchange,隊列名稱,屬性,參數.getbyte())
channel.basicPublish(EXCHANGE_NAME,"sjw-key", MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());
}
//結束時間
long end=System.currentTimeMillis();
//輸出所需時間
System.out.println("進入隊列總共耗時:"+(end-start));
//關閉信道
channel.close();
//關閉連接
connection.close();
}
}
消費者程序
核心代碼
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
channel.queueBind(queuename,EXCHANGE_NAME,"sjw-key");
consumer.class
public class MessageConsumer2 {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange.direct";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//創建連接工廠
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection = factory.newConnection();
//獲取信道,可以有多個信道
Channel channel = connection.createChannel();
//從信道中尋找隊列名稱
String queuename=channel.queueDeclare().getQueue();
//信道設置,必須與要對應接收的隊列設置一模一樣,有差別則無法接收你想要的信道
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
//信道交換器綁定,綁定相應的隊列,消費者從隊列中取出數據進行處理
//第一個參數是隊列名稱,第二個是交換器名稱,第三個rountingkey
channel.queueBind(queuename,EXCHANGE_NAME,"sjw-key");
QueueingConsumer consumer = new QueueingConsumer(channel);
//信道交給consumer進行內容接收處理
channel.basicConsume(queuename,consumer);
while (true) { //消費者程序運行開着 如果生產者新增了數據會自動獲取
// nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("[消費者B:]"+"[消息]" + message);
}
}
}
此時啓動三個消費者,只有B的與生產者key匹配
只有消費者B可以接收生產者產生的消息
直連模式成功!!!
3.主題模式(topic)
主題模式:主題模式更像是廣播模式和直連模式的結合體.根據自定義的規則分配rountingkey給不同的隊列,讓對應key的隊列進行處理.這種模式常用於RabbitMQ.
例如:我們讓模2=0的數字在消費者B出現,其餘的在消費者A出現
生產者代碼
public class MessageProducerfount {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange.topic";
public static void main(String[] args) throws IOException, TimeoutException {
//創建連接工廠
ConnectionFactory factory=new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection=factory.newConnection();
//獲取信道,可以有多個信道
Channel channel=connection.createChannel();
//信道進行交換器類型指定
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//在開始前獲取一下當前時間,方便統計消息全部進入隊列所需的時間
long start=System.currentTimeMillis();
for (int i=0;i<10;i++){
String message="sjw"+i;
//basicPublish(exchange,隊列名稱,屬性,參數.getbyte())
if (i%2==0) {
channel.basicPublish(EXCHANGE_NAME, "sjw-key-B", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
}else {
channel.basicPublish(EXCHANGE_NAME, "sjw-key-A", MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
}
}
//結束時間
long end=System.currentTimeMillis();
//輸出所需時間
System.out.println("進入隊列總共耗時:"+(end-start));
//關閉信道
channel.close();
//關閉連接
connection.close();
}
}
消費者代碼
消費者A
public class MessageConsumer1 {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange.topic";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//創建連接工廠
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection = factory.newConnection();
//獲取信道,可以有多個信道
Channel channel = connection.createChannel();
//從信道中尋找隊列名稱
String queuename=channel.queueDeclare().getQueue();
//信道設置,必須與要對應接收的隊列設置一模一樣,有差別則無法接收你想要的信道
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//交換器Bind隊列,綁定相應的隊列,消費者從隊列中取出數據進行處理
//第一個參數是隊列名稱,第二個是交換器名稱,第三個rountingkey
channel.queueBind(queuename,EXCHANGE_NAME,"sjw-key-A");
QueueingConsumer consumer = new QueueingConsumer(channel);
//信道交給consumer進行內容接收處理
channel.basicConsume(queuename,consumer);
while (true) { //消費者程序運行開着 如果生產者新增了數據會自動獲取
// nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("[消費者A:]"+"[消息]" + message);
}
}
}
消費者B
public class MessageConsumer2 {
//RabbitMQ服務所在地址
public final static String HOST="192.168.74.142";
//RabbitMQ端口
public final static int PORT=5672;
//RabbitMQ登陸用戶名
public final static String USERNAME="sjw";
//RabbitMQ登陸密碼
public final static String PASSWORD="123";
//隊列名稱
public final static String EXCHANGE_NAME="sjw.exchange.topic";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//創建連接工廠
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST);
factory.setPort(PORT);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost("sjw.virtual");
//獲取連接
Connection connection = factory.newConnection();
//獲取信道,可以有多個信道
Channel channel = connection.createChannel();
//從信道中尋找隊列名稱
String queuename=channel.queueDeclare().getQueue();
//信道設置,必須與要對應接收的隊列設置一模一樣,有差別則無法接收你想要的信道
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//信道交換器綁定,綁定相應的隊列,消費者從隊列中取出數據進行處理
//第一個參數是隊列名稱,第二個是交換器名稱,第三個rountingkey
channel.queueBind(queuename,EXCHANGE_NAME,"sjw-key-B");
QueueingConsumer consumer = new QueueingConsumer(channel);
//信道交給consumer進行內容接收處理
channel.basicConsume(queuename,consumer);
while (true) { //消費者程序運行開着 如果生產者新增了數據會自動獲取
// nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("[消費者B:]"+"[消息]" + message);
}
}
}