deno深入揭秘及未来展望
denonode.js之父Ryan Dahl在一个月前发起了名为deno的项目,项目的初衷是打造一个基于v8引擎的安全的TypeScript运行时,同时实现HTML5的基础API。所谓的安全运行时,是将TS代码运行在一个沙盒里,访问受限的文件系统、网络功能,这比较类似于web里的iframe sandbox。
现阶段,deno的变化可谓翻天覆地。Ryan的项目一个月前提供了golang版本的deno简易源码,而如今不仅仅重构了项目,底层语言都切换为c++,接口也做了很大的更新,这源自于社区内热情的讨论,有太多太多的开发者、协作人员提出了太多的优化以及改进意见,这也就导致接下来未来几个月deno仍然会出现大改变,这在后文会提及。现在,我就带领大家进入最初的deno微观世界探索deno最初的设计。
架构
q 本文讲解deno的golang版本,当前最新的deno由于性能问题放弃了golang的实现,但这不影响我们分析deno的原理。未来在七月deno估计会释放出基于Rust的底层特权级实现,性能更优。
q 由于deno涉及之处是为了直接运行TS,因此下文会用TS来代指JS(现阶段TS没有自己的运行时,仍是基于编译为JS在运行在v8)
deno的设计初期来看比较简单,宏观上看包括三部分:deno的go运行时、v8引擎以及连接go运行时和v8的v8worker2库。
go运行时是deno的特权级,它负责deno对系统资源的申请、使用、释放;v8引擎此处不仅仅执行JS代码,同时也负责TypeScript的编译;而v8worker2负责go与v8的全双工通信,通过ArrayBuffer传输数据,传输的协议规范为protobuf。
深入到go运行时里,目前deno对TS层提供了几种能力:Console、fetch、fs、module、timer、stack trace,虽然有些功能没有提供用户端API,不过golang的接口已完成,扩展很容易。
go运行时
deno在特权级代码执行了3端逻辑:
[*]初始化go运行时环境
[*]初始化TS运行时环境
[*]启动go这一侧的事件循环(该事件循环不同于node的基于libuv的event loop,下文会提到)
初始化go运行时环境
// HOME目录下创建 cache和src目录 createDirs()
// 利用 afero 库创建虚拟fs对象;同时订阅 v8端的 os事件,在go端实现 文件抓取、获取缓存、磁盘I/O,同时返回 proto序列化数据 给v8
InitOS()
// 心跳
InitEcho()
// 接受v8消息,进行 timeout、interval和clear
InitTimers()
// 订阅 fetch 事件,代理服务器。当代理请求结束时,返回两个消息:第一个为状态码;第二个为body体
InitFetch()
// recv为 v8->go 的回调函数,处理v8的消息
worker = v8worker2.New(recv)
// 初始化ts的相关环境,和go端对应
main_js = stringAsset("main.js")
err := worker.Load("/main.js", main_js)
exitOnError(err)
依次执行以下任务:
- 创建缓存目录,存储TS文件编译后的JS文件
- 订阅 os 事件,处理来自v8层的操作,如fs等
- 订阅 timer 事件,处理来自v8的定时器操作
- 订阅 fetch 事件,处理来自v8的http request
- 初始化v8worker2实例,实现go与v8的绑定
- 加载js入口文件main.js,该文件定义了js的全局接口、初始化逻辑和与go运行时通信的方法,等待下一阶段的执行。
初始化js运行时环境
// v8端执行 denoMain函数,在main.ts中定义
deno.Eval("deno_main.js", "denoMain()")
上一步v8已经加载并执行了main.js文件,现在该执行denoMain方法了。denoMain是在main.js中定义的初始化方法,它定义了deno在js层的API以及v8worker实例,也是开发者密切相关的一层。
关于ts层的逻辑留在下文讲述。
启动事件循环
var resChan = make(chan *BaseMsg, 10) var doneChan = make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
first := true
// In a goroutine, we wait on for all goroutines to complete (for example
// timers). We use this to signal to the main thread to exit.
// wg.Add(1) basically translates to uv_ref, if this was Node.
// wg.Done() basically translates to uv_unref
go func() {
wg.Wait()
doneChan
页:
[1]