vscode server源码解析(四) - vscode server内核

前面我们说明了code server是如何把ide作为一个服务提供出去的,code server除了提供服务器功能外,最重要的事情当然就是加载ide内核代码了

Code Server加载vscode内核

我们回顾一下code server加载vscode内核相关的代码,loadAMDModule函数这个方法本质就是一个require,引入了vscode内核代码中的out/bootstrap-amd这个文件里面的load方法,然后通过vscode原生的模块加载机制加载vs/server/node/server.main下的createServer方法,获取到的createVSServer方法负责在当前进程里面加载vscode内核代码。
这也就是我们前面架构图上说明的,code server和vscode内核虽然逻辑上独立,但是是存在同一个进程的,vscode内核其实是作为一个模块被code server加载的

src/node/routes/vscode.ts

const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")

this._codeServerMain = await createVSServer(null, {
  ...(await toCodeArgs(args)),
  "without-connection-token": true,
})

export const loadAMDModule = async <T>(amdPath: string, exportName: string): Promise<T> => {
  process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] =
    process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] || path.join(vsRootPath, "remote", "node_modules")
    
  require(path.join(vsRootPath, "out/bootstrap-node")).injectNodeModuleLookupPath(
    process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"],
  )

  const module = await new Promise<AMDModule<T>>((resolve, reject) => {
    require(path.join(vsRootPath, "out/bootstrap-amd")).load(amdPath, resolve, reject)
  })

  return module[exportName] as T
}

vscode内核加载

好吧,现在让我们进入vscode源代码,vscode server加载逻辑入口在src/vs/server/node/server.main.ts,也就是前面code server加载的模块,这个createServer调用doCreateServer方法,具体内容都在doCreateServer里面
这里面简化下来做了这几个重要的事情
1、setupServerService明确哪些服务代码需要被加载,初始化各项服务,准备之后服务间的依赖注入
2、初始化RemoteExtensionHostAgentServer,这个是最核心的类,我们等下会重点说,他处理了code sever转发过来的http请求,以及之后的websocket连接,其实搞懂了这个类,vscode的远程开发也就基本能弄懂了

export function createServer(address: string | net.AddressInfo | null): Promise<IServerAPI> {
	return doCreateServer(address, args, REMOTE_DATA_FOLDER);
}

export async function createServer(address: string | net.AddressInfo | null, args: ServerParsedArgs, REMOTE_DATA_FOLDER: string): Promise<IServerAPI> {
    // 初始化各项服务,准备依赖注入
	const { socketServer, instantiationService } = await setupServerServices(connectionToken, args, REMOTE_DATA_FOLDER, disposables);
	// 初始化真正的服务器管理类RemoteExtensionHostAgentServer
	const remoteExtensionHostAgentServer = instantiationService.createInstance(RemoteExtensionHostAgentServer, socketServer, connectionToken, vsdaMod, hasWebClient);
}

RemoteExtensionHostAgentServer本身代码就很多,我们先看一个接口

export interface IServerAPI {
	handleRequest(req: http.IncomingMessage, res: http.ServerResponse): Promise<void>;
	handleUpgrade(req: http.IncomingMessage, socket: net.Socket): void;
	handleServerError(err: Error): void;
	dispose(): void;
}

RemoteExtensionHostAgentServer就是实现了这个接口的,我们今天也就从接口的功能来说,其实最重要的两个事情也就是
1、handleRequest处理http请求
2、handleUpgrade处理websocket连接

class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI {}

前端静态资源服务器

怎么突然跳到前端了呢?是不是很突兀,其实这里我想说的是,上面提到的handleRequest,这里要处理的前端http请求最重要的就是前端的静态资源
也就是说,我们这个ide后台同时承担了前端静态资源服务器的功能,相当于一个tomcat,只不过我们前后端代码都打在一个包里了(但是,运行的时候,还是一个前后端分离的架构,前端在浏览器,后端在远程机器)
this._webClientServer.handle就是去返回前端UI所需文件的方法,我们往里面看,第一个请求(“/”)来的时候,handleRoot方法会将workbench.html(vscode前端加载的入口文件)返回,workbench.html里面通过

<script src="{{WORKBENCH_WEB_BASE_URL}}/out/vs/code/browser/workbench/workbench.js"></script>

浏览器加载workbench.html后,会根据script中的地址进一步请求需要的资源,这个时候就走到handleStatic方法,读取相应的文件并返回

// workbench web UI
if (this._webClientServer) {
	this._webClientServer.handle(req, res, parsedUrl);
	return;
}

async handle(req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
	try {
		const pathname = parsedUrl.pathname!;

		if (pathname.startsWith(this._staticRoute) && pathname.charCodeAt(this._staticRoute.length) === CharCode.Slash) {
			//
			return this._handleStatic(req, res, parsedUrl);
		}
		if (pathname === '/') {
			// 返回替换模版后的workbench.html入口文件
			return this._handleRoot(req, res, parsedUrl);
		}
	}
}

我们可以在前端按F12打开dev tools查看Network中的资源加载情况,还是可以明显看出来请求了workbench.html,然后再去加载workbench.html中引用的js文件
在这里插入图片描述

websocket服务器

我们除了前端资源的加载依靠http协议(handleRequest),其他功能实现基本上都是靠websocket协议了,比如文件查看编辑、插件运行(代码调试、语法高亮)等,也正是因为websocket特别重要,我们单独一个章节来说明,我们现在只需要知道,RemoteExtensionHostAgentServer中处理了websocket连接建立的逻辑,而且是两种websocket连接,一个主进程,一个插件进程,就可以了

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值