From: 011netservice@gmail.com Date: 2023-04-02 Subject: Readme-RabbitMQ.txt 章節 #### 安裝 RabbitMQ Docker #### 安裝 MassTransit1 RabbitMQ Docker #### RabbitMQ 基本概念 #### RabbitMQ Docker 常用指令 #### RabbitMQ系列介紹 歡迎來信交流, 訂購軟體需求. 以下 #### 標記段落, **** 標記常用(流程、設定、備忘) RabbitMQ: https://www.rabbitmq.com/ Erlang/OTP: https://www.erlang.org/downloads RabbitMQ: https://www.rabbitmq.com/download.html Enable Management API: https://www.rabbitmq.com/management.html 直接安裝 RabbitMQ Docker 比較快: 參考 ( #### 安裝 RabbitMQ Docker) 詳細內容 #### 安裝 RabbitMQ Docker rabbitmq DOCKER OFFICIAL IMAGE: https://hub.docker.com/_/rabbitmq □ pull rabbitmq 建議指令: $ docker pull rabbitmq:3-management 挑一個 Dockerfile: rabbitmq: 安裝, 選用 version 為 management 較簡單, 其他指令參考: pull from RabbitMQ DOCKER OFFICIAL IMAGE: $ docker pull rabbitmq:3 $ docker pull rabbitmq:latest pull Management plugin pre-installed: $ docker pull rabbitmq:management $ docker pull rabbitmq:3-management (目前版本) □ Running the daemon 建議指令: $ docker run -d -p 15672:15672 -p 5672:5672 --hostname host1 --name rabbit1 rabbitmq:3-management 參數說明: -d, --detach Run container in background and print container ID -h, --hostname 資料儲存名. if not explicitly specify hostname, then a random hostname will be used to track RabbitMQ's data. --name Name of the local image. rabbitmq:3, rabbitmq:management Dockerfile: rabbitmq: This will start a RabbitMQ container listening on the default port of 5672: $ docker run -d --hostname my-rabbit --name some-rabbit rabbitmq:3 map port 15672 for the management web app and port 5672 for the message broker: $ docker run --rm -it -p 15672:15672 -p 5672:5672 rabbitmq:3-management map port 8080 for the management web app: $ docker run -d --hostname my-rabbit --name some-rabbit -p 8080:15672 rabbitmq:3-management 以上執行成功後, 可檢查執行紀錄: $ docker logs rabbit1 $ docker logs $ docker logs some-rabbit =INFO REPORT==== 6-Jul-2015::20:47:02 === node : rabbit@my-rabbit home dir : /var/lib/rabbitmq config file(s) : /etc/rabbitmq/rabbitmq.config cookie hash : UoNOcDhfxW9uoZ92wh6BjA== log : tty sasl log : tty database dir : /var/lib/rabbitmq/mnesia/rabbit@my-rabbit (hostname 會在這裡) 或是測試 RabbitMQ Docker, 登入 rabbitmq broker: 安裝成功後, 可瀏覽網頁到 http://localhost:15672 測試登入. 登入帳號guest, 密碼guest, 這組預設帳密只能在本機 localhost 使用. 15672 is the default port for RabbitMQ GUI, 5672 for RabbitMQ message broker. □ Setting default user and password If you wish to change the default username and password of guest / guest, you can do so with the RABBITMQ_DEFAULT_USER and RABBITMQ_DEFAULT_PASS environmental variables. These variables were available previously in the docker-specific entrypoint shell script but are now available in RabbitMQ directly. $ docker run -d --hostname my-rabbit --name some-rabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=password rabbitmq:3-management You can then go to http://localhost:8080 or http://host-ip:8080 in a browser and use user/password to gain access to the management console □ Memory Limits RabbitMQ contains functionality which explicitly tracks and manages memory usage, and thus needs to be made aware of cgroup-imposed limits (e.g. docker run --memory=..). The upstream configuration setting for this is vm_memory_high_watermark in rabbitmq.conf, and it is described under "Memory Alarms" in the documentation. If you set a relative limit via vm_memory_high_watermark.relative, then RabbitMQ will calculate its limits based on the host's total memory and not the limit set by the contianer runtime. □ Erlang Cookie See the RabbitMQ "Clustering Guide" for more information about cookies and why they're necessary. For setting a consistent cookie (especially useful for clustering but also for remote/cross-container administration via rabbitmqctl), provide a cookie file (default location of /var/lib/rabbitmq/.erlang.cookie). For example, you can provide the cookie via a file (such as with Docker Secrets): docker service create ... --secret source=my-erlang-cookie,target=/var/lib/rabbitmq/.erlang.cookie ... rabbitmq □ Management Plugin There is a second set of tags provided with the management plugin installed and enabled by default, which is available on the standard management port of 15672, with the default username and password of guest / guest: $ docker run -d --hostname my-rabbit --name some-rabbit rabbitmq:3-management You can access it by visiting http://container-ip:15672 in a browser or, if you need access outside the host, on port 8080: $ docker run -d --hostname my-rabbit --name some-rabbit -p 8080:15672 rabbitmq:3-management You can then go to http://localhost:8080 or http://host-ip:8080 in a browser. □ 測試 RabbitMQ Docker 登入 rabbitmq broker 安裝成功後, 可瀏覽網頁到 http://localhost:15672 測試登入. 登入帳號guest, 密碼guest, 這組預設帳密只能在本機 localhost 使用. 15672 is the default port for RabbitMQ GUI, 5672 for RabbitMQ message broker. □ Enabling Plugins Creating a Dockerfile will have them enabled at runtime. To see the full list of plugins present on the image rabbitmq-plugins list FROM rabbitmq:3.8-management RUN rabbitmq-plugins enable --offline rabbitmq_mqtt rabbitmq_federation_management rabbitmq_stomp You can also mount a file at /etc/rabbitmq/enabled_plugins with contents as an erlang list of atoms ending with a period. Example enabled_plugins [rabbitmq_federation_management,rabbitmq_management,rabbitmq_mqtt,rabbitmq_stomp]. □ Additional Configuration If configuration is required, it is recommended to supply an appropriate /etc/rabbitmq/rabbitmq.conf file (see the "Configuration File(s)" section of the RabbitMQ documentation for more details), for example via bind-mount, Docker Configs, or a short Dockerfile with a COPY instruction. Alternatively, it is possible to use the RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS environment variable, whose syntax is described in section 7.8 ("Configuring an Application") of the Erlang OTP Design Principles User's Guide (the appropriate value for -ApplName is -rabbit), this method requires a slightly different reproduction of its equivalent entry in rabbitmq.conf. For example, configuring channel_max would look something like -e RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-rabbit channel_max 4007". Where the space between the variable channel_max and its value 4007 correctly becomes a comma when translated in the environment. #### 安裝 MassTransit1 RabbitMQ Docker 安裝 RabbitMQ Docker 比較簡單, 不需要安裝 RabbitMQ 到主 Host. □ 安裝 RabbitMQ Docker 這是由 MassTransit 維護的 RabbitMQ Docker, 包括管理介面以及其他 plug-in. Run RabbitMQ This is running the preconfigured Docker image maintained by the MassTransit team (opens new window). The container image includes the delayed exchange plug-in and the Management interface enabled. $ docker run -p 15672:15672 -p 5672:5672 --name masstransit1 masstransit/rabbitmq -p, --publish list Publish a container's port(s) to the host 15672 is the default port for RabbitMQ GUI, 5672 for RabbitMQ message broker. 若未加參數 --name, 則每次執行就會新增一個新的(自動命名)的 container: $ docker run -p 15672:15672 -p 5672:5672 masstransit/rabbitmq ○ If you are running on an ARM platform $ docker run --platform linux/arm64 -p 15672:15672 -p 5672:5672 masstransit/rabbitmq □ 測試 RabbitMQ Docker 登入 rabbitmq broker 安裝成功後, 可瀏覽網頁到 http://localhost:15672 測試登入. 登入帳號guest, 密碼guest, 這組預設帳密只能在本機 localhost 使用. 15672 is the default port for RabbitMQ GUI, 5672 for RabbitMQ message broker. 或是用指令測試: docker 必須已經啟動執行中. #### RabbitMQ 基本概念 □ Message ○ 狀態 △ ready: 新增到 queue 時. 1. 設定 queue length 可指定上限數量. 2. 若已達上限, 預設會刪除掉 ready 的訊息後再新增. △ unacked: consumer 讀取時. △ acked: consumer 完成讀取時. △ consumer_timeout: 若超過時間, 則移除(ready 和 acked). 在 rabbitmq.conf 檔案中可設定 consumer_timeout, 時間單位為 milliseconds. 例如一小時: consumer_timer = 3600000 ○ 長度上限: 512 MiB. ○ requeue or discard: reject/nact 時可選擇是否要 requeue. □ Queue 格式: https://www.rabbitmq.com/tutorials/amqp-concepts.html#queues ○ Queue 可由 producer 或 consumer 建立. 若連線到不存在的 queue, 則均視為錯誤! ○ (vhost + queue)為 unique. ○ Durable 或 persistence: 訊息是否存入磁碟中? rabbitMQ關機後仍可保留. ○ Exclusive: 只允許一個 consumer 連線使用, 斷線後立刻刪除. ○ Auto-delete: 沒有 consumer 連線就不保留. 建立 queue 時可設定為 Single Active Consumer, 同一時間只允許一個 Consumer 連線使用. □ Channel Multiple channels can be opened on a connection at the same time. RabbitMQ 通訊基礎為 TCP connection. Connection 比較耗費資源. 將一個 Connection 給多個 Channel 使用可節省資源. □ Exchange 訊息是先傳送到 exchange, 再送到 queue. Exchange模式: ○ Default: 等於(無名的 Direct)模式. ○ Direct: 1. Bind queue key=B in exchange. 2. Receive message routing key=R. 3. Forward R=B message to queue. ○ Fanout Broadcast to all binding queues in exchange. ○ Topic Forward topic matched message to queues. Topic special char: * (star) can substitute for exactly one word. # (hash) can substitute for zero or more words. ○ Headers Forward Header matched message to queues. □ □ #### RabbitMQ Docker 常用指令 □ 測試 RabbitMQ Docker $ docker run -p 15672:15672 -p 5672:5672 --name MassTransit1 masstransit/rabbitmq | 下載由 MassTransit 維護的 RabbitMQ Docker, 並命名為 MassTransit1 -p, --publish list Publish a container's port(s) to the host 15672 is the default port for RabbitMQ GUI, 5672 for RabbitMQ message broker. 安裝後可以網址: http://localhost:15672/, 瀏覽 RabbitMQ 的管理介面. 登入帳號密碼, 預設均為 guest. guest 帳號除非開啟權限, 否則只能以 localhost 連線使用. "guest" user can only connect from localhost $ docker exec [DockerImageName] rabbitmqctl rabbitmq-plugins enable rabbitmq_management | 開啟 RabbitMQ 管理介面. $ docker exec [DockerImageName] rabbitmqctl status | 檢查 RabbitMQ Docker Image 的狀態. 例如: $ docker exec MassTransit1 rabbitmqctl status Status of node rabbit@1979db74238c ... Runtime OS PID: 15 OS: Linux Uptime (seconds): 1072 Is under maintenance?: false RabbitMQ version: 3.10.5 Node name: rabbit@1979db74238c Erlang configuration: Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:no-native-stack] Crypto library: OpenSSL 1.1.1p 21 Jun 2022 Erlang processes: 412 used, 1048576 limit Scheduler run queue: 1 Cluster heartbeat timeout (net_ticktime): 60 Plugins Enabled plugin file: /etc/rabbitmq/enabled_plugins Enabled plugins: * rabbitmq_prometheus * rabbitmq_shovel_management * rabbitmq_shovel * amqp10_client * prometheus * rabbitmq_delayed_message_exchange * rabbitmq_consistent_hash_exchange * accept * rabbitmq_management * amqp_client * rabbitmq_web_dispatch * cowboy * cowlib * rabbitmq_management_agent Data directory Node data directory: /var/lib/rabbitmq/mnesia/rabbit@1979db74238c Raft data directory: /var/lib/rabbitmq/mnesia/rabbit@1979db74238c/quorum/rabbit@1979db74238c Config files * /etc/rabbitmq/conf.d/10-defaults.conf Log file(s) * /var/log/rabbitmq/rabbit@1979db74238c_upgrade.log * Alarms (none) Memory Total memory used: 0.1593 gb Calculation strategy: rss Memory high watermark setting: 0.4 of available memory, computed to: 3.267 gb reserved_unallocated: 0.0852 gb (53.5 %) code: 0.0394 gb (24.74 %) other_system: 0.0284 gb (17.82 %) other_proc: 0.0191 gb (11.98 %) other_ets: 0.0033 gb (2.09 %) atom: 0.0015 gb (0.92 %) plugins: 0.0014 gb (0.9 %) metrics: 2.0e-4 gb (0.15 %) mgmt_db: 2.0e-4 gb (0.13 %) mnesia: 1.0e-4 gb (0.06 %) binary: 1.0e-4 gb (0.05 %) msg_index: 0.0 gb (0.02 %) quorum_ets: 0.0 gb (0.01 %) connection_other: 0.0 gb (0.0 %) quorum_queue_dlx_procs: 0.0 gb (0.0 %) stream_queue_procs: 0.0 gb (0.0 %) stream_queue_replica_reader_procs: 0.0 gb (0.0 %) allocated_unused: 0.0 gb (0.0 %) connection_channels: 0.0 gb (0.0 %) connection_readers: 0.0 gb (0.0 %) connection_writers: 0.0 gb (0.0 %) queue_procs: 0.0 gb (0.0 %) queue_slave_procs: 0.0 gb (0.0 %) quorum_queue_procs: 0.0 gb (0.0 %) stream_queue_coordinator_procs: 0.0 gb (0.0 %) File Descriptors Total: 2, limit: 1048479 Sockets: 0, limit: 943629 Free Disk Space Low free disk space watermark: 0.05 gb Free disk space: 253.7747 gb Totals Connection count: 0 Queue count: 0 Virtual host count: 1 Listeners Interface: [::], port: 15672, protocol: http, purpose: HTTP API Interface: [::], port: 15692, protocol: http/prometheus, purpose: Prometheus exporter API over HTTP Interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication Interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0 #### RabbitMQ系列介紹 RabbitMQ系列-簡介 https://www.twblogs.net/a/5ee66913bf0f925f719ba4ff RabbitMQ系列(一)啓動及網頁、外網訪問配置 https://www.twblogs.net/a/5ee6691df604b46ffc8a8cfa RabbitMQ系列(二)VirtualHost作用及角色權限管理實戰 https://www.twblogs.net/a/5ee6691dd28323b1555679d2 RabbitMQ系列(三)RabbitMQ進階-Queue隊列特性 (一)簡單隊列 https://www.twblogs.net/a/5eee8dce210de7f528fd9308 RabbitMQ系列(四)RabbitMQ進階-Queue隊列特性 (二)工作隊列 Work模式 https://www.twblogs.net/a/5eee1bb8411a0ed3151daaf6 #### **** 以下舊資料, 確認後移到上面 ref: Message Queue 簡介(以 RabbitMQ 為例) https://godleon.github.io/blog/ChatOps/message-queue-concepts/ Preface 以下內容是學習線上課程 RabbitMQ and Messaging Concepts 時所留下的學習筆記。 由於目前 Message Queue 在系統架設設計時很常出現,到底這類型的元件有什麼特性? 可以為系統帶來什麼優點? 以及其相關的特性 & 運作方式 (以 RabbitMQ 為例)。以下的部份會進行說明解釋。 Application 整合模式 在一個系統中,一般不會只有一隻程式在運作,而是會有多隻程式同時負責各種不同的任務,而程式之間難免會有互相傳遞資料進行處理的需求,而這類的需求,以下都統稱為 applcation 的整合。 而一般常見的 application 整合方式大概可以分為以下幾種: Filed Based Integration File Based Integration source application 會根據需要處理的工作,產生新的檔案到特定的路徑 其他的 process application 則會是一直監控該路徑有沒有新檔案,有新的檔案則取出進行處理 Shared Database Integration Shared Database Integration source application 收到新任務時,或將資訊寫入 DB table 中 processor application 則是持續監控 DB 中的特定 table,若有新紀錄則取出進行處理 processor application 處理完工作後可能會將狀態回寫到 DB 中 Direct Connection Integration Direct Connection Integration source application 直接傳遞訊息給 processor application 可能透過 TCP/IP 或是 named pipe connection 的方式傳遞資料 不限傳遞的資料格式,連線兩端的 application 傳遞的資料格式可自訂,可能是純文字、XML or JSON 透過 Message Broker 以非同步的方式傳遞訊息Asynchronous Messaging Message Broker Based Integration 不限傳遞的資料格式 需要額外的 Message Queue middleware 的協助,也有可能被稱為 Message Broker 或是 Message Bus Message Broker 搜到訊息後(來自 producer application )會轉發給 consumer application 從上面看得出來,其實透過 Message Broker 的方式是以上很多不同方式改良後所產生的結果 使用 Message Queue 有什麼優點? 了解 Message Queue(Broker) 的簡單架構後,那接下來的問題可能是:到底在系統中加入 Message Queue,具體可以帶來哪些好處 or 優點呢? 以下列出幾項說明:(publisher 為訊息傳送者,consumer 為訊息接收者) 將 publisher & consumer 進行 decouple 了,因此程式開發人員可以各自專心負責規模較小 & 單純的程式開發工作 publisher & consumer 不需要知道雙方的實際的位置(例如:IP address),只要將資料往 message queue 送就好 即使 consumer 短暫的無法提供服務也沒關係,message queue 可以將資料暫存起來,等待 consumer 重新上線時再送過去 比起持續 polling 的方式相對有效率的多 提供了一個可靠的方式,讓訊息傳遞 & 工作處理兩件事情可以用非同步的方式進行 當單一 consumer 不足以完成所有工作時,可以很容易的增加 consumer 數量進行水平擴展 從上面的優點可以看出,加入了 Message Queue,對於整體系統中各個不同元件的解耦很有幫助,同時了也帶來了水平擴展的可能性。(當然這部份也會牽涉到系統流程面上的設計,並非只透過系統架構就可以完成) 使用場景範例 以下就透過兩個範例說明,介紹 Message Queue 在整體系統中可以作為什麼樣的角色。 Case 1 Message Queue Case 1 從上圖可以看出,當 product 有任何更動時,需要後端資料庫的 search index 時,相關的資訊會先傳進 message queue,然後會有其他 worker(consumer) 接收進行處理。 Case 2 Message Queue Case 2 在一個電子商務網站,可能會因為以下不同的理由,以非同步的方式寄送 email 給會員: 驗證 E-Mail address 重設密碼 訂單確認 促銷活動 Messaging System 中的標準組成元素 了解 Message Queue 的功能之後,接著說明在一個 Messaging System 會包含的標準元素: Message:這是最主要的部份,簡單來說 message 就是要從一個 application 傳遞到另一個 application 的資料,可以是很多形式,例如:command、query 或是任何事件資訊;而每個 message 會包含兩個部份,分別是 routing information & payload(實際資料)。 Producer/Publisher:producer 是產生 message 並將其傳到 message broker Consumer/Receiver:收取來自 message broker 的 message 並進行處理 Message Queue:是個存放來自 producer 的 message list,通常一個 messaging system 中會有多個 queue,而每個 queue 都會有一個識別名稱 Message Broker:將 message 從傳送者轉發到 receiver 的一個中間者 Router/Exchange:根據軟體設定,決定 message 傳遞的路徑,將不同的訊息轉發進不同的 queue 中 Routing 的概念在 RabbitMQ 中以 Exchange 來表示 Connection:真實的 TCP 連接,像是 producer & message broker 之間的連接,或是 message broker 與 consumer 之間的連接 Channel:在真實的 TCP 連接中定義出來的 virtual connection,這才是實際上 producer/consumer 與 message broker 之間的連接改念 Binding:Binding 定義了 Exchange & Queue 之間的關係以及訊息 routing 的設定,可能還包含了一些 filter 的設定 Message Queue Case 2 細部探究 Message, Queue & Exchange 的屬性資訊 了解了上面關於 Messaging System 的標準組成元素之後,可以大致了解一個 Message Queue 大概是如何與外部 application 進行溝通運作的;但如果要更探究 Message Queue 內部的運作細節,可以從標準組成元素中的屬性(attribute)來進行了解。 這部份的學習會以 RabbitMQ 為主,因此以下屬性介紹會以 RabiitMQ 為主,但跟其他主流的 Message Queue 應該不會差異太大 Message 的屬性 Routing Key:用來決定訊息進入到 Messaging System 後如何被轉發的資訊 Headers:key/value 的資訊集合,可用來作為訊息 routing 之後或是傳遞 publisher 想要傳遞的額外訊息 Payload:實際所要傳遞的資料 Publishing TimeStamp(optional):publisher 所提供的 timestamp 資訊 Expiration:Message 可停留在 Queue 中的存活時間,超過 expiration 的設定則 message 會視為 dead 而不會傳送 Delivery Mode:會有 persistent or trasient 兩個選項,而 persistent 會將 message 寫入 disk 中,即使 RabiitMQ 服務重啟,訊息也都不會遺失,而 trasient 則不會 Priority:message 的優先權(0~255, 這個需要 Queue 支援才可以) Message ID(optional):由 publisher 給入,用來識別 message 的 ID 資訊 Correlation ID(optional):在 RPC 場景中用來匹配 request & response 用的資訊 Replay To(optional):在 request-response 場景中會使用到的 exchange 或是 queue 的名稱 Queue 的屬性 Name:唯一的名稱,最多 255 個字元的 UTF-8 字串 Durable:指定在 RabbitMQ 重啟後保留 or 移除 Queue 的依據 Auto Delete:沒有任何訂閱者的情況下是否自動移除 Exclusive:只服務特定一個 connection,一旦該 connection 斷掉就會移除 Queue Max Length:最多可以停留在 Queue 中的 message 數量,可以指定若超過會從最舊的訊息開始移除或是拒絕新的 message 進入 Max Priority:priority 可設定的最大值 Message TTL:存入到 Queue 中的 Message 的 TTL(若是 Message & Queue 都有 TTL 的設定,會以較低的為主) Dead-letter Exchange:可用來指定過期 or 被丟棄的 message 被自動傳送到某一個 exchange Binding Configuration:Queue & Exchange 之間的關聯資訊 (每個 Queue 都必須與某個 Exchange 綁定,確保可以從 Exchange 取得 message) Exchange 的屬性 Name:Exchange 的名稱(必須唯一) Type:Exchange 的運作類型,會是 Fanout, Direct, Topic, Headers 四種之一 Durability:與 Queue Durability 相同,用來決定在 RabbitMQ 服務重啟後會不會依然存在 Auto Delete:設定是否在沒有任何 Queue 綁定的情況下,就自動移除 Internal:若設定為 Internal,表示僅能接收來自其他 exchange 的 message,無法接收來自 publisher 的 message Alternate Exchange:指定無法 route 的 message 的去向 **Other Arguments(x-arguments)**:Exchange 其他的 metadata,可能會用於其他的 plugin 中 Exchange 要切入 RabbitMQ 之前,Exchange 的概念是必須先了解的,以下是 RabbitMQ 的內部架構圖: RabbitMQ Exchange Exchange 是 RabbitMQ 系統中負責轉發訊息的元件 Message Producer 無法將訊息直接傳到 Queue 中,在 RabbitMQ 中訊息的第一個進入點是 Exchange 實際儲存訊息的 Queue 會根據使用者的設定,與不同的 Exchange 進行綁定 當 Exchange 收到訊息後,就會轉發到與其綁定的 Queue (可能 0 到多個不等) Exchange 僅能將訊息轉發到與其綁定的 Queue 上 Exchange 有四種轉發模式,分別是 Fanout, Direct, Topic, Headers 至少會有一個預設 Exchange 存在於 RabbitMQ 系統中,稱為 default exchange,轉發的模式為 direct;每個新建立的 Queue,若是沒指定 exchane 資訊,就會與預設的綁定 總結 透過上面關於 Messaging System 的標準組成元素說明,可以了解到一個 Message Queue 在整體系統上所扮演的角色;加上內部組件的屬性(attribute)說明,也大致可以推敲出一個 Messaging System(以 RabbitMQ 為主) 內部運作的狀況。 因此在實際使用上,我們可以利用 Message Queue 有效的將 application 進行解耦,讓大問題拆分為小問題,並且讓 developer 可以專注在特定的系統功能上,讓 application 面對的,就是 Message Queue 而已。 References Free RabbitMQ Tutorial - RabbitMQ and Messaging Concepts | Udemy Documentation: Table of Contents — RabbitMQ