V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
vtexhtllo1
V2EX  ›  Node.js

Deno 原理详解,让我们一起从源码分析开始

  •  
  •   vtexhtllo1 · 2018-06-05 14:05:19 +08:00 · 4683 次点击
    这是一个创建于 2397 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Node 之父 ry:在“ Node 中的设计错误”演讲中表示:

    • 不允许将任意本地函数绑定至 V8 当中。
    • 所有系统调用都将通过消息传递完成( protobuf 序列化)。
    • 两项原生函数:send 与 recv。
    • 这既简化了设计流程,又使得系统更易于审计。

    这几点很大程度上体现出了 node 和 deno 在设计本质上的区别,同时这几点体现了 deno 的安全性(利用 JavaScript 本身即为安全沙箱这一事实)

    V8worker2 是 Go 和 V8 连接的桥梁

    • 允许从 GO 程序执行 JavaScript
    • 只允许 GO 和 V8 之间的消息传递(传统:暴露 C++函数作为函数在 JavaScript。)
    • 维护一个安全的 JS 沙箱
    • JS 中只允许绑定 3 个函数:send(), recv(), print()

    deno 架构图(Parsa Ghadimi 绘制) 从图中可以清晰的看出,V8worker2 是 v8 和 Go 之间实现调用的核心组件

    v8worker2 架构图

    可以看出 V8worker2 是通过 binding C++ 模块进行绑定 V8,bingding 暴露了基础操作方法:v8_init() 、worker_load()、worker_send_bytes()、worker_dispose()...提供给 GO 进行调用

    //binding.h
    const char* worker_version();
    void worker_set_flags(int* argc, char** argv);
    void v8_init();
    worker* worker_new(int table_index);
    int worker_load(worker* w, char* name_s, char* source_s);
    const char* worker_last_exception(worker* w);
    int worker_send_bytes(worker* w, void* data, size_t len);
    void worker_dispose(worker* w);
    void worker_terminate_execution(worker* w);
    

    通过 Golang 的 GC 提供的 CGO 模块调用 C 语言暴露的方法,就可以实现 GO 和 V8 之间的通信了:

    1. 创建一个实例:v8worker2.New(ReceiveMessageCallback)
    2. 加载执行 JS:worker.Load(scriptName,codeString)
    // worker.go
    package v8worker2
    
    import "C"
    ...
    
    func recvCb(buf unsafe.Pointer, buflen C.int, index workerTableIndex) C.buf {
        ...
    }
    
    func New(cb ReceiveMessageCallback) *Worker {
        ...
    	initV8Once.Do(func() {
    		C.v8_init()
    	})
    }
    
    func (w *Worker) Load(scriptName string, code string) error {
        ...
    	r := C.worker_load(w.worker.cWorker, scriptName_s, code_s)
    ...
    }
    
    func (w *Worker) SendBytes(msg []byte) error {
        ...
    	r := C.worker_send_bytes(w.worker.cWorker, msg_p, C.size_t(len(msg)))
    }
    

    案例演示

    • 实现 Js 中的 console.log() 方法
    • Js 发送数据给 Go
    • Go 发送数据给 Js
    // hello.go
    package main
    
    import (
    	"fmt"
    
    	"github.com/ry/v8worker2"
    )
    
    func main() {
    	worker := v8worker2.New(recv)
    
    	// 实现 JS 的 console.log 方法
    	err := worker.Load("hello.js", `
    		this["console"] = {
    			log(...args) {
    				V8Worker2.print(args)
    			}
    		};
    		console.log("Hello World");
    	`)
    
    	if err != nil {
    		fmt.Println(err)
    	}
    
    	// 发送数据给 GO
    	err = worker.Load("sendData.js", `
    		V8Worker2.send(new ArrayBuffer(5))
    	`)
    	if err != nil {
    		fmt.Println(err)
    	}
    
    	// 发送数据给 JS
    	err = worker.Load("recvData.js", `
    		V8Worker2.recv(function(msg) {
    			const len =msg.byteLength;
    			console.log("recv data from go,length: "+len);
    		});
    	`)
    	if err != nil {
    		fmt.Println(err)
    	}
    	err = worker.SendBytes([]byte("abcd"))
    
    }
    
    func recv(buf []byte) []byte {
    	fmt.Println("recv data from js,length:", len(buf))
    	return nil
    }
    

    在控制台运行: go run hello.go 运行结果

    需要运行测试代码,可以直接访问我的 github:deno 案例源码

    参考资料

    2 条回复    2018-06-06 16:58:09 +08:00
    nine99
        1
    nine99  
       2018-06-05 23:53:54 +08:00
    不错的资料
    jziwenchen
        2
    jziwenchen  
       2018-06-06 16:58:09 +08:00
    加了这么多东西 性能不会很差吗 ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5548 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 06:53 · PVG 14:53 · LAX 22:53 · JFK 01:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.