在上一篇中,我們一起分析了 VS Code 整體的代碼架構,瞭解了 VS Code 是由前後端分離的方式開發的。且無論前端是基於 electron 還是 web,後端是本地還是雲端,其調用方式並無不同。 這樣的架構下,前後端的通信方式是如何實現的呢?本篇我們將一起來探究 VS Code For We ...
在上一篇中,我們一起分析了 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 模塊的基本架構,瞭解了進程間的通信細節。
用一張圖總結梳理一下知識點:
由於 VSCode 的 IPC 模塊天然支持非同步能力,因此事實上它並不區分進程是本地進程還是遠端進程,只要是通過 Channel
通信的,都可以被認為是進程間通信,都可以復用相同的代碼編寫。