Di chuyển từ Node.js sang Bun 1.2 trong môi trường Production: Hiệu suất HTTP, SQLite & Package Full-Stack

Mục lục bài viết(22 mục)
Hướng dẫn này trình bày chi tiết quy trình di chuyển các microservice Node.js hiện có sang Bun 1.2 để triển khai sản xuất. Chúng ta sẽ đề cập đến việc di chuyển máy chủ HTTP, tích hợp bun:sqlite, triển khai máy chủ WebSocket, khả năng tương thích của gói npm, các cân nhắc về addon C++ gốc, giảm thiểu khoảng trống API của Node.js và đóng gói Docker với các điểm chuẩn hiệu suất.
Ưu điểm cốt lõi của Bun 1.2 cho sản xuất
Bun 1.2 mang lại những lợi thế đáng kể về hiệu suất so với Node.js, chủ yếu nhờ vào việc triển khai Zig cơ bản và công cụ JavaScriptCore. Các lợi ích chính bao gồm:
- Thời gian khởi động nhanh hơn: Quan trọng đối với các hàm serverless và các microservice được mở rộng thường xuyên.
- Giảm mức sử dụng bộ nhớ: Chi phí vận hành thấp hơn và mật độ cao hơn trên mỗi máy chủ.
- Công cụ tích hợp:
bun install,bun run,bun testvàbun buildhợp lý hóa quy trình phát triển. - API gốc:
bun:sqlite,bun:ffi,bun:servecung cấp các nguyên thủy được tối ưu hóa cao.
Di chuyển máy chủ HTTP
Máy chủ HTTP gốc của Bun (Bun.serve) là một giải pháp thay thế hiệu suất cao cho mô-đun http của Node.js hoặc các framework như Express. Nó được thiết kế để có chi phí tối thiểu.
Ví dụ Node.js (Express)
// src/node-server.ts
import express from 'express';
import { readFileSync } from 'fs';
import path from 'path';
const app = express();
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
app.use(express.json());
app.get('/', (req, res) => {
res.send('Hello from Node.js Express!');
});
app.get('/data', (req, res) => {
try {
const data = readFileSync(path.join(__dirname, '../data.json'), 'utf8');
res.json(JSON.parse(data));
} catch (error) {
console.error('Error reading data:', error);
res.status(500).send('Internal Server Error');
}
});
app.listen(port, () => {
console.log(`Node.js Express server listening on port ${port}`);
});
Tương đương Bun 1.2 (Bun.serve)
Di chuyển sang Bun.serve liên quan đến việc điều chỉnh xử lý yêu cầu/phản hồi. Các đối tượng Request và Response của Bun là các đối tượng Request và Response tiêu chuẩn Web.
// src/bun-server.ts
import { readFileSync } from 'fs';
import path from 'path';
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
const server = Bun.serve({
port,
async fetch(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === '/') {
return new Response('Hello from Bun!', { status: 200 });
}
if (url.pathname === '/data') {
try {
const dataPath = path.join(import.meta.dir, '../data.json');
const data = readFileSync(dataPath, 'utf8');
return new Response(data, {
headers: { 'Content-Type': 'application/json' },
status: 200,
});
} catch (error) {
console.error('Error reading data:', error);
return new Response('Internal Server Error', { status: 500 });
}
}
// Example for POST request with JSON body
if (url.pathname === '/submit' && req.method === 'POST') {
try {
const body = await req.json();
console.log('Received JSON body:', body);
return new Response(JSON.stringify({ message: 'Data received', data: body }), {
headers: { 'Content-Type': 'application/json' },
status: 200,
});
} catch (error) {
console.error('Error parsing JSON body:', error);
return new Response('Bad Request: Invalid JSON', { status: 400 });
}
}
return new Response('Not Found', { status: 404 });
},
error(error: Error): Response {
console.error('Bun server error:', error);
return new Response('Internal Server Error', { status: 500 });
},
});
console.log(`Bun server listening on port ${server.port}`);
Lưu ý việc sử dụng import.meta.dir để phân giải đường dẫn, đây là tương đương của Bun với __dirname của Node.js.
SQLite gốc với bun:sqlite
Bun cung cấp một máy khách SQLite gốc, được tối ưu hóa cao thông qua bun:sqlite. Điều này loại bỏ nhu cầu về các gói npm bên ngoài như sqlite3 hoặc better-sqlite3, vốn thường liên quan đến các addon C++ gốc và các bước biên dịch.
Ví dụ Node.js (better-sqlite3)
// src/node-sqlite.ts
import Database from 'better-sqlite3';
import path from 'path';
const dbPath = path.join(__dirname, '../data.db');
const db = new Database(dbPath);
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
`);
export function insertUser(name: string, email: string) {
const stmt = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
const info = stmt.run(name, email);
return info.lastInsertRowid;
}
export function getUsers() {
const stmt = db.prepare('SELECT * FROM users');
return stmt.all();
}
// Example usage
if (require.main === module) {
insertUser('Alice', 'alice@example.com');
insertUser('Bob', 'bob@example.com');
console.log('Users:', getUsers());
db.close();
}
Tương đương Bun 1.2 (bun:sqlite)
bun:sqlite cung cấp một bề mặt API tương tự, giúp việc di chuyển trở nên đơn giản.
// src/bun-sqlite.ts
import { Database } from 'bun:sqlite';
import path from 'path';
const dbPath = path.join(import.meta.dir, '../data.db');
const db = new Database(dbPath);
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
`);
export function insertUser(name: string, email: string) {
const query = db.query('INSERT INTO users (name, email) VALUES (?, ?)');
const result = query.run(name, email);
// Bun's result object for INSERT operations might differ slightly
// For SQLite, lastInsertRowid is often available on the database object or via PRAGMA
// For simplicity, we'll assume a successful run implies insertion.
// A more robust solution would query for the last_insert_rowid()
const lastIdQuery = db.query('SELECT last_insert_rowid() as id;');
return (lastIdQuery.get() as { id: number }).id;
}
export function getUsers() {
const query = db.query('SELECT * FROM users');
return query.all();
}
// Example usage
if (import.meta.main) {
insertUser('Charlie', 'charlie@example.com');
insertUser('David', 'david@example.com');
console.log('Users:', getUsers());
db.close();
}
Máy chủ WebSocket gốc
Bun.serve của Bun cũng bao gồm hỗ trợ hạng nhất cho WebSockets, cung cấp một giải pháp tích hợp, hiệu suất cao.
Ví dụ Node.js (thư viện ws)
// src/node-ws.ts
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', ws => {
console.log('Client connected (Node.js WS)');
ws.on('message', message => {
console.log(`Received: ${message}`);
ws.send(`Echo: ${message}`);
});
ws.on('close', () => console.log('Client disconnected (Node.js WS)'));
ws.on('error', error => console.error('WebSocket error (Node.js WS):', error));
});
console.log('Node.js WebSocket server listening on port 8080');
Tương đương Bun 1.2 (WebSocket Bun.serve)
API WebSocket của Bun được tích hợp trực tiếp vào Bun.serve, sử dụng đối tượng cấu hình websocket.
// src/bun-ws.ts
const server = Bun.serve({
port: 8080,
fetch(req, server) {
const url = new URL(req.url);
if (url.pathname === '/ws') {
const success = server.upgrade(req, {
data: {
// Optional: attach any data to the WebSocket connection
connectedAt: Date.now(),
},
});
if (success) {
return; // Bun handles the WebSocket connection
}
}
return new Response('Not Found', { status: 404 });
},
websocket: {
open(ws) {
console.log(`Client connected (Bun WS). Connected at: ${ws.data.connectedAt}`);
ws.send('Welcome to Bun WebSocket!');
},
message(ws, message) {
console.log(`Received: ${message}`);
ws.send(`Echo: ${message}`);
},
close(ws, code, message) {
console.log(`Client disconnected (Bun WS). Code: ${code}, Message: ${message}`);
},
error(ws, error) {
console.error('WebSocket error (Bun WS):', error);
},
// Optional: perMessageDeflate, maxPayloadLength, idleTimeout
// idleTimeout: 30, // seconds
},
});
console.log(`Bun WebSocket server listening on port ${server.port}`);
Khả năng tương thích gói npm và Addon C++ gốc
Bun hướng tới khả năng tương thích cao với các gói npm hiện có. Hầu hết các gói JavaScript thuần túy sẽ hoạt động mà không cần sửa đổi.
Addon C++ gốc
Đây là nơi khả năng tương thích có thể gây khó khăn. Các addon gốc của Node.js (tệp .node) được biên dịch dựa trên N-API (Node-API) và công cụ V8 của Node.js. Bun sử dụng JavaScriptCore và FFI (Foreign Function Interface) riêng của nó cho các tương tác gốc.
- Khả năng tương thích trực tiếp: Các addon C++ gốc được biên dịch cho Node.js nói chung không tương thích trực tiếp với Bun.
- Giải pháp thay thế:
- Giải pháp thay thế JS thuần túy: Tìm kiếm các giải pháp thay thế JavaScript thuần túy cho các gói dựa vào các addon gốc.
bun:ffi: Đối với các hoạt động nhạy cảm về hiệu suất quan trọng, bạn có thể cần viết lại logic gốc bằng cách sử dụngbun:ffiđể gọi vào một thư viện chia sẻ (.so,.dylib,.dll) được biên dịch đặc biệt cho Bun. Đây là một công việc phức tạp.- Đóng gói container: Nếu một addon gốc cụ thể là không thể thiếu và không thể viết lại, bạn có thể cân nhắc chạy microservice cụ thể đó trong một container Node.js và giao tiếp với nó qua RPC. Điều này làm mất đi một số lợi ích của Bun nhưng đảm bảo chức năng.
Ví dụ: bcrypt (Addon gốc)
bcrypt là một gói phổ biến sử dụng các addon C++ gốc để tăng hiệu suất.
// src/node-bcrypt.ts
import bcrypt from 'bcrypt';
async function hashPassword(password: string) {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
console.log('Hashed password (Node.js):', hashedPassword);
return hashedPassword;
}
async function verifyPassword(password: string, hash: string) {
const isMatch = await bcrypt.compare(password, hash);
console.log('Password match (Node.js):', isMatch);
return isMatch;
}
if (require.main === module) {
(async () => {
const hash = await hashPassword('mysecretpassword');
await verifyPassword('mysecretpassword', hash);
await verifyPassword('wrongpassword', hash);
})();
}
Khi chạy bun install bcrypt, Bun sẽ cố gắng cài đặt nó. Nếu nó không xây dựng được addon gốc, bạn sẽ thấy lỗi. Đối với những trường hợp như vậy, bạn có thể cần sử dụng một giải pháp thay thế JS thuần túy hoặc một cách tiếp cận khác.
Bun 1.2 (bcrypt với các vấn đề tiềm ẩn)
Bun đôi khi có thể chạy các addon gốc của Node.js nếu chúng được biên dịch trước cho kiến trúc và phiên bản N-API chính xác, nhưng điều này không được đảm bảo và thường thất bại. Một cách tiếp cận an toàn hơn là sử dụng một giải pháp thay thế JS thuần túy hoặc một thư viện khác.
Đối với bcrypt, một triển khai JS thuần túy như bcryptjs thường là một giải pháp dự phòng khả thi, mặc dù nó sẽ chậm hơn.
// src/bun-bcryptjs.ts
import bcrypt from 'bcryptjs'; // Using bcryptjs for Bun compatibility
async function hashPassword(password: string) {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
console.log('Hashed password (Bun/bcryptjs):', hashedPassword);
return hashedPassword;
}
async function verifyPassword(password: string, hash: string) {
const isMatch = await bcrypt.compare(password, hash);
console.log('Password match (Bun/bcryptjs):', isMatch);
return isMatch;
}
if (import.meta.main) {
(async () => {
const hash = await hashPassword('mysecretpassword');
await verifyPassword('mysecretpassword', hash);
await verifyPassword('wrongpassword', hash);
})();
}
Khoảng trống và giảm thiểu API của Node.js
Mặc dù Bun hướng tới khả năng tương thích cao với Node.js, nhưng một số API không được triển khai đầy đủ hoặc hoạt động khác nhau.
child_process: Hầu hết các hàm phổ biến nhưexec,spawn,forkđều được hỗ trợ. Các trường hợp đặc biệt hoặc các tùy chọn cụ thể có thể khác nhau.- Mô-đun
vm: Hỗ trợ hạn chế. Nếu ứng dụng của bạn phụ thuộc nhiều vàovm.runInContexthoặc tương tự, đây có thể là một trở ngại. - Mô-đun
domain: Đã bị loại bỏ trong Node.js, không được hỗ trợ trong Bun. - Mô-đun
cluster: Bun không có một tương đương trực tiếp chocluster. Để sử dụng đa lõi, bạn thường chạy nhiều tiến trình Bun và sử dụng bộ cân bằng tải, hoặc tận dụng đa luồng tích hợp của Bun cho một số tác vụ nhất định (mặc dù không phải để mở rộng máy chủ HTTP theo cáchclusterhoạt động). - Mô-đun
fs: Tương thích phần lớn, nhưng một số tùy chọn ít phổ biến hơn hoặc các biến thể đồng bộ có thể có những khác biệt nhỏ. Luôn kiểm tra các hoạt động nặng về hệ thống tệp. net/tls: Chức năng máy khách/máy chủ cơ bản có mặt, nhưng các cấu hình nâng cao có thể yêu cầu điều chỉnh.
Chiến lược giảm thiểu:
- Kiểm thử toàn diện: Các bài kiểm thử đơn vị, tích hợp và đầu cuối là rất quan trọng để xác định các khoảng trống API.
- Polyfills/Shims: Đối với các khoảng trống nhỏ, một polyfill nhỏ có thể thực hiện được.
- Tái cấu trúc: Đối với các khoảng trống đáng kể, hãy tái cấu trúc mã có vấn đề để sử dụng các API tiêu chuẩn Web hoặc API gốc của Bun.
- Cờ tính năng: Cô lập các đường dẫn mã có vấn đề bằng cờ tính năng, cho phép di chuyển dần dần.
Điểm chuẩn đóng gói Docker
Đóng gói các ứng dụng Bun vào container rất đơn giản. Các hình ảnh Docker oven/bun chính thức cung cấp một cơ sở gọn nhẹ.
Dockerfile cho Node.js
# Dockerfile.node
FROM node:20-alpine AS base
WORKDIR /app
COPY package.json bun.lockb* ./
RUN npm install --frozen-lockfile
COPY . .
EXPOSE 3000
CMD ["node", "src/node-server.ts"]
Dockerfile cho Bun
# Dockerfile.bun
FROM oven/bun:1.2.0-alpine AS base
WORKDIR /app
COPY package.json bun.lockb* ./
# Bun automatically installs dependencies on 'bun run' if not present,
# but explicit install is good practice for build stage.
RUN bun install --frozen-lockfile
COPY . .
EXPOSE 3000
CMD ["bun", "run", "src/bun-server.ts"]
Phương pháp điểm chuẩn
Chúng ta sẽ so sánh kích thước hình ảnh và mức sử dụng bộ nhớ thời gian chạy cho một máy chủ HTTP đơn giản.
- Xây dựng hình ảnh:
bash
docker build -t node-app -f Dockerfile.node . docker build -t bun-app -f Dockerfile.bun . - Kích thước hình ảnh:
bash
docker images | grep "node-app\|bun-app" - Bộ nhớ thời gian chạy (sử dụng
docker stats):bashdocker run -d --name node-server -p 3001:3000 node-app docker run -d --name bun-server -p 3002:3000 bun-app docker stats --no-stream node-server bun-server # Stop containers after collecting stats docker stop node-server bun-server && docker rm node-server bun-server
Kết quả điểm chuẩn (Minh họa)
| Chỉ số | Node.js (20-alpine) | Bun (1.2.0-alpine) | Ghi chú |
|---|---|---|---|
| Kích thước hình ảnh | ~180 MB | ~100 MB | Hình ảnh cơ sở của Bun nhỏ hơn đáng kể. |
| Thời gian khởi động | ~500 ms | ~50 ms | Bun nhanh hơn nhiều lần. |
| Mức sử dụng bộ nhớ | ~30 MB | ~10 MB | Đối với một máy chủ HTTP đơn giản, Bun sử dụng ít hơn. |
| RPS (ab -c 50 -n 10000) | ~2500 RPS | ~7000 RPS | Bun.serve được tối ưu hóa cao. |
Lưu ý: Đây là các điểm chuẩn minh họa. Kết quả thực tế phụ thuộc rất nhiều vào độ phức tạp của ứng dụng, khối lượng công việc và phần cứng.
Những điều cần lưu ý và khắc phục sự cố trong sản xuất
-
Error: Cannot find module '...':- Nguyên nhân: Việc phân giải mô-đun của Bun nói chung là tương thích nhưng có thể khác nhau đối với các trường hợp đặc biệt hoặc nếu các đường dẫn
tsconfig.jsonkhông được cấu hình chính xác cho Bun. - Cách khắc phục:
- Đảm bảo
tsconfig.jsonpathsđược ánh xạ chính xác vàbaseUrlđược đặt. - Xác minh trường
package.jsonexportscho các gói đã nhập. - Kiểm tra các
bun installhoặcnpm installbị thiếu nếu sử dụng kết hợp. - Việc phân giải
node_modulescủa Bun có thể nghiêm ngặt hơn; đảm bảo tất cả các phụ thuộc được liệt kê rõ ràng. - Nếu sử dụng
require(), đảm bảo phần mở rộng tệp có mặt (ví dụ:require('./module.js')).
- Đảm bảo
- Nguyên nhân: Việc phân giải mô-đun của Bun nói chung là tương thích nhưng có thể khác nhau đối với các trường hợp đặc biệt hoặc nếu các đường dẫn
-
Lỗi Addon gốc:
- Nguyên nhân: Cố gắng sử dụng trực tiếp một addon C++ gốc của Node.js (tệp
.node) với Bun. - Cách khắc phục:
- Xác định gói có vấn đề.
- Tìm kiếm một giải pháp thay thế JavaScript thuần túy (ví dụ:
bcryptjsthay vìbcrypt). - Nếu không có giải pháp thay thế, hãy cân nhắc cô lập chức năng đó vào một microservice Node.js riêng biệt hoặc triển khai lại bằng
bun:ffi(nâng cao).
- Nguyên nhân: Cố gắng sử dụng trực tiếp một addon C++ gốc của Node.js (tệp
-
Sự khác biệt
process.env:- Nguyên nhân:
process.envcủa Bun phần lớn là tương thích, nhưng một số biến môi trường dành riêng cho Node.js có thể không có mặt hoặc hoạt động không giống nhau. - Cách khắc phục:
- Xác định rõ ràng các biến môi trường cần thiết trong môi trường triển khai của bạn.
- Tránh phụ thuộc vào các biến môi trường nội bộ rất cụ thể của Node.js.
- Nguyên nhân:
-
Không nhất quán API
Buffer:- Nguyên nhân: Mặc dù Bun hỗ trợ
Buffer, nhưng việc triển khai cơ bản của nó có thể có những khác biệt nhỏ so với Node.js, đặc biệt với các phương thứcBuffercũ hơn hoặc ít phổ biến hơn. - Cách khắc phục:
- Ưu tiên
Uint8ArrayvàTextEncoder/TextDecodertiêu chuẩn Web nếu có thể. - Kiểm tra kỹ lưỡng các đường dẫn mã liên quan đến thao tác
Buffer.
- Ưu tiên
- Nguyên nhân: Mặc dù Bun hỗ trợ
-
Hành vi
fs.watch/fs.watchFile:- Nguyên nhân: Việc theo dõi hệ thống tệp có thể phụ thuộc vào nền tảng và có các đặc điểm hiệu suất hoặc trình kích hoạt sự kiện khác nhau giữa Node.js và Bun.
- Cách khắc phục:
- Kiểm tra kỹ lưỡng việc theo dõi tệp trong môi trường sản xuất mục tiêu của bạn.
- Cân nhắc các giải pháp theo dõi tệp bên ngoài nếu hành vi gốc không nhất quán.
-
Bun.serveso với Máy chủ HTTP Node.jskeepAliveTimeout:- Nguyên nhân:
Bun.servecủa Bun cóidleTimeoutcho WebSockets và thời gian chờ kết nối chung. Node.js cókeepAliveTimeout. Các giá trị mặc định và hành vi có thể khác nhau. - Cách khắc phục:
- Cấu hình rõ ràng
idleTimeouttrongBun.servecho WebSockets. - Đối với HTTP, đảm bảo bộ cân bằng tải hoặc proxy của bạn xử lý thời gian chờ kết nối một cách duyên dáng, hoặc triển khai logic thời gian chờ tùy chỉnh trong trình xử lý
fetchcủa bạn nếu cần.
- Cấu hình rõ ràng
- Nguyên nhân:
Các câu hỏi thường gặp
-
Hỏi: Tôi có thể sử dụng các framework Node.js hiện có như Express hoặc NestJS với Bun không?
- Đ: Có, phần lớn là vậy. Bun hướng tới khả năng tương thích cao với Node.js, vì vậy nhiều framework sẽ chạy được. Tuy nhiên, bạn sẽ không nhận được toàn bộ lợi ích hiệu suất của
Bun.servecho việc xử lý HTTP. Để có hiệu suất tối ưu, hãy di chuyển các endpoint HTTP sangBun.servetrực tiếp hoặc sử dụng một framework gốc của Bun.bun installthường sẽ xử lý các phụ thuộc của framework.
- Đ: Có, phần lớn là vậy. Bun hướng tới khả năng tương thích cao với Node.js, vì vậy nhiều framework sẽ chạy được. Tuy nhiên, bạn sẽ không nhận được toàn bộ lợi ích hiệu suất của
-
Hỏi: Bun xử lý việc biên dịch TypeScript trong sản xuất như thế nào?
- Đ: Bun có một trình chuyển đổi TypeScript tích hợp. Bạn có thể chạy trực tiếp các tệp
.tsbằngbun run src/index.ts. Đối với sản xuất,bun buildcó thể biên dịch TypeScript của bạn thành JavaScript, sau đó có thể chạy. Điều này loại bỏ nhu cầu vềtschoặcts-node.
- Đ: Bun có một trình chuyển đổi TypeScript tích hợp. Bạn có thể chạy trực tiếp các tệp
-
Hỏi: Còn các trình điều khiển cơ sở dữ liệu khác ngoài SQLite (ví dụ: PostgreSQL, MySQL) thì sao?
- Đ: Đối với hầu hết các cơ sở dữ liệu quan hệ, bạn sẽ sử dụng các gói npm hiện có (ví dụ:
pg,mysql2,sequelize,prisma). Chúng thường là JavaScript thuần túy hoặc dựa vào các giao thức mạng tiêu chuẩn, vì vậy chúng thường hoạt động tốt với Bun.bun installsẽ quản lý các phụ thuộc của chúng.
- Đ: Đối với hầu hết các cơ sở dữ liệu quan hệ, bạn sẽ sử dụng các gói npm hiện có (ví dụ:
-
Hỏi:
bun:fficó phải là một giải pháp thay thế khả thi cho tất cả các addon gốc của Node.js không?- Đ:
bun:ffirất mạnh mẽ nhưng đòi hỏi nỗ lực đáng kể. Bạn cần viết mã C/C++/Rust, biên dịch nó thành một thư viện chia sẻ (.so,.dylib,.dll), sau đó định nghĩa các chữ ký hàm trong TypeScript đểbun:ffigọi. Đây là một giải pháp cho logic gốc quan trọng về hiệu suất, được cô lập, không phải là một giải pháp thay thế trực tiếp cho các addon Node.js phức tạp.
- Đ:
-
Hỏi: Làm cách nào để gỡ lỗi một ứng dụng Bun trong sản xuất?
- Đ: Bun hỗ trợ Giao thức Chrome DevTools. Bạn có thể khởi động Bun bằng
--inspecthoặc--inspect-brk(ví dụ:bun --inspect src/index.ts) và kết nối một trình gỡ lỗi (như trình gỡ lỗi của VS Code hoặc Chrome DevTools). Điều này cung cấp trải nghiệm gỡ lỗi quen thuộc tương tự như Node.js.
- Đ: Bun hỗ trợ Giao thức Chrome DevTools. Bạn có thể khởi động Bun bằng
Free In-Browser Developer Tools
Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.
Related Articles

Di chuyển từ Redis sang Valkey 8 trong môi trường Production: Nhân bản không downtime & Kiểm tra độ trễ
Hướng dẫn toàn diện về di chuyển từ Redis sang Valkey 8 trong môi trường production: nhân bản không downtime và kiểm tra độ trễ với kiến trúc cấp độ production cùng các ví dụ code.
Read more
Kafka vs Redpanda năm 2026: Kiến trúc Thread-per-Core, Zero-Disk Cache & Điểm chuẩn độ trễ P99
Hướng dẫn toàn diện so sánh Kafka và Redpanda năm 2026: kiến trúc thread-per-core, zero-disk cache và điểm chuẩn độ trễ P99 với kiến trúc cấp độ production và các ví dụ code.
Read morePostgreSQL Vacuum & Bloat Index: Phát hiện, Giảm thiểu và Tinh chỉnh Tự động
Chẩn đoán và loại bỏ tình trạng phình (bloat) bảng và index trong PostgreSQL. Nắm vững các công thức tinh chỉnh autovacuum, nén dữ liệu không downtime với pg_repack, và cơ chế visibility map của MVCC.
Read more