Extension Host & RPC
← Về MOC · Liên quan: Kiến trúc đa tiến trình (Multi-process)
1. Extension Host là gì?
Theo tài liệu chính thức Extension Host (code.visualstudio.com): Extension Host là tiến trình chịu trách nhiệm chạy extension. Nó bảo vệ trình soạn thảo bằng cách không cho extension làm chậm khởi động, làm chậm thao tác UI hay tự ý sửa giao diện. Extension được nạp lười (lazy) theo activation event – ví dụ extension Markdown chỉ nạp khi bạn mở file Markdown, nhờ đó tiết kiệm CPU và bộ nhớ.
3 loại extension host
| Loại | Môi trường | Chạy ở đâu |
|---|---|---|
| Local | Node.js | Cùng máy với giao diện |
| Web | Web Worker của trình duyệt | Trong trình duyệt hoặc cục bộ |
| Remote | Node.js | Trong container, máy SSH, WSL… |
| Cấu hình | Local | Web | Remote |
|---|---|---|---|
| VS Code desktop | ✅ | ✅ | ❌ |
| VS Code + remote (Container, SSH, WSL, Codespace, Tunnel) | ✅ | ✅ | ✅ |
| VS Code for the Web (vscode.dev, github.dev) | ❌ | ✅ | ❌ |
| VS Code for the Web + Codespaces | ❌ | ✅ | ✅ |
- Extension chạy Node.js khai báo file vào ở trường
maintrongpackage.json; extension chạy web khai báo ở trườngbrowser. - Trường
extensionKind(mảng, ví dụ["workspace"],["ui", "workspace"]) cho biết extension muốn chạy gần giao diện (ui) hay gần workspace (thư mục dự án). Đa số extension dùng["workspace"].
Mã nguồn tương ứng trong repo (src/vs/workbench/services/extensions/):
electron-browser/localProcessExtensionHost.ts– host Node.js cục bộbrowser/webWorkerExtensionHost.ts– host Web Workercommon/remoteExtensionHost.ts– host từ xacommon/rpcProtocol.ts,common/proxyIdentifier.ts– giao thức RPC
2. File định nghĩa API: vscode.d.ts
src/vscode-dts/vscode.d.ts(~21.200 dòng): toàn bộ API ổn định mà extension được dùng. Gói npm@types/vscode(trên DefinitelyTyped) được cập nhật bằng cách chép file này từ nhánh release (wiki Publish vscode types).src/vscode-dts/vscode.proposed.<tên>.d.ts(~180 file): API đề xuất – đang thử nghiệm.
Proposed API (API đề xuất)
Theo wiki Extension API Process: muốn thử proposed API phải dùng bản Insiders, thêm
"enabledApiProposals": ["<tên>"]vàopackage.jsonvà chép file.d.tstương ứng. Không được publish extension dùng proposed API lên Marketplace (trừ danh sách ngoại lệ do Microsoft quản lý trongproduct.json), vì API có thể thay đổi phá vỡ tương thích.
3. Kiến trúc hai nửa: ExtHost* ↔ MainThread*
Thư mục src/vs/workbench/api/ hiện thực API theo mô hình hai nửa, giao tiếp bằng RPC (Remote Procedure Call – gọi hàm ở tiến trình khác như gọi hàm cục bộ):
| Nửa | Thư mục | Số file | Chạy ở |
|---|---|---|---|
ExtHost* | api/common/extHost*.ts | 115 | Tiến trình extension host |
MainThread* | api/browser/mainThread*.ts | 98 | Tiến trình renderer (workbench) |
| Giao thức | api/common/extHost.protocol.ts | 1 | Dùng chung – định nghĩa “hình dạng” (Shape) của mọi lời gọi |
| Ráp API | api/common/extHost.api.impl.ts | 1 | Tạo đối tượng vscode mà extension import |
sequenceDiagram participant Ext as Extension của bạn participant EH as ExtHostStatusBar<br/>(extension host) participant MT as MainThreadStatusBar<br/>(renderer) participant SB as Status bar service<br/>(workbench) Ext->>EH: item.text = "$(database) BRAVO"<br/>item.show() EH->>MT: RPC: $setEntry(...) MT->>SB: cập nhật mục trên status bar SB-->>MT: người dùng di chuột (cần tooltip) MT->>EH: RPC: $provideTooltip(...)
Ví dụ lần theo code: window.createStatusBarItem
Bước 1 – Giao thức (extHost.protocol.ts): định nghĩa interface “Shape” mà phía main thread phải hiện thực. Quy ước: phương thức RPC bắt đầu bằng $.
// trích src/vs/workbench/api/common/extHost.protocol.ts
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: string, statusId: string, extensionId: string | undefined, statusName: string,
text: string, /* ... */ priority: number | undefined, /* ... */): void;
$disposeEntry(id: string): void;
}
// Định danh proxy cho cả hai chiều
export const MainContext = {
// ...
MainThreadStatusBar: createProxyIdentifier<MainThreadStatusBarShape>('MainThreadStatusBar'),
};
export const ExtHostContext = {
// ...
ExtHostStatusBar: createProxyIdentifier<ExtHostStatusBarShape>('ExtHostStatusBar'),
};Bước 2 – Phía extension host (extHostStatusBar.ts): lấy proxy và gọi $setEntry.
// trích (rút gọn) src/vs/workbench/api/common/extHostStatusBar.ts
this._proxy = mainContext.getProxy(MainContext.MainThreadStatusBar);
// ... khi thuộc tính item thay đổi:
this.#proxy.$setEntry(this._entryId, id, this._extension?.identifier.value, name, this._text, /* ... */);Bước 3 – Phía main thread (mainThreadStatusBar.ts): hiện thực Shape, dùng service của workbench qua DI.
// trích src/vs/workbench/api/browser/mainThreadStatusBar.ts (thêm chú thích)
@extHostNamedCustomer(MainContext.MainThreadStatusBar) // đăng ký làm "khách hàng" của RPC
export class MainThreadStatusBar extends Disposable implements MainThreadStatusBarShape {
constructor(
extHostContext: IExtHostContext, // tham số thường – đứng trước
@IExtensionStatusBarItemService private readonly statusbarService: IExtensionStatusBarItemService
) {
super();
// Proxy chiều ngược lại: gọi về extension host khi cần (vd: lấy tooltip)
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStatusBar);
}
$setEntry(entryId: string, /* ... */): void { /* cập nhật status bar */ }
}Bước 4 – Ráp vào đối tượng vscode (extHost.api.impl.ts): hàm createStatusBarItem trong namespace window gọi tới ExtHostStatusBar.
Công thức tìm code của một API bất kỳ
- Tìm tên API trong
src/vscode-dts/vscode.d.ts(ví dụcreateStatusBarItem).- Tìm cùng tên trong
src/vs/workbench/api/common/extHost.api.impl.tsđể biết lớpExtHost…nào xử lý.- Trong lớp đó, tìm lời gọi
$…→ traMainThread…ShapetrongextHost.protocol.ts.- Mở
src/vs/workbench/api/browser/mainThread….tsđể xem phía workbench.
4. Vì sao thiết kế như vậy?
- Extension không bao giờ chạm trực tiếp vào DOM của workbench → không làm hỏng hay làm chậm giao diện.
- Dữ liệu qua RPC phải serializable (tuần tự hóa được thành JSON/buffer), nên API dùng DTO (Data Transfer Object – đối tượng chuyển dữ liệu) như
StatusBarItemDto. - Cùng một API chạy được trên host cục bộ, web worker hay từ xa, vì chỉ khác kênh truyền.
Liên kết
- Các extension đi kèm sản phẩm: Built-in Extensions – extension tích hợp sẵn
- Tự viết extension: Thực hành: viết extension đầu tiên
- Quy trình đề xuất API mới: Quy trình đóng góp (Issue, PR, API)