Bun v0.8.1 為 Bun.serve() 新增了 unix domain socket 支援,修復了效能衰退問題,並修正了 bun install、node:http 和 napi 中的錯誤。
Bun 1.0 即將在 9 月 7 日推出!請至 https://bun.dev.org.tw/1.0 註冊觀看發佈直播。
Bun 是一個速度極快的 JavaScript 執行時、打包器、轉譯器和套件管理器 — 功能All-in-One。我們最近發佈了許多 Bun 的變更。以下是最近幾個版本的重點回顧。In case you missed it
v0.7.0
- Web Workers、--smol
、structuredClone()
、WebSocket 可靠性改進、node:tls
修正及更多。v0.7.1
- ES Modules 載入速度提升 30-250%、fs.watch
修正及許多node:fs
相容性改進。v0.7.2
- 實作了node:worker_threads
、node:diagnostics_channel
和BroadcastChannel
。v0.7.3
-bun test
中的覆蓋率報告,以及使用bun test -t
進行測試篩選。v0.8.0
- 偵錯器支援、fetch 串流和bun update
。實作了node:tty
中的 ReadStream 和 WriteStream,包括process.stdin
上的原始模式。SvelteKit 支援
安裝 Bun
curl -fsSL https://bun.dev.org.tw/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
升級 Bun
bun upgrade
使用 Bun.serve() 在 unix domain socket 上啟動 HTTP 伺服器
Bun.serve()
現在支援 unix domain sockets,可讓您將 socket 指向檔案系統上的檔案,而不是網路主機/port。當您想要執行僅能從同一部機器存取的伺服器(有時是容器或代理伺服器)時,這非常有用。
const server = Bun.serve({
unix: "/tmp/my-socket.sock", // <-- new option
fetch(req){
console.log(req.url);
return new Response("Hello world!");
}
});
console.log(`Listening on unix:///tmp/my-socket.sock!`);
若要啟動伺服器,請執行 bun ./server.ts
。
bun ./server.ts
Listening on unix:///tmp/my-socket.sock!
然後,您可以使用 curl
向 socket 發出請求。
curl --unix-socket /tmp/my-socket.sock https://127.0.0.1/my-path
Hello world!
修復了讀取請求body時的效能衰退問題
Bun v0.8.0(昨天發佈)在讀取請求 body 時引入了效能衰退問題。此問題已在 v0.8.1 中修復。
以下腳本
Bun.serve({
port: 3000,
async fetch(request) {
await request.json();
return new Response();
},
});
Bun v0.8.1
❯ oha http://127.0.0.1:3000 -m POST -d '{"a": 123}' -n 200000
Summary:
Success rate: 1.0000
Total: 1.8099 secs
Slowest: 0.0068 secs
Fastest: 0.0000 secs
Average: 0.0005 secs
Requests/sec: 110505.0574
Bun v0.8.0
❯ oha http://127.0.0.1:3000 -m POST -d '{"a": 123}' -n 200000
Summary:
Success rate: 1.0000
Total: 5.1787 secs
Slowest: 0.0121 secs
Fastest: 0.0002 secs
Average: 0.0013 secs
Requests/sec: 38619.4133
效能衰退了 2.8 倍!
問題在於 microtask 排程。
Microtask 排程
在 Bun v0.8 中,我們修正了 HTTP 伺服器和 sockets 的事件迴圈排程中長期存在的效率低下的問題,但遺漏了一個導致此效能衰退的案例。
JavaScript 的事件迴圈有兩種任務類型:microtasks 和 tasks。
Task | Microtask |
---|---|
setTimeout 、setInterval 、async I/O | queueMicrotask 、Promise.resolve 、process.nextTick |
Microtasks 使用 queueMicrotask()
、Promise.resolve()
、process.nextTick
和一些其他 API 進行排程。
Tasks 使用 setTimeout()
、setInterval()
或 async I/O(例如 HTTP 伺服器)進行排程。Tasks 使程序保持運作,而 microtasks 在每個 task 結束時清空。
您可以將 JavaScript 的事件迴圈視為更複雜的版本
let task;
while ((task = getNextTask())) {
task();
do {
let microtask;
while ((microtask = getNextMicrotask())) {
microtask();
}
} while (hasMicrotasks());
}
每個 task 都可能排程更多 microtasks 和 tasks。Microtasks 在每個 task 結束時清空,而 tasks 在每個事件迴圈迭代結束時清空。
Bun 的事件迴圈先前更像是這樣運作
while (true) {
let task;
while ((task = getNextTask())) {
task();
do {
let microtask;
while ((microtask = getNextMicrotask())) {
microtask();
}
} while (hasMicrotasks());
}
while ((task = getAsyncIOTask())) {
task();
// where's the microtask draining? 🤔
}
}
對於 async IO,我們沒有清空 microtasks!這導致了許多不必要的 microtask 排程,這在某些情況下導致過多的記憶體使用和延遲。 案例。
但這並不是 v0.8.0 之後效能衰退的原因。
效能衰退的原因是我們忘記在 讀取請求 body 時清空 microtasks。
這表示對於每個請求 body,我們都會排程一個 microtask 來讀取 body,然後僅在稍後排程下一個 task 時才清空它。 排程。在 < Bun v0.8.0 中,HTTP 伺服器中建立的 microtasks 會在 所有其他 tasks 完成後才清空。這表示遺漏 microtask 清空 在任何地方都是一致的。在 Bun v0.8.0 中,HTTP 伺服器中建立的 microtasks 在當前請求完成後清空,這表示在此案例(讀取請求 body)中跳過它會 變得不平衡。這導致了效能衰退。 效能衰退。
bun install 錯誤修正:版本名稱格式錯誤
^0.0.2rc1
版本規範無效,但在 npm 中實際存在。先前,Bun 在收到此輸入時會崩潰,此問題已修正。
靜態已知的失敗 require
錯誤地在執行時內聯
當 Bun 的打包器知道 require()
呼叫在執行時會失敗,並且位於 try/catch 區塊內時,會自動內聯失敗的 require()
呼叫。這對於打包使用選用相依性的程式碼非常有用。
輸入
try {
require("i-dont-exist-but-thats-okay");
} catch (e) {
console.log("I don't exist, but that's okay!");
}
輸出
try {
(() => {
// the bug! it shouldn't be inlined here. it should only be inlined when bundling.
throw new Error(`Cannot require module "i-dont-exist-but-thats-okay"`);
})();
} catch (e) {
console.log("I don't exist, but that's okay!");
}
此功能錯誤地在執行時啟用,而不僅僅是在打包時啟用。這 破壞了一些依賴於檢查模組是否存在或 檢查 Node.js 公開的特定 code
屬性的套件。此問題已 修正。Bun 的執行時不再內聯失敗的 require()
呼叫(打包器仍然會,這是正確的)
此錯誤主要影響 napi。
當存在大量 Headers 和 Blob 物件時,減少記憶體使用量
Bun 的 Headers
和 Blob
實作未將其大小報告給 垃圾回收器。由於 Bun 在原生程式碼中實作了許多類別,因此垃圾 回收器並不總是能看到類別實際使用了多少記憶體。 當 Headers
或 Blob
夠大時,這可能會導致垃圾回收器 無法像應有的頻率那樣執行。
現在 Bun 會將 Headers
和 Blob
的大小報告給垃圾回收器。
fetch() 記憶體報告錯誤修正
目前,每次呼叫 fetch()
約使用 3 KB 的記憶體。此記憶體 在原生程式碼中使用,這表示垃圾回收器無法看到它。現在 Bun 會將此記憶體報告給垃圾回收器,這表示垃圾 回收器在有大量 fetch()
呼叫時將更頻繁地執行。
bun install 錯誤修正:package.json 腳本過時
bun install
的 lockfile 是二進位檔,這讓我們可以在 lockfile 中儲存比平常更多的資料。我們儲存的其中一個項目是 package.json
腳本。 先前,如果您執行 bun install
,然後變更 package.json
腳本,bun install
並非總是會擷取變更。此問題已 修正。
Sourcemap 錯誤修正在 bun --inspect
中
由於 Bun 會轉譯每個檔案,因此 Bun 也必須為每個檔案保留 sourcemap。 這用於使 Error.prototype.stack
產生 sourcemapped stacktraces 和 console.log
報告準確的行號。
不幸的是,Sourcemaps 非常耗用記憶體。我們沒有將整個 sourcemap 儲存在記憶體中,而是儲存更精簡的版本,直到首次使用。精簡 版本的前 24 個位元組包含額外的元資料(例如,原始輸入原始碼的 行數)。此 24 位元組標頭錯誤地包含在 bun --inspect
中使用的內聯 sourcemaps 中, 導致 JSON sourcemap 的前 24 個位元組左右的輸入無效。 令人驚訝的是,這並非總是會破壞 sourcemaps。只有有時會。 無論如何,此問題已修正,現在 Bun 會如預期般包含 sourcemap 位元組, 預期般包含 sourcemap 位元組,而沒有額外的元資料。 預期般包含 sourcemap 位元組,而沒有額外的元資料。
Proxy URL 與 node:http 錯誤已修正
當使用設定了 http_proxy
環境變數執行以下腳本時,會擲回錯誤
腳本
import axios from "axios";
const res = await axios.get("https://httpbin.org/get?answer=42");
console.log(res.data.args);
執行
http_proxy=http://127.0.0.1:1087 https_proxy=http://127.0.0.1:1087 bun run index.ts
錯誤
TypeError: fetch() URL is invalid
at node:http:839
不應該發生此錯誤。感謝 @Hanaasagi,此問題已修正。