4、RabbitMQ的七種工作模式介紹

目錄

一、Simple(簡單模式)

1.1 概念

1.2 代碼實現

消費者

運行結果

二、Work Queue(工作隊列)

2.1 概念

1.2 代碼實現

生產者

消費者

?運行結果

三、Publish/Subscribe(發布/訂閱模式)

3.1 概念

3.2 代碼實現

生產者

消費者

運行結果

四、Routing(路由模式)

4.1 概念

4.2 代碼實現

Constants類

生產者

消費者

運行結果

五、Topics(通配符模式)

5.1 概念

5.2 代碼實現

生產者

消費者

運行結果

六、RPC(RPC通信)了解

6.1 概念

6.2 代碼實現

客戶端代碼編寫

編寫服務器代碼

運行結果:

七、Publish Confirms(發布確認模式)???????

publishing Messages Individually(單獨確認)

Publishing Messages in Batches(批量確認)

Handling Publisher Confirms Asynchronously(異步確認)


一、Simple(簡單模式)

1.1 概念

P:?生產者,也就是要發送消息的程序

C:?消費者,消息的接受者

Queue:消息隊列,圖中Queue類似提個郵箱,可以緩存消息;生產者向其中投遞消息,消費者從中取出消息。

特點:一個生產者P,一個消費者C,消息只能被消費一次,也稱為點對點(Point-to-Point)模式

適用場景:消息只能被單個消費者處理

1.2 代碼實現

消費者

package rabbitmq.simple;import com.rabbitmq.client.*;
import java.io.IOException;public class ConsumerDemo {public static void main(String[] args) throws Exception {//1.創建連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost("8.136.108.248");// 設置RabbitMQ服務器的端口號connectionFactory.setPort(5672);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername("pinkboy");// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword("123456");// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost("/");// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2.創建ChannelChannel channel = connection.createChannel();/***3.聲明一個隊列** @param channel RabbitMQ的通道,用于執行隊列操作** 此處使用了queueDeclare方法來聲明一個名為"hello"的隊列該方法的參數分別表示:* 1. 隊列名稱("hello"):指定要聲明的隊列的名稱* 2. true:表示該隊列是持久化的,意味著即使RabbitMQ服務重啟,隊列也會被保留* 3. false:表示該隊列不是排他的,意味著該隊列可以被所有通道共享* 4. false:表示該隊列不會在使用后自動刪除,需要手動刪除* 5. null:表示不設置額外的參數** 選擇這些參數值的原因可能是希望創建一個持久化的、共享的隊列,以便在不同的時間點和不同的消費者之間傳遞消息*/channel.queueDeclare("hello", true, false, false, null);// 4.開始從名為"hello"的隊列中消費消息channel.basicConsume("hello", true, new DefaultConsumer(channel) {/*** 處理接收到的消息** @param consumerTag 消費者標簽,用于標識消費者* @param envelope 包含消息路由信息的信封* @param properties 消息的屬性,如內容類型、內容編碼等* @param body 消息的主體內容,以字節數組形式表示* @throws IOException 如果處理消息時發生I/O錯誤*/@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});Thread.sleep(2000);//5.關閉資源channel.close();connection.close();}
}

運行結果

啟動生產者代碼:

觀察消息隊列

?啟動消費者代碼:

觀察消息隊列?

二、Work Queue(工作隊列)

2.1 概念

一個生產者P,多個消費者C1,C2 在多個消息的情況下,WorkQueue會將消息分派給不同的消費者,每個消費者都會接收到不同的消息

特點:消息不會重復,分配各不同的消費者

適用場景:集群環境中做異步處理

舉個例子:12306短息通知服務,訂票成功后,訂單消息會發送到RabbitMQ,短信服務從RabbitMQ中獲取訂單信息,并發送通知信息(在短信服務之間進行任務分配)

1.2 代碼實現

工作模式就是簡單模式的增強版 和簡單模式的區別就是 簡單模式就一個消費者,工作模式支持多個消費者接收消息,消費者之間是竟爭關系 每個消息只能被一個消費者接收

和簡單模式代碼差不多 為了展示多個消費者競爭的關系 生產者一次生產10條消息

常量類

package rabbitmq.constant;public class Constants {public static final String HOST = "8.136.108.248";public static final Integer PORT = 5672;public static final String USER_NAME = "pinkboy";public static final String PASSWORD = "123456";public static final String VIRTUAL_HOST = "/";//工作隊列模式public static final String WORK_QUEUE = "work_queue";}

生產者

package rabbitmq.work;import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws Exception {//1、建立連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost(Constants.HOST);// 設置RabbitMQ服務器的端口號connectionFactory.setPort(Constants.PORT);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername(Constants.USER_NAME);// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword(Constants.PASSWORD);// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2、創建通道Channel channel = connection.createChannel();//3、聲明交換機//4、聲明隊列/*** 聲明一個隊列** @param channel RabbitMQ的通道,用于執行隊列操作** 此處使用了queueDeclare方法來聲明一個名為"hello"的隊列該方法的參數分別表示:* 1. 隊列名稱("hello"):指定要聲明的隊列的名稱* 2. true:表示該隊列是持久化的,意味著即使RabbitMQ服務重啟,隊列也會被保留* 3. false:表示該隊列不是排他的,意味著該隊列可以被所有通道共享* 4. false:表示該隊列不會在使用后自動刪除,需要手動刪除* 5. null:表示不設置額外的參數** 選擇這些參數值的原因可能是希望創建一個持久化的、共享的隊列,以便在不同的時間點和不同的消費者之間傳遞消息*/channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);//5、發送消息// 循環發送消息到 RabbitMQ 的 "hello" 隊列中for (int i = 0; i < 10; i++) {// 構造消息內容String msg = "hello work queue ..." + i;/*** 參數1 表示交換機名稱,因為使用默認交換機,所以為空字符串* 參數2 表示隊列名稱* 參數3 :消息的屬性* 參數4:消息內容*/channel.basicPublish("", Constants.WORK_QUEUE, null, msg.getBytes());}System.out.println("消息發送成功!");//6、釋放資源channel.close();connection.close();}
}

消費者

兩個消費者的代碼是一樣的

消費者1

package rabbitmq.work;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer1 {public static void main(String[] args) throws Exception {//1、建立連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost(Constants.HOST);// 設置RabbitMQ服務器的端口號connectionFactory.setPort(Constants.PORT);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername(Constants.USER_NAME);// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword(Constants.PASSWORD);// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2.創建ChannelChannel channel = connection.createChannel();channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);channel.basicConsume(Constants.WORK_QUEUE, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");//5.關閉資源
//        channel.close();
//        connection.close();}
}

消費者2

package rabbitmq.work;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer2 {public static void main(String[] args) throws Exception {//1、建立連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost(Constants.HOST);// 設置RabbitMQ服務器的端口號connectionFactory.setPort(Constants.PORT);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername(Constants.USER_NAME);// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword(Constants.PASSWORD);// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2.創建ChannelChannel channel = connection.createChannel();channel.queueDeclare(Constants.WORK_QUEUE, true, false, false, null);channel.basicConsume(Constants.WORK_QUEUE, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer2 啟動成功!");//5.關閉資源
//        channel.close();
//        connection.close();}
}

?運行結果

生產者

生產者消息隊列

?

消費者

為了避免第一個啟動的消費者會將10條消息消費掉 需要先啟動兩個消費者,再去啟動生產者

消費者1

消費者2

觀察消息隊列?

可以看到管理頁面中有兩個消費者被顯示

三、Publish/Subscribe(發布/訂閱模式)

3.1 概念

Exchange: 交換機 (X).
作?: 生產者將消息發送到Exchange, 由交換機將消息按?定規則路由到?個或多個隊列中(上圖中?產 者將消息投遞到隊列中, 實際上這個在RabbitMQ中不會發生. )
RabbitMQ交換機有四種類型: fanout,direct, topic, headers, 不同類型有著不同的路由策略. AMQP協議里還有另外兩種類型, System和?定義, 此處不再描述.
Fanout:廣播,將消息交給所有綁定到交換機的隊列( Publish/Subscribe模式)
一個生產者P,多個消費者C1,C2,X代表交換機消息復制多份,每個消費者接收相同的消息
生產者發送一條消息,經過交換機轉發到多個不同的隊列,多個不同的隊列就有多個不同的消費者
適合場景:消息需要被多個消費者同時接收的場景.如:實時通知或者廣播消息

3.2 代碼實現

常量類

public class Constants {public static final String HOST = "8.136.108.248";public static final Integer PORT = 5672;public static final String USER_NAME = "pinkboy";public static final String PASSWORD = "123456";public static final String VIRTUAL_HOST = "/";//發布訂閱模式public static final String FANOUT_EXCHANGE = "fanout_exchange";public static final String FANOUT_QUEUE1 = "fanout_queue1";public static final String FANOUT_QUEUE2 = "fanout_queue2";
}

這個模式需要創建交換機,并綁定隊列和交換機?

//3、聲明交換機
/*** 參數1:交換機名稱* 參數2:交換機類型 Fanout類型 -> 廣播機制* 參數3:是否持久化*/
channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);

聲明隊列

//4、聲明隊列
/*** 參數1:隊列名稱* 參數2:是否持久化* 參數3:是否獨占隊列,該隊列只允許在該連接中訪問,如果連接關閉隊列則自動刪除,如果將此參數設置true可用于臨時隊列的創建* 參數4:是否自動刪除,當沒有生產者或者消費者使用此隊列,該隊列會自動刪除* 參數5:其他參數*/
channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);
channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);

綁定隊列和交換機

//5、交換機和隊列綁定
/*** 參數1:隊列名稱* 參數2:交換機名稱* 參數3:路由鍵,綁定規則 如果交換機類型為fanout類型,routingKey設置為空字符串*/
channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");
channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");

生產者

package rabbitmq.fanout;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;public class Producer {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、打開信道Channel channel = connection.createChannel();//3、聲明交換機/*** 參數1:交換機名稱* 參數2:交換機類型 Fanout類型 -> 廣播機制* 參數3:是否持久化*/channel.exchangeDeclare(Constants.FANOUT_EXCHANGE, BuiltinExchangeType.FANOUT, true);//4、聲明隊列/*** 參數1:隊列名稱* 參數2:是否持久化* 參數3:是否獨占隊列,該隊列只允許在該連接中訪問,如果連接關閉隊列則自動刪除,如果將此參數設置true可用于臨時隊列的創建* 參數4:是否自動刪除,當沒有生產者或者消費者使用此隊列,該隊列會自動刪除* 參數5:其他參數*/channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);//5、交換機和隊列綁定/*** 參數1:隊列名稱* 參數2:交換機名稱* 參數3:路由鍵,綁定規則 如果交換機類型為fanout類型,routingKey設置為空字符串*/channel.queueBind(Constants.FANOUT_QUEUE1, Constants.FANOUT_EXCHANGE, "");channel.queueBind(Constants.FANOUT_QUEUE2, Constants.FANOUT_EXCHANGE, "");//6、發布消息String msg = "hello fanout ...";channel.basicPublish(Constants.FANOUT_EXCHANGE, "", null, msg.getBytes());System.out.println("消息發送成功!");//7、釋放資源channel.close();connection.close();}
}

消費者

消費者1

package rabbitmq.fanout;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer1 {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、創建信道Channel channel = connection.createChannel();//3、聲明隊列channel.queueDeclare(Constants.FANOUT_QUEUE1, true, false, false, null);//4、消費消息channel.basicConsume(Constants.FANOUT_QUEUE1, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");}
}

消費者2

package rabbitmq.fanout;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer2 {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、創建信道Channel channel = connection.createChannel();//3、聲明隊列channel.queueDeclare(Constants.FANOUT_QUEUE2, true, false, false, null);//4、消費消息channel.basicConsume(Constants.FANOUT_QUEUE2, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer2 啟動成功!");}
}

運行結果

啟動生產者

觀察消息隊列

fanout_queue1 和 fanout_queue2 分別有了1條消息

?Exchange中多了隊列的綁定關系

啟動兩個消費者

?

觀察消息隊列

四、Routing(路由模式)

4.1 概念

路由模式是發布訂閱模式的變種,在發布訂閱基礎上,增加路由key

發布訂閱模式是無條件的將所有消息分發給所有的消費者,路由模式是Exchange根據RoutingKey的規則,將數據篩選后發給對應的消費者隊列

適合場景:需要根據特定規則分發消息的場景

比如系統打印日志, 日志等級分為error, warning, info,debug, 就可以通過這種模式,把不同的日志發
送到不同的隊列, 最終輸出到不同的?件
Exchange(交換機)只負責轉發消息,不具備存儲消息的能力,因此如果沒有任何隊列與Exchange綁定,或者沒有符合路由規則的隊列,那么消息就會丟失
?
RoutingKey:路由鍵.生產者將消息發給交換器時,指定的一個字符串,用來告訴交換機應該如何處理這個消息.
?
BindingKey:綁定.RabbitMQ中通過Binding(綁定)將交換器與隊列關聯起來,在綁定的時候一般會指定一個BindingKey,這樣RabbitMQ就知道如何正確地將消息路由到隊列了.

隊列和交換機的綁定,不能是任意的綁定了,而是要制定了一個BindKey(RoutingKey的一種)消息的發送方在向Exchange發送消息時也需要指定消息的RoutingKey

Exchange也不再把消息交給每一個綁定的key,而是根據消息的RountingKey進行判斷,只要隊列的BindingKey和發送消息的RoutingKey完全一致,才會接收到消息

創建交換機,定義交換機類型為BuiltinExchangeType.DIRECT

4.2 代碼實現

Constants類

public class Constants {public static final String HOST = "8.136.108.248";public static final Integer PORT = 5672;public static final String USER_NAME = "pinkboy";public static final String PASSWORD = "123456";public static final String VIRTUAL_HOST = "/";//路由模式public static final String DIRECT_EXCHANGE = "direct.exchange";public static final String DIRECT_QUEUE1 = "direct.queue1";public static final String DIRECT_QUEUE2 = "direct.queue2";
}

生產者

package rabbitmq.direct;import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;public class Producer {public static void main(String[] args) throws Exception {//1. 建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2.  創建信道Channel channel = connection.createChannel();//3.  聲明交換機/*** 參數1:交換機名稱* 參數2:交換機類型* 參數3:是否持久化* 參數4:是否自動刪除* 參數5:其他參數*/channel.exchangeDeclare(Constants.DIRECT_EXCHANGE, "direct", true, false, null);//4.  聲明隊列/*** 參數1:隊列名稱* 參數2:是否持久化* 參數3:是否獨占隊列,該隊列只允許在該連接中訪問,如果連接關閉隊列則自動刪除,如果將此參數設置true可用于臨時隊列的創建* 參數4:是否自動刪除,隊列不再使用時是否自動刪除此隊列,如果將此參數和參數2設置為true就可以實現臨時隊列(隊列不用了就自動刪除)* 參數5:其他參數*/channel.queueDeclare(Constants.DIRECT_QUEUE1, true, false, false, null);channel.queueDeclare(Constants.DIRECT_QUEUE2, true, false, false, null);//5、交換機和隊列綁定/*** 參數1:隊列名稱* 參數2:交換機名稱* 參數3:路由鍵,綁定規則*/channel.queueBind(Constants.DIRECT_QUEUE1, Constants.DIRECT_EXCHANGE, "a");channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "a");channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "b");channel.queueBind(Constants.DIRECT_QUEUE2, Constants.DIRECT_EXCHANGE, "c");//6、發布消息String msg_a = "hello direct my routingKey is a...";channel.basicPublish(Constants.DIRECT_EXCHANGE, "a", null, msg_a.getBytes());String msg_b = "hello direct my routingKey is b...";channel.basicPublish(Constants.DIRECT_EXCHANGE, "b", null, msg_b.getBytes());String msg_c = "hello direct my routingKey is c...";channel.basicPublish(Constants.DIRECT_EXCHANGE, "c", null, msg_c.getBytes());System.out.println("消息發送成功!");//7、釋放資源channel.close();connection.close();}
}

消費者

package rabbitmq.direct;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer1 {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、創建信道Channel channel = connection.createChannel();//3、聲明隊列channel.queueDeclare(Constants.DIRECT_QUEUE1, true, false, false, null);//4、消費消息channel.basicConsume(Constants.DIRECT_QUEUE1, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");}
}
package rabbitmq.direct;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer2 {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、創建信道Channel channel = connection.createChannel();//3、聲明隊列channel.queueDeclare(Constants.DIRECT_QUEUE2, true, false, false, null);//4、消費消息channel.basicConsume(Constants.DIRECT_QUEUE2, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");}
}

運行結果

啟動生產者

觀察消息隊列界面

exchange下隊列和Routing Key的綁定關系

啟動消費者

觀察消息隊列界面

五、Topics(通配符模式)

5.1 概念

在路由模式上進行了升級,在routingKey的基礎上,增加可通配符的功能,適之更加靈活

Topic和Routing的基本原同,即:生產者將消息發送給交換機,交換機根據RoutingKey將消息轉發給RoutingKey匹配的隊列,類似于正則表達式的方式來定義RoutingKey的模式.?

適合場景:需要靈活匹配和過濾消息的場景

Topic和Routing模式的區別:

1、topic模式使用的交換機類型為topic(Rounting模式使用的交換機類型為direct)

2、topic類型的交換機在匹配規則上進行了擴展,BingingKey支持通配符匹配(direct類型的將換季路由規則是BingKey和RoutingKey完全匹配)

在topic類型的交換機在匹配規則上有些要求:

1.RoutingKey是一系列由點(.)分隔的單詞,比如“stock.usd.nyse”,"nyse.vmw","quick.organge.rabbit"

2. BingdingKey和RountingKey,也是點(.)分隔的字符串

3. BingdingKey中可以存在兩種特殊的字符串,用于模糊匹配

? * 表示一個單詞

? # 表示0-N個單詞

舉個例子:

Binding Key 為"d.a.b" 會同時路由到Q1 和Q2
Binding Key 為"d.a.f" 會路由到Q1
Binding Key 為"c.e.f" 會路由到Q2
Binding Key 為"d.b.f" 會被丟棄, 或者返回給?產者(需要設置mandatory參數)

5.2 代碼實現

Constants類

public class Constants {public static final String HOST = "8.136.108.248";public static final Integer PORT = 5672;public static final String USER_NAME = "pinkboy";public static final String PASSWORD = "123456";public static final String VIRTUAL_HOST = "/";//通配符模式public static final String TOPIC_EXCHANGE = "topic.exchange";public static final String TOPIC_QUEUE1 = "topic.queue1";public static final String TOPIC_QUEUE2 = "topic.queue2";}

生產者

創建交換機類型為BuiltinExchangeType.TOPIC

?//3.聲明交換機

channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC, true, false, null);
?

聲明隊列

//4.聲明隊列
channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);
channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);

?綁定交換機和隊列

//5.綁定交換機和隊列
channel.queueBind(Constants.TOPIC_QUEUE1, Constants.TOPIC_EXCHANGE, "*.a.*");
channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "*.*.b");
channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "c.#");
package rabbitmq.topic;import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {//1.建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2.開啟信道Channel channel = connection.createChannel();//3.聲明交換機channel.exchangeDeclare(Constants.TOPIC_EXCHANGE, BuiltinExchangeType.TOPIC, true, false, null);//4.聲明隊列channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);//5.綁定交換機和隊列channel.queueBind(Constants.TOPIC_QUEUE1, Constants.TOPIC_EXCHANGE, "*.a.*");channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "*.*.b");channel.queueBind(Constants.TOPIC_QUEUE2, Constants.TOPIC_EXCHANGE, "c.#");//6、發布消息String msg_a = "hello topic my routingKey is ae.a.f...";channel.basicPublish(Constants.TOPIC_EXCHANGE, "ae.a.f", null, msg_a.getBytes());String msg_b = "hello topic my routingKey is ef.a.b...";channel.basicPublish(Constants.TOPIC_EXCHANGE, "ef.a.b", null, msg_b.getBytes());String msg_c = "hello topic my routingKey is c...";channel.basicPublish(Constants.TOPIC_EXCHANGE, "c.ef.d", null, msg_c.getBytes());System.out.println("消息發送成功!");}
}

消費者

package rabbitmq.topic;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;public class Consumer1 {public static void main(String[] args) throws Exception {//1、建立連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost(Constants.HOST);// 設置RabbitMQ服務器的端口號connectionFactory.setPort(Constants.PORT);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername(Constants.USER_NAME);// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword(Constants.PASSWORD);// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2.創建ChannelChannel channel = connection.createChannel();channel.queueDeclare(Constants.TOPIC_QUEUE1, true, false, false, null);channel.basicConsume(Constants.TOPIC_QUEUE1, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");}
}
public class Consumer2 {public static void main(String[] args) throws Exception {//1、建立連接// 創建一個ConnectionFactory實例來配置RabbitMQ連接ConnectionFactory connectionFactory = new ConnectionFactory();// 設置RabbitMQ服務器的主機地址connectionFactory.setHost(Constants.HOST);// 設置RabbitMQ服務器的端口號connectionFactory.setPort(Constants.PORT);// 設置登錄RabbitMQ服務器的用戶名connectionFactory.setUsername(Constants.USER_NAME);// 設置登錄RabbitMQ服務器的密碼connectionFactory.setPassword(Constants.PASSWORD);// 設置RabbitMQ服務器的虛擬主機connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);// 使用ConnectionFactory創建一個新的連接Connection connection = connectionFactory.newConnection();//2.創建ChannelChannel channel = connection.createChannel();channel.queueDeclare(Constants.TOPIC_QUEUE2, true, false, false, null);channel.basicConsume(Constants.TOPIC_QUEUE2, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 打印消息的主體內容System.out.println("body:" + new String(body));}});System.out.println("Consumer1 啟動成功!");}
}

運行結果

生產者

觀察消息隊列界面

消費者

六、RPC(RPC通信)了解

6.1 概念

RPC(Remote Procedure Call) 即遠程調用 它是一種通過網絡從遠程計算機上請求服務,而不是需要了解底層網絡的技術,類似HTTP遠程調用

RabbitMQ實現RPC通信的過程,大概率是通過兩個隊列實現一個可回調的過程

大概流程

1.客戶端發消息到一個指定的隊列,并在消息屬性中設置replyTo字段,這個字段指定一個回調隊列,服務端處理后,會把響應結果發送到這個隊列

2.服務端接收到請求后,處理請求并發送響應消息到replyTo指定的回調隊列

3.客戶端在回調隊列上等待響應消息,一旦收到響應,客戶端會檢查消息的correlationId屬性,以確保它是所期望的響應

客戶端:

1 發送請求(攜帶replyTo,CorrelationID)

2 接收響應(校驗correlationID)

服務端:

1 接受請求,進行響應

2 發送響應(按照客戶端指定的replyTo,設置correlationID)

6.2 代碼實現

客戶端代碼編寫

1、聲明兩個隊列 RPC_REQUEST_QUEUE和RPC_RESPONSE_QUEUE,聲明本次請求的唯一標志correlationID

2、將RPC_RESPONSE_QUEUE和correlationID配置到要發送的消息隊列中

3、使用阻塞隊列來阻塞當前進程,監聽回調隊列中的消息,把請求放到阻塞隊列中

4、阻塞隊列有消息后,主線程被喚醒,打印返回內容

Constants類

public class Constants {public static final String HOST = "8.136.108.248";public static final Integer PORT = 5672;public static final String USER_NAME = "pinkboy";public static final String PASSWORD = "123456";public static final String VIRTUAL_HOST = "/";//rpc 模式public static final String RPC_REQUEST_QUEUE = "rpc.request.queue";public static final String RPC_RESPONSE_QUEUE = "rpc.response.queue";}

客戶端代碼

package rabbitmq.rpc;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;/*** rpc 客戶端* 1. 發送請求* 2. 等待響應*/
public class RpcClient {public static void main(String[] args) throws Exception { //1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、開啟信道Channel channel = connection.createChannel();channel.queueDeclare(Constants.RPC_REQUEST_QUEUE, true, false, false, null);channel.queueDeclare(Constants.RPC_RESPONSE_QUEUE, true, false, false, null);//3、發送請求String msg = "hello rpc...";//設置請求唯一標識//設置請求的相關屬性// 生成一個唯一的關聯ID,用于跟蹤請求和響應String correlationID = UUID.randomUUID().toString();// 創建并配置AMQP基本屬性,設置消息的關聯ID和回復隊列AMQP.BasicProperties prop = new AMQP.BasicProperties().builder().correlationId(correlationID).replyTo(Constants.RPC_RESPONSE_QUEUE).build();// 發布消息到指定的請求隊列,攜帶配置的屬性和消息體channel.basicPublish("", Constants.RPC_REQUEST_QUEUE, prop, msg.getBytes());//4、接收響應//使用阻塞隊列,存儲響應信息final ArrayBlockingQueue<String> response = new ArrayBlockingQueue<>(1);channel.basicConsume(Constants.RPC_RESPONSE_QUEUE, true, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {String respMsg = new String(body);System.out.println("接收到回調消息:" + respMsg);if (correlationID.equals(properties.getCorrelationId())) {//如果correlationID校驗一致,則將響應信息保存在response中response.offer(respMsg);}}});/*** 阻塞等待響應*/String take = response.take();System.out.println("[RPC Client 響應結果]:" + take);}
}

編寫服務器代碼

package rabbitmq.rpc;import com.rabbitmq.client.*;
import rabbitmq.constant.Constants;import java.io.IOException;/*** 1、接受請求* 2、發送響應*/
public class RpcServer {public static void main(String[] args) throws Exception {//1、建立連接ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);Connection connection = connectionFactory.newConnection();//2、開啟信道Channel channel = connection.createChannel();//3、接受請求channel.basicConsume(Constants.RPC_REQUEST_QUEUE, false, new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {String request = new String(body);System.out.println("接收到請求:" + request);String response = "針對request:" + request + ",響應成功";AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().correlationId(properties.getCorrelationId()).build();channel.basicPublish("", Constants.RPC_RESPONSE_QUEUE, basicProperties, response.getBytes());channel.basicAck(envelope.getDeliveryTag(), false);}});}
}

運行結果:

啟動客戶端

觀察消息隊列界面

啟動服務器端

客戶端輸出消息?

?觀察消息隊列界面

七、Publish Confirms(發布確認模式)

作為消息中間件,都會面臨消息丟失的問題

消息丟失大概分為三種情況

1、生產者問題 因為應用程序故、障網絡抖動等原因,生產者沒有成功想broker發送消息

2、消息中間件自身問題,生產者成功發送給Broker 但是Broker沒有把消息保存好,導致消息丟失

3、消費者問題,Broker發送到消費者,消費者在消費時,因沒處理好,導致消費者失敗的消息從隊列中刪除了

針對問題1 可以采用發確認(Publisher Cofirms)機制實現

生產者將信道設置成confirm(確認)模式,一但信道進入confirm模式,所有在該信道上面發布的消息都是會被指派一個唯一的ID(從1開始),一但消息被投遞到所有匹配的隊列之后,RabbitMq就會發送一個確認給生產者(包括消息的唯一ID)這就使得生產者知道消息已經正確到達目的隊列了,如果消息和隊列是可持久化的,那么消息確認會在將消息寫入到磁盤之后發出,broker回傳給生產者的確認消息中

deliveryTag包包含了消息的序號,此外broker也可以設置channel basicAck方法中的multiple參數,表示到這個序號之前的所有消息都已經得到處理

發送方確認機制最大的好處是他是異步的,生產者可以同時發布消息和等待信道返回確認消息

1、當消息最終得到確認之后,生產者可以通過回調方法來處理該確認消息

2、如果RabbitMQ因為自身內部錯誤導致消息丟失,就會發送一條nack(Basic.Nack)命令,生產者同樣可以在回調方法中處理該nack命令

使用發送確認機制,必須要將信道設置成confirm(確認)模式

package rabbitmq.comfirms;import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.constant.Constants;import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;public class PublisherConfirms {private static final Integer MESSAGE_COUNT = 200;static Connection createConnection() throws Exception {ConnectionFactory connectionFactory = new ConnectionFactory();connectionFactory.setHost(Constants.HOST);connectionFactory.setPort(Constants.PORT);connectionFactory.setUsername(Constants.USER_NAME);connectionFactory.setPassword(Constants.PASSWORD);connectionFactory.setVirtualHost(Constants.VIRTUAL_HOST);return connectionFactory.newConnection();}public static void main(String[] args) throws Exception {// 發布確認publishingMessagesIndividually();// 批量發布確認publishingMessagesInBatchs();// 異步發布確認publishingMessagesAsynchronously();}private static void publishingMessagesAsynchronously() throws Exception {try (Connection connection = createConnection()) {//1 、創建信道Channel channel = connection.createChannel();//2、設置信道為confirm模式channel.confirmSelect();//3、聲明隊列channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE3, true, false, false, null);//4、監聽confirm消息//集合中存儲的是未確認的消息IDlong start = System.currentTimeMillis();SortedSet<Long> confirmSeqNo = Collections.synchronizedSortedSet(new TreeSet<>());channel.addConfirmListener(new ConfirmListener() {@Overridepublic void handleAck(long deliveryTag, boolean multiple) throws IOException {if (multiple) {confirmSeqNo.headSet(deliveryTag + 1).clear();} else {confirmSeqNo.remove(deliveryTag);}}@Overridepublic void handleNack(long deliveryTag, boolean multiple) throws IOException {if (multiple) {confirmSeqNo.headSet(deliveryTag + 1).clear();} else {confirmSeqNo.remove(deliveryTag);}//業務需要根據實際場景進行處理,比如重發}});//5.發送消息for (int i = 0; i < MESSAGE_COUNT; i++) {String msg = "hello publish confirms" + i;long seqNO = channel.getNextPublishSeqNo();channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE3, null, msg.getBytes());confirmSeqNo.add(seqNO);}while (!confirmSeqNo.isEmpty()) {Thread.sleep(10);}long end = System.currentTimeMillis();System.out.printf("異步確認策略,消息條數:%d,耗時:%d ms \n", MESSAGE_COUNT, (end - start));}}private static void publishingMessagesInBatchs() throws Exception {try (Connection connection = createConnection()) {//1 、創建信道Channel channel = connection.createChannel();//2、設置信道為confirm模式channel.confirmSelect();//3、聲明隊列channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE2, true, false, false, null);//4、發送消息long start = System.currentTimeMillis();int batchSize = 100;int outStandingMessageCount = 0;for (int i = 0; i < MESSAGE_COUNT; i++) {String msg = "hello publish confirms" + i;channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE2, null, msg.getBytes());outStandingMessageCount++;if (outStandingMessageCount == batchSize) {//6、等待確認channel.waitForConfirms(5000);outStandingMessageCount = 0;}}if (outStandingMessageCount > 0) {channel.waitForConfirms(5000);}long end = System.currentTimeMillis();System.out.printf("批量確認策略,消息條數:%d,耗時:%d ms \n", MESSAGE_COUNT, (end - start));}}private static void publishingMessagesIndividually() throws Exception {try (Connection connection = createConnection()) {//1 、創建信道Channel channel = connection.createChannel();//2、設置信道為confirm模式channel.confirmSelect();//3、聲明隊列channel.queueDeclare(Constants.PUBLISH_CONFIRM_QUEUE1, true, false, false, null);//4、發送消息long start = System.currentTimeMillis();for (int i = 0; i < MESSAGE_COUNT; i++) {String msg = "hello publish confirms" + i;channel.basicPublish("", Constants.PUBLISH_CONFIRM_QUEUE1, null, msg.getBytes());//5、等待確認channel.waitForConfirms(5000);}long end = System.currentTimeMillis();System.out.printf("單獨確認策略,消息條數:%d,耗時:%d ms \n", MESSAGE_COUNT, (end - start));} catch (IOException e) {throw new RuntimeException(e);} catch (TimeoutException | InterruptedException e) {throw new RuntimeException(e);}}
}

publishing Messages Individually(單獨確認)

觀察上面代碼, 會發現這種策略是每發送?條消息后就調用channel.waitForConfirms方法,之后等待服務端的確認, 這實際上是?種串行同步等待的方式. 尤其對于持久化的消息來說, 需要等待消息確
認存儲在磁盤之后才會返回(調用Linux內核的fsync方法). 但是發布確認機制是?持異步的. 可以?邊發送消息, ?邊等待消息確認。

Publishing Messages in Batches(批量確認)

?

相比于單獨確認策略, 批量確認極大地提升了confirm的效率, 缺點是出現Basic.Nack或者超時時, 我們 不清楚具體哪條消息出了問題. 客?端需要將這?批次的消息全部重發, 這會帶來明顯的重復消息數量.
當消息經常丟失時,批量確認的性能應該是不升反降的.

Handling Publisher Confirms Asynchronously(異步確認)

異步confirm方法的編程實現最為復雜. Channel 接?提供了?個方法addConfirmListener. 這個方法
可以添加ConfirmListener 回調接口.
ConfirmListener 接口中包含兩個方法: handleAck(long deliveryTag, boolean multiple) 和 handleNack(long deliveryTag, boolean multiple) , 分別對應處理 RabbitMQ發送給?產者的ack和nack.
deliveryTag 表示 發送消息的序號. multiple 表示 是否批量確認.
我們需要為每?個Channel 維護?個已發送消息的序號集合. 當收到RabbitMQ的confirm 回調時, 從集
合中刪除對應的消息. 當Channel開啟confirm模式后, channel上發送消息都會附帶?個從1開始遞增的
deliveryTag序號. 我們可以使?SortedSet 的有序性來維護這個已發消息的集合.
1. 當收到ack時, 從序列中刪除該消息的序號. 如果為批量確認消息, 表示小于等于當前序號
deliveryTag的消息都收到了, 則清除對應集合
2. 當收到nack時, 處理邏輯類似, 不過 需要結合具體的業務情況, 進型消息重發等操作.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/903433.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/903433.shtml
英文地址,請注明出處:http://en.pswp.cn/news/903433.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

厚銅PCB鉆孔工藝全解析:從參數設置到孔壁質量的關鍵控制點

在現代電子設備中&#xff0c;厚銅PCB&#xff08;印刷電路板&#xff09;扮演著至關重要的角色。它們不僅為電子元件提供了支撐&#xff0c;還實現了電路之間的連接。然而&#xff0c;在生產厚銅PCB時&#xff0c;鉆孔是一個關鍵環節。本文將為您介紹厚銅PCB生產中鉆孔的科普知…

缺口拼圖,非線性坐標關聯

繼上一篇文章&#xff0c; 歡迎一起交流探討 https://t.zsxq.com/GEIze

OTA(Over-The-Air)升級

簡介&#xff1a; OTA&#xff08;Over-the-Air&#xff09;是一種通過無線方式進行數據傳輸和更新的技術&#xff0c;通常用于電子設備&#xff08;如智能手機、汽車、物聯網設備等&#xff09;的軟件、固件或配置更新。OTA可以在設備與服務器之間進行遠程傳輸&#xff0c;用戶…

fastapi和flaskapi有什么區別

FastAPI 和 Flask 都是 Python 的 Web 框架&#xff0c;但設計目標和功能特性有顯著差異。以下是它們的核心區別&#xff1a; 1. ?性能與異步支持? ?FastAPI? 基于 ?Starlette?&#xff08;高性能異步框架&#xff09;和 ?Pydantic?&#xff08;數據校驗庫&#xff09;…

RCS認證是什么?RCS認證的好處?RCS認證所需要的資料

1. RCS&#xff08;Recycled Claim Standard&#xff09;認證 定義&#xff1a;由 Textile Exchange&#xff08;紡織品交易所&#xff09; 制定的國際標準&#xff0c;用于驗證產品中回收材料&#xff08;如再生纖維、塑料、金屬等&#xff09;的含量和供應鏈的可追溯性&…

10 基于Gazebo和Rviz實現導航仿真,包括SLAM建圖,地圖服務,機器人定位,路徑規劃

在9中我們已經實現了機器人的模塊仿真&#xff0c;現在要在這個基礎上實現SLAM建圖&#xff0c;地圖服務&#xff0c;機器人定位&#xff0c;路徑規劃 1. 還是在上述機器人的工作空間下&#xff0c;新建功能包&#xff08;nav&#xff09;&#xff0c;導入依賴 gmapping ma…

OpenGL----OpenGL紋理與紋理緩存區

在現代計算機圖形學中,紋理(Texture)是一個至關重要的概念。它不僅可以為幾何體表面添加細節和真實感,還可以用于實現各種復雜的視覺效果和數據處理。在OpenGL中,紋理的應用范圍非常廣泛,從基本的顏色映射到高級的陰影映射、環境映射等。本文將深入探討OpenGL紋理與紋理緩…

Scikit-learn工具介紹與數據集

一、Scikit-learn簡介與安裝 Scikit-learn是Python中最流行的機器學習庫之一&#xff0c;它提供了簡單高效的數據挖掘和數據分析工具。 Python語言機器學習工具 Scikit-learn包括許多智能的機器學習算法的實現 Scikit-learn文檔完善&#xff0c;容易上手&#xff0c;豐富的A…

Byte-Buddy系列 - 第4講 byte-buddy無法讀取到SpringBoot Jar中的類

目錄 一、問題描述二、原因分析三、解決方案1&#xff08;推薦&#xff09;&#xff1a;獲取線程上下文中的類加載器擴展 四、解決方案2&#xff1a;自定義SpringBoot類加載器 一、問題描述 在使用Byte-Buddy中的TypePool對類進行擴展后&#xff0c;在本地開發集成環境&#x…

AutogenStudio使用

官網介紹&#xff1a;https://microsoft.github.io/autogen/stable/ Autogen是什么&#xff1f; AutoGen 是由微軟開發的一個開源框架&#xff0c;旨在通過 多智能體協作&#xff08;Multi-Agent Collaboration&#xff09; 實現復雜的任務自動化。它的核心思想是讓多個 AI 代…

Vue3 Echarts 3D圓形柱狀圖實現教程以及封裝一個可復用的組件

文章目錄 前言一、實現原理二、series ——type: "pictorialBar" 簡介2.1 常用屬性 三、代碼實戰3.1 封裝一個echarts通用組件 echarts.vue3.2 首先實現一個基礎柱狀圖3.3 添加上下2個橢圓面3.4 進階封裝一個可復用的3D圓形柱狀圖組件 總結 前言 在前端開發的數據可視…

yolov8中train、test、val

說明yolov8中train、test、val是什么意思&#xff0c;是什么作用呢&#xff1f;詳細介紹使用yolov8進行實例分割&#xff0c;我應該如何制作我的數據集呢&#xff1f; 1. YOLOv8中的train、val、test是什么意思&#xff1f;作用是什么&#xff1f; 在YOLOv8&#xff08;由Ultr…

借助Spring AI實現智能體代理模式:從理論到實踐

借助Spring AI實現智能體代理模式&#xff1a;從理論到實踐 前言 在人工智能領域&#xff0c;大語言模型&#xff08;LLM&#xff09;的應用愈發廣泛&#xff0c;如何高效構建基于LLM的系統成為眾多開發者關注的焦點。Anthropic的研究報告《構建高效代理》為我們提供了新的思…

【學習筆記】計算機操作系統(二)—— 進程的描述與控制

第二章 進程的描述與控制 文章目錄 第二章 進程的描述與控制2.1 前趨圖和程序執行2.1.1 前趨圖2.1.2 程序順序執行2.1.3 程序并發執行 2.2 進程的描述2.2.1 進程的定義和特征2.2.2 進程的基本狀態及轉換2.2.3 掛起操作和進程狀態的轉換2.2.4 進程管理中的數據結構 2.3 進程控制…

具身智能之強化學習

在具身智能&#xff08;Embodied AI&#xff09;中&#xff0c;強化學習&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;是一種非常核心的學習方法。它讓智能體&#xff08;agent&#xff09;通過與環境交互&#xff0c;不斷試錯&#xff0c;學習完成任務的策略…

go打印金字塔

需求 打印空心金字塔 解析 // * // * * // * * * // * * * *// 看成由星號、空格組成的矩形&#xff1a; // 1 1 1 0 // 2 3 2 1 // 3 5 3 2 // 4 7 4 3// 層數&#xff1a;n // 每層總元素數&#xff1a;2n-1 // 每星號數&#xff1a;n // 每層空格數&am…

C語言教程(二十二):C 語言頭文件詳解

一、頭文件的定義與形式 頭文件一般具有 .h 擴展名&#xff0c;它主要用來存放函數聲明、宏定義、結構體和共用體的定義、全局變量的聲明等內容。在C語言程序里&#xff0c;可借助 #include 預處理指令把這些頭文件包含到源文件中。 二、頭文件的作用 2.1 函數聲明 頭文件可對…

數據庫day-08

一、實驗名稱和性質 刪除修改數據 驗證 設計 二、實驗目的 1&#xff0e;掌握數據操作-- 刪除、修改&#xff1b; 三、實驗的軟硬件環境要求 硬件環境要求&#xff1a; PC機&#xff08;單機&#xff09; 使用的軟件名稱、版本號以及模塊&#xff1a; Windows 10&#x…

JAVA中Spring全局異常處理@ControllerAdvice解析

一、ControllerAdvice基礎概念 1. 什么是ControllerAdvice&#xff1f; ControllerAdvice是Spring 3.2引入的注解&#xff0c;用于定義全局控制器增強組件&#xff0c;主要功能包括&#xff1a; 全局異常處理&#xff08;最常用&#xff09;全局數據綁定全局數據預處理 2. …

開放平臺架構方案- GraphQL 詳細解釋

GraphQL 詳細解釋 GraphQL 是一種用于 API 的查詢語言&#xff0c;由 Facebook 開發并開源&#xff0c;旨在提供一種更高效、靈活且強大的數據獲取和操作方式。它與傳統的 REST API 有顯著不同&#xff0c;通過類型系統和靈活的查詢能力&#xff0c;解決了 REST 中常見的過度獲…