fibjs 是什麼?
fibjs 是一個專為web 後端開發設計的應用程式伺服器開發框架。它建立在Google V8 JavaScript 引擎的基礎上,並採用與傳統回調不同的並發解決方案。 fibjs 使用fiber(纖程)在框架層面上隔離了非同步呼叫所帶來的業務複雜性,大大降低了開發難度,並減少了用戶空間頻繁的非同步處理而導致的效能問題。
由於歷史原因,JavaScript 主要被用來處理瀏覽器的UI。 UI 開發是典型的單執行緒事件驅動模式,因此JavaScript 逐漸形成了以非同步處理為主要程式設計範式。
隨著JavaScript 的成功,越來越多的人開始將JavaScript 套用到其他場景。同時,人們也越來越意識到在許多場景中,非同步處理並不是最適合的選擇。
返璞歸真,敏捷開發
fibjs 在框架層面使用fiber(纖程)隔離了非同步呼叫所帶來的業務複雜性,將I/O 的非同步處理封裝為更直觀的同步呼叫。工程師只需按照通常的同步業務邏輯編寫程式碼,即可享受非同步處理所帶來的巨大便利。
以下這段程式碼摘自mysql 模組的文檔:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32conn.beginTransaction(err => {
if (err) {
throw err;
}
conn.query('INSERT INTO posts SET title=?', title,
(error, results, fields) => {
if (error) {
return conn.rollback(() => {
throw error;
});
}
var log = 'Post ' + results.insertId + ' added';
conn.query('INSERT INTO log SET data=?', log,
(error, results, fields) => {
if (error) {
return conn.rollback(() => {
throw error;
});
}
conn.commit((err) => {
if (err) {
return conn.rollback(() => {
throw err;
});
}
console.log('success!');
});
});
});
});
在fibjs 中,完成同樣的工作,程式碼如下:
1
2
3
4
5
6conn.trans(() => {
var result = conn.execute('INSERT INTO posts SET title=?', title);
var log = 'Post ' + results.insertId + ' added';
conn.execute('INSERT INTO log SET data=?', log);
});
console.log('success!');
如果你追求簡潔,你甚至可以把程式碼寫成這樣:
1
2
3
4conn.trans(() => conn.execute('INSERT INTO log SET data=?',
'Post ' + conn.execute('INSERT INTO posts SET title=?', title).insertId +
' added'));
console.log('success!');
透過比較,我們可以明顯看到不同程式設計風格所帶來的差異。較少的程式碼意味著較少的錯誤,隨著程式碼的減少,程式碼的邏輯也會更加清晰。在這種情況下,不論是開發還是維護工作都會獲益。
擁抱高能
儘管擴展伺服器來提高回應速度是相對容易的,但效能仍然是選擇一個開發框架時的重要考慮因素之一。隨著ES7 的推出,async 作為一種新的非同步開發模式被引入到JavaScript 中。然而,當我們享受async 帶來的同步程式風格時,也必須面對它對效能的影響。
為了比較不同程式設計風格所帶來的效能差異,我們可以使用以下測試程式碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39var count = 1000;
async function test_async(n) {
if (n == count)
return;
await test_async(n + 1);
}
function test_callback(n, cb) {
if (n == count)
return cb();
test_callback(n + 1, () => {
cb();
});
}
function test_sync(n) {
if (n == count)
return;
test_sync(n + 1);
}
async function test() {
console.time("async");
await test_async(0);
console.timeEnd("async");
console.time("callback");
test_callback(0, () => {
console.timeEnd("callback");
});
console.time("sync");
test_sync(0);
console.timeEnd("sync");
}
test();
在最新的v8 下,這段程式碼的運行結果如下:
1
2
3async: 0.539ms
callback: 0.221ms
sync: 0.061ms
從測試結果中,我們可以清楚地看到,當廣泛應用async 後,伺服器將花費大量時間來處理async 函數的呼叫和返回。我們在一些實際的服務端應用程式測試中也發現了這一點。然而,這種急劇下降的表現是完全無法接受的。
與此相比,fibjs 使用fiber 技術,充分利用了JavaScript 語言本身的特性,並最大限度地發揮了V8 引擎的優越性能。工程師可以輕易地將伺服器的效能發揮到極致。
靈活選擇範式而不被綁架
選擇使用fibjs 並不代表你必須使用同步的開發風格。實際上,fibjs 支援你所熟悉的各種非同步程式設計範式,並且可以靈活地在同步風格和非同步風格之間切換。
然而,無論是回呼函數還是async,它們都有一個致命的缺陷,那就是傳染性。如果函數是回呼函數或async 函數,那麼所有依賴它的其他函數也必須是回呼函數或async 函數。在大規模軟體開發中,這將導致巨大的開發成本。
以一個簡單的伺服器開發場景為例。在專案初期,我們選擇將session 資料儲存在記憶體中,此時,我們可以使用同步方式直接讀取和儲存數據,並基於此開發完整的業務功能。隨著業務規模的擴大,我們需要將session 資料儲存到Redis 或MongoDB 中,此時,我們需要將與session 相關的操作改為非同步模式。
理論上,我們可以逐一修改每個函數,使它們符合所依賴函數的要求,但這要求我們完全了解所有模組並具備修改它們的能力。當多人協作開發或使用第三方模組時,這幾乎是不可能的。
因此,在所有通用模組中,都應同時提供同步和非同步接口,以平衡非同步和效能之間的關係。而普通開發者通常會選擇僅提供非同步接口,從而導致效能問題。
在fibjs 中,你可以輕鬆解決類似問題,避免不受控制地傳播顯式非同步:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var util = require('util');
function session_get(sid) {
return sdata;
}
async function async_session_get(sid) {
return sdata;
}
function callback_session_get(sid, cb) {
cb(null, sdata);
}
data = session_get(sid);
data = util.sync(async_session_get)(sid);
data = util.sync(callback_session_get)(sid);
fibjs 提供了util.sync 函數,可以將回呼函數或非同步函數轉換為同步函數並直接呼叫。借助這種方式,我們可以輕鬆地整合不同程式設計範式的模組,並最小化開發成本,將它們轉變為同步範式,有效地避免範式傳染帶來的災難。
開始體驗
準備好開始一場愉快的開發經驗了嗎?那麼,就從安裝開始吧。
👉 【安裝運作環境】