本節將介紹 Broker 的生命週期中,啟動 Broker 與停止 Broker 到底各自發生了什麼事情。
當一個 Broker 被啟動時,它將會嘗試與 Transporter 建立連線。建立完成後,它不會將本地服務清單發佈到遠端節點,而是先啟動所有的服務,然後呼叫每個服務中的 started
處理程序。待所有的服務都啟動成功後, Broker 才會將本地服務清單發佈到遠端節點。因此只有在所有本地服務都成功初始化並啟動後,遠端節點才能夠發送請求。
Fig. 1. 啟動 Broker 流程
當兩個服務互相依賴的時候,可能會陷入死結。例如:
users
服務設定了dependencies: ["posts"]
,posts
也設定了dependencies: ["users"]
。為避免此狀況,請移除dependencies
設定,改使用this.waitForServices
來等待多個服務後再啟動。
補充,官方文件是寫在started
執行,但筆者測試仍然會死結,建議可以檢查是否有架構設計上的缺陷,或可將微服務再做細分來避免此狀況,例如另外建立一個整合服務來等待兩個服務。
當呼叫 broker.stop
或停止處理程序時,首先 Broker 會透過 Transporter 發送空的服務列表,這時已停止的服務就不會繼續收到請求,而是將請求改送到其它的實例。接著 Broker 會開始停止所有的本地服務,最後中斷 Transporter 的連線並結束處理程序。
Fig. 2. 停止 Broker 流程
在介紹完 Broker 的生命週期後,本節將介紹服務的創建、啟動、停止與合併事件,以及該如何使用生命週期事件來做處理。
created
事件會在服務實例被創建時觸發。例如: broker.createService
、 broker.loadService
。
範例:在服務創建時建立 http
模組實例並存放於 this
中。
const http = require("http");
module.exports = {
name: "www",
created() {
// 建立 HTTP 服務
this.server = http.createServer(this.httpHandler);
}
};
注意,這是個同步事件處理函數,不可以返回
Promise
,也不可以使用async/await
語法糖。
started
事件會在呼叫 broker.start
並啟動所有本地服務時被觸發。
範例:用於連線到資料庫。
module.exports = {
name: "users",
async started() {
try {
await this.db.connect();
} catch(e) {
throw new MoleculerServerError("Unable to connect to database.", e.message);
}
}
};
注意,這是個非同步事件處理函數,可以返回
Promise
,也可以使用async/await
語法糖。
stopped
事件會在呼叫 broker.stop
並停止所有本地服務時被觸發。
範例:用於中斷資料庫連線。
module.exports = {
name: "users",
async stopped() {
try {
await this.db.disconnect();
} catch(e) {
this.logger.warn("Unable to stop database connection gracefully.", e);
}
}
};
注意,這是個非同步事件處理函數,可以返回
Promise
,也可以使用async/await
語法糖。
merged
事件會在綱目 (包含 mixin ) 合併後被呼叫,呼叫完成後才會將服務註冊,因此你可以在這之前就先對其操作。
module.exports = {
name: "posts",
settings: {},
actions: {
find: {
params: {
limit: "number"
},
handler(ctx) {
// ...
}
}
},
merged(schema) {
// 修改服務設定
schema.settings.myProp = "myValue";
// 修改 Actions 的驗證參數
schema.actions.find.params.offset = "number";
}
};
[1] Lifecycle, https://moleculer.services/docs/0.14/lifecycle.html