[VSCode 源码阅读笔记] VSCode 的进程间通讯 ( 二 )

ChannelClient 和 ChannelServer

Posted by Nicodechal on 2020-04-03

本文介绍 VSCode 中的 ChannelServer 和 ChannelClient。

前面提到,VSCode 中 Server 对象维护了一个 Channel 的列表,客户端连接 Server 时可以会创建一个连接 ConnectionConnection 中的 channelClient 可以发起请求调用方法或监听事件,请求打包成一条消息发送给 channelServerchannelServer 负责和 ServerChannel 交互得到响应的结果,再通过消息将结果返回给 channelClient。本文主要介绍 channelClientchannelServer 之间的通讯细节。

1
2
3
4
5
6
7
              call / listen         call / listen
result response
<------------ <------------
channelClient Message channelServer
------------> ------------>
call / listen do
request call / listen

channelClient 进行 calllisten

调用 channelClientgetChannel 方法可以得到一个可以调用 channelServer 中的 Channel 的本地 Channel ( 或者说客户端 Channel )。调用该 Channelcall 方法,会调用 channelServer 中对应的 Channelcall 方法 ( 向 ChannelServer 发起请求 ),channelServer 中对应的 Channelcall 执行后会将结果返回给本地 Channel,最终由本地 Channel 返回包含调用结果的 Promise

事件处理类似,调用本地 Channellisten 方法,会得到一个本地的监听器,当 channelServer 中对应的事件触发时,会触发该监听器并带上响应的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ChannelClient {
// ...
getChannel<T extends IChannel>(channelName: string): T {
const that = this;

return {
call(command: string, arg?: any, cancellationToken?: CancellationToken) {
return that.requestPromise(channelName, command, arg, cancellationToken);
},
listen(event: string, arg: any) {
return that.requestEvent(channelName, event, arg);
}
} as T;
}
// ...
}

发起 call 请求

发起 call 请求使用 requestPromise 实现。requestPromise 先为本次请求计算一个请求 ID,然后为本次请求注册一个 handler ( 和 ID 相关联 ),当 channelServer 返回结果时,会触发 protocolonMessage ( 此时会调用 onBuffer 方法,该方法又会调用 onResponse 方法),此时根据请求 ID 调用对应的 handler,最终 handler 将结果包裹在 Promise 中返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private requestPromise(channelName: string, name: string, arg?: any, cancellationToken = CancellationToken.None): Promise<any> {
// 得到 ID
const id = this.lastRequestId++;
// 返回的 Promise
const result = new Promise((c, e) => {
if (cancellationToken.isCancellationRequested) {
return e(errors.canceled());
}

let uninitializedPromise: CancelablePromise<void> | null = createCancelablePromise(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;

// 建立本消息的 handler,在 onResponse 中调用
const handler: IHandler = response => {
switch (response.type) {
case ResponseType.PromiseSuccess:
this.handlers.delete(id);
// 执行成功 resolve 结果
c(response.data);
break;

// ...
}
};
// 这里把 handler 注册到消息监听器列表,得到响应时会调用。
this.handlers.set(id, handler);
// 发送请求
this.sendRequest(request);
});

// ...
});
// 返回包含结果的 Promise
return result.finally(() => this.activeRequests.delete(disposable));
}
private onResponse(response: IRawResponse): void {
// 得到相应的 handler 并执行
const handler = this.handlers.get(response.id);
if (handler) {
handler(response);
}
}

发起 listen 请求

listen 原理和 call 类似:

1
2
3
4
5
6
7
8
9
10
11
12
private requestEvent(channelName: string, name: string, arg?: any): Event<any> {
// 请求 ID
const id = this.lastRequestId++;
// 本地的 emitter
const emitter = new Emitter<any>(/* */);
// onResponse 调用该方法触发事件
const handler: IHandler = (res: IRawResponse) => emitter.fire((res as IRawEventFireResponse).data);
// 注册 handler
this.handlers.set(id, handler);
// 返回本地事件监听器
return emitter.event;
}

channelServer 处理 calllisten 请求

channelServer 处理 calllisten 请求从得到请求开始,channelServer 通过 protocolonMessage 事件得到请求,并使用 onRawMessage 对不同类型的消息进行处理 ( 使用 onPromise 处理函数调用请求,使用 onEventListen 处理事件监听请求 )。

处理 call 请求

onPromise 首先从 Channel 列表中取出请求的 Channel,如果有相应的 Channel 直接调用相应的 call 方法。得到的结果使用 sendResponse 直接发给客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private onPromise(request: IRawPromiseRequest): void {
const channel = this.channels.get(request.channelName);

let promise: Promise<any>;

try {
// 这里调用相应的方法
promise = channel.call(this.ctx, request.name, request.arg, cancellationTokenSource.token);
} catch (err) {
promise = Promise.reject(err);
}

const id = request.id;

promise.then(data => {
// 将得到的结果发给客户端
this.sendResponse(<IRawResponse>{ id, data, type: ResponseType.PromiseSuccess });
}, err => {/* */});
}

处理 listen 请求

过程与 call 类似,当事件触发时,向客户端发送事件触发消息,并带上相应的数据:

1
2
3
4
5
6
7
8
9
private onEventListen(request: IRawEventListenRequest): void {
const channel = this.channels.get(request.channelName);

const id = request.id;
// 根据参数的到相应的 Event
const event = channel.listen(this.ctx, request.name, request.arg);
// 当事件触发时发送事件出发的数据给客户端
event(data => this.sendResponse(<IRawResponse>{ id, data, type: ResponseType.EventFire }));
}

小结

总的来说,channelServer 负责维护一组 Channel,接收请求调用 Channel 并响应相应的结果。channelClient 则对外提供 getChannel 接口,用于取得一个可以调用 channelServer 上的指定 ChannelChannel,用户直接调用这个 Channel 的方法 ( calllisten ) 相当于直接调用对应的 channelServer 上的 Channel。总结如图:

1
2
3
4
5
6
7
              call / listen         call / listen
result response
<------------ <------------
channelClient Message channelServer
------------> ------------>
call / listen do
request call / listen