VS Code For Web 深入淺出 -- 進程間通訊篇

  • 2022 年 10 月 20 日
  • 筆記

在上一篇中,我們一起分析了 VS Code 整體的程式碼架構,了解了 VS Code 是由前後端分離的方式開發的。且無論前端是基於 electron 還是 web,後端是本地還是雲端,其調用方式並無不同。

這樣的架構下,前後端的通訊方式是如何實現的呢?本篇我們將一起來探究 VS Code For Web 的進程間通訊方式。

進程通訊與調用方式

進程間通訊協議

對於多進程架構的項目,進程之間的通訊會通過進程間調用 (Inter Process Calling, IPC)。VSCode 中自己設計了專門的 IPC 模組來實現通訊。程式碼位於 src/vs/base/parts/ipc。

export const enum RequestType {
    Promise = 100,
    PromiseCancel = 101,
    EventListen = 102,
    EventDispose = 103
}

從 enum type 可以看出,VSCode 的 IPC 模組同時支援兩種調用方式,一種是基於 Promise 的調用實現, 另一種是通過 Event Emitter/Listener 的那一套事件監聽機制來實現。

以事件監聽機製為例,VSCode 中採用 vscode-jsonrpc 這個包來封裝實現,調用方式如下:

import * as cp from 'child_process';
import * as rpc from 'vscode-jsonrpc/node';

let childProcess = cp.spawn(...);

// Use stdin and stdout for communication:
let connection = rpc.createMessageConnection(
    new rpc.StreamMessageReader(childProcess.stdout),
    new rpc.StreamMessageWriter(childProcess.stdin));

let notification = new rpc.NotificationType<string, void>('testNotification');

connection.listen();

connection.sendNotification(notification, 'Hello World');

服務端調用也採用類似的包裝:

import * as rpc from 'vscode-jsonrpc/node';

let connection = rpc.createMessageConnection(
    new rpc.StreamMessageReader(process.stdin),
    new rpc.StreamMessageWriter(process.stdout));

let notification = new rpc.NotificationType<string, void>('testNotification');
connection.onNotification(notification, (param: string) => {
    console.log(param); // This prints Hello World
});

connection.listen();

進程間通訊單元

為了實現客戶端與服務端之間的點對點通訊,我們需要一個最小單元來實現消息的調用與監聽。在 VSCode 中,這個最小單元即為 Channel。

/**
 * An `IChannel` is an abstraction over a collection of commands.
 * You can `call` several commands on a channel, each taking at
 * most one single argument. A `call` always returns a promise
 * with at most one single return value.
 */
export interface IChannel {
    call<T>(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
    listen<T>(event: string, arg?: any): Event<T>;
}

每次通訊過程,需要客戶端與服務端處於同一個 Channel 中。

進程間通訊建連

在 VSCode 中,客戶端與服務端之間的通訊建立是通過 Connection 類來建立,通過傳入客戶端與服務端的 Channel ,即 ChannelClient 與 ChannelServer 來實例化連接。

interface Connection<TContext> extends Client<TContext> {
    readonly channelServer: ChannelServer<TContext>;
    readonly channelClient: ChannelClient;
}

它們之間的區別是,由於服務端可以同時對多個客戶端服務,因此支援多個 Channel 的獲取,而ChannelClient 為一對一連接。

綜上,我們就梳理清楚了 VSCode 中 IPC 模組的基本架構,了解了進程間的通訊細節。

用一張圖總結梳理一下知識點:

20221010150243

由於 VSCode 的 IPC 模組天然支援非同步能力,因此事實上它並不區分進程是本地進程還是遠端進程,只要是通過 Channel 通訊的,都可以被認為是進程間通訊,都可以復用相同的程式碼編寫。

參考

VSCode 的官方文檔

VSCode API

VSCode 源碼解讀--IPC 通訊機制

vscode 源碼解析 - 進程間調用

Tags: