最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 更了不起的Deno

    正文概述 掘金(huge)   2020-12-07   650

    前言

    Deno从诞生之初,就一直受到广大开发者的关注。曾经还因为issue事件,掀起一阵全民狂欢。主要还是因为他的作者太特殊,那个创造了node的男人。本人也是众多关注者中的一个,恰好团队把Deno立为一个技术规划方向,于是就抽出时间好好研究了下,趁着周末和大家分享一波,也作为自己的一个总结。

    本文会对Deno做个全方面的介绍,包括基础篇,架构篇,实现篇,生态篇

    预备知识

    在学习Deno之前,需要先掌握一些前置知识,以便更好的理解Deno的实现和代码细节

    ArrayBuffer

    ArrayBuffer诞生的背景是因为WebGL,为了满足 JavaScript 与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式.于是设计了这一套标准

    要操作这段内存空间,需要使用视图,JS中定义了两种类型的视图:

    • TypedArray—数组成员都是同一个数据类型视图
    • DataView—数组成员是不同数据类型的视图

    TypedArray是一系列视图的合集,包括Uint8Array(无符号 8 位整数)数组视图, Int16Array(16 位整数)数组视图等等,DataView则是这些视图的组合(数组中会包含多种数据类型),来个例子:

    const buffer = new ArrayBuffer(12);
    
    const x1 = new Int32Array(buffer);
    x1[0] = 1;
    const x2 = new Uint8Array(buffer);
    x2[0]  = 2;
    
    x1[0] // 2
    //x1和x2共享同一段内存,所以会互相影响,如果设置x2[1] = 2,x1[0]=?
    

    编解码

    主要就是将字符串和TypedArray进行互相转换,Deno中文件处理会大量使用,例子: 更了不起的Deno

    new TextEncoder().encode('copy') ===> Uint8Array(4) [99, 111, 112, 121]

    基础篇

    可能大家对Deno都不是很了解,先简单介绍下

    更了不起的Deno

    从Deno的发展历程中,可以看出,作者创造Deno时,借鉴了很多业界的先进经验,以及自己多年的技术沉淀,所以在设计Deno时会有一些和node明显的区别,先上一段代码感受下:

    //index.js
    import { serve } from "https://deno.land/std@0.79.0/http/server.ts";
    
    let str: string = "hello";
    
    const text = Deno.readFileSync("./data.txt");
    
    window.addEventListener("load", ()=>{});
    
    window.addEventListener("unload", ()=>{});
    
    
    
    deno run  --allow-read index.js
    

    从中可以看出一些特色:

    内置APi

    Deno中api很多都是直接挂载在Deno全局对象上的,不像node进行了分类:fs,crypto,path,url这些模块。使用比较方便,但似乎不太好管理

    模块系统

    Deno中的模块都是直接通过url直接引入的,没有node_modules这个黑洞。将npm的中心化管理变成了去中心化,对此业界讨论比较大,可能url引入不是最完美的,但是至少是解决了npm的很多问题,对模块化的一种积极尝试,未来让我们拭目以待

    安全模型

    Deno中对应用的权限进行了分类,需要显式指定文件、网络和环境权限,否则运行文件会报错 一般通过 --allow-*来指定对应的权限,详细的细节,会在实现篇中进行讲解

    Typescript

    Deno中内置了ts解析引擎,直接编写ts代码即可,Deno会自动解析,转成对应的JS代码

    兼容浏览器

    Deno从设计之初,就是拥抱浏览器,ECMAScript等标准,让前端代码尽可能地在Deno中运行。我们可以在Deno中使用window对象,还可以用addEventListener监听事件,以及使用console和URL等等,未来支持的会越来越多

    和node简单的区别

    • 事件和异步
      • 回调 ===> promise
    • 多进程
      • child_process ===> worker
    • IO操作
      • Buffer ===> TypedArray
    • 事件循环
      • libuv ===> Tokio

    到这,如果之前完全没接触过Deno的,应该有个大致的认识了,了解了Deno的一些编写风格,由于本篇不是一个使用手册,不会对所有细节进行阐述,大家可以参考官方文档 查看更多细节。

    内置工具集

    • 内置格式化工具:deno fmt myfile1.ts
      • // deno-fmt-ignore ---忽略下一行代码
      • // deno-fmt-ignore-file---忽略整个文件
    • 构建工具:deno bundle './colors.ts' colors.bundle.js
    • 生成文档:deno doc add.ts --json
    • 代码检查:deno lint --unstable myfile1.ts

    Deno中内置了很多开发工具,可以看出想把当前的node生态太过分散,开发者们疲于应付各种层出不穷的工具的困扰的现状进行改进,让代码编码,构建,语法规范这些都交给Deno来做,未来这些工具集的不断完善,很有可能会改变前端百花齐放的生态

    架构篇

    首先,我们去github上看下Deno的源码结构: 更了不起的Deno

    这是目前(2020-12-5)Deno代码库的划分,对其中的一些模块进行了标注,方便大家自行研究时有个分类

    谈到架构,就得上官网的核心架构图,不过这张图更新太慢,有些实现已经变了 更了不起的Deno

    主要变化就是libdeno已经替换成rusty_v8,js可以直接和rust进行交互了,使用rust就可以完成功能开发,后面会介绍。Tokio是一个事件驱动的非阻塞I/O平台,用于使用Rust编程语言编写异步应用程序,相当于node中libuv。Deno里面js与rust的交互都会通过Deno.core.send和Deno.core.recv这两个方法,send发起同步任务,recv用来触发异步回调。Deno为了安全的原因,对资源都做了一层映射,原本系统的文件描述符或者其他资源都映射成了一个rid(非负整数)。rust和Tokio之间通过ops进行任务的关联,ops是一个系统操作映射表,将操作和一个唯一的id进行关联,结构为:

    更了不起的Deno

    更了不起的Deno 这是官网的一张类比表,可以看出Deno团队想把Deno打造的和linux一样,成为一个JS的工作平台,而不单是一门服务端语言

    实现篇

    接下来,我们一起从源码的角度去看下Deno中的一些模块的具体实现

    安全模型

    上文提过,我们通过命令行运行Deno程序的时候,都会显示地指定具体的权限:

    deno run --allow-read=/foo/bar 'https://xxxx'

    在Deno内部是通过permission模块来管理这些权限的,上面的命令在Deno源码中会被解析为:

    const desc = { name: "read", path: "/foo/bar" } as const;

    Deno还会暴露三个核心Api来让用户去管理权限:

    console.log(await Deno.permissions.query(desc));
    // PermissionStatus { state: "granted" }
    
    const status1 = await Deno.permissions.request(desc);
    // ⚠️ Deno requests read access to "/foo". Grant? [g/d (g = grant, d = deny)] g
    console.log(status1);
    // PermissionStatus { state: "granted" }
    
    console.log(await Deno.permissions.revoke(desc));
    // PermissionStatus { state: "prompt" }
    

    permission类中通过状态去标识对应的权限:

    pub enum PermissionState {
      Granted = 0,
      Prompt = 1,
      Denied = 2,
    }
    
    pub struct Permissions {
      pub read: UnaryPermission<PathBuf>,
      pub write: UnaryPermission<PathBuf>,
      pub net: UnaryPermission<String>,
      pub env: PermissionState,
      pub run: PermissionState,
      pub plugin: PermissionState,
      pub hrtime: PermissionState,
    }
    

    从代码中可以清晰看到Deno的权限都有哪几类,接下来,我们以env权限为例,因为它的代码较少,方便梳理,源码中的实现为:

    pub fn request_env(&mut self) -> PermissionState {
        if self.env == PermissionState::Prompt {
          if permission_prompt("Deno xxx") {
            self.env = PermissionState::Granted;
          } else {
            self.env = PermissionState::Denied;
          }
        }
        self.env
      }
    
    pub fn query_env(&self) -> PermissionState {
        self.env
     }
     
     pub fn revoke_env(&mut self) -> PermissionState {
        if self.env == PermissionState::Granted {
          self.env = PermissionState::Prompt;
        }
        self.env
     }
    

    可以看到代码结构很清晰,其中permission_prompt方法会输出终端,接受用户输入,更改权限状态,其他的逻辑就是根据对应的类型修改状态了

    那么Deno程序中是怎么运用这个权限的呢,我们的程序就是一个mainWorker,会有一个全局状态: 更了不起的Deno

    permission就是其中的一个属性,flags属性也很关键,是我们命令行的一些参数构成的对象,通过这张图就能清楚的知道permission是怎么运作的了: 更了不起的Deno

    执行机制

    在讲执行机制之前,先过一下之前提到过的Tokio:

    Tokio中的异步模型,是基于future实现的,熟悉Rust语言的应该很了解。基于我们熟悉的promise,简单对比一下,future的基本特征:

    future特征:
    trait SimpleFuture {
        type Output;
        fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
    }
     enum Poll<T> {
        Ready(T),
        Pending,
    }
    

    js中的promise状态转移靠的是push,它本身就是一个事件源,能够主动去改变自身状态,我们在then函数中注册的回调,会自动在下一个微任务队列中执行;而future靠的是poll,需要一个excutor去轮询获取其状态,也就是说excutor会在适当的时候执行future的poll方法,当返回状态为Poll::Pending也就意味需要excutor等待进行下一次轮询,当返回Poll::Ready也就是意味future已经完成,然后根据返回的值去判断是否出错,去执行下一步代码逻辑。 当然了,excutor不会不间断地去轮询,所以future的poll方法里面是可以设置一个waker(唤醒任务),去通知excutor什么时候该来轮询的。

    更了不起的Deno

    接下来,分析下Deno程序是怎么执行的

    入口函数:cli/main.rs的main方法,主要干了两件事:

    1. 解析命令行参数然后创建对应的future(任务)
    2. 启动Tokio执行future(任务)

    更了不起的Deno

    通过代码可以看出,关键的步骤就是Run对应的逻辑,也就是run_command方法

    更了不起的Deno

    核心逻辑就是Deno会创建一个MainWorker,就是我们的主进程,然后是四个关键步骤:

    执行用户逻辑代码->触发load事件->事件循环->触发unload事件

    JS与Rust交互

    代码路径:core/core.js

    deno里面JS与Rust的交互只能通过send和recv这两个方法

    send会根据opId去调用对应的rust方法,同步方法会直接返回,异步的话,需要通过recv去获取异步结果

    Object.assign(window.Deno.core, {
        jsonOpAsync,
        jsonOpSync,
        setAsyncHandler,
        dispatch: send,
        ops,
        close,
        sharedQueue: {},
        ...
      })
    

    其中ops很关键,之前有提到过,它是一个系统操作映射表,将操作和一个唯一的id进行关联,是JS和Rust之间沟通的桥梁

    整个core.js就是给Window.Deno.core定义这些api方法,Deno中触发任务的代码如下:

    //同步
    Deno.core.dispatch(opId, scratchBytes, ...zeroCopy);
    
    //异步
    Deno.core.setAsyncHandler(opId,cb)
    Deno.core.dispatch(opId, scratchBytes, ...zeroCopy);
    

    接下来,我们通过关键代码,看下基本流程是什么样的

    更了不起的Deno 更了不起的Deno

    通过setAsyncHandler设定的回调函数,会进行初始化,回调函数会存储到全局的handles表里。初始化函数里设置recv的回调函数,供Tokio异步任务完成后触发。handleAsyncMsgFromRust函数则会从全局的回调队列中取出队首任务,根据opId去执行对应的handler

    这一套机制和我们平时进行hybrid开发的设计是不是有点类似,对比下设计图就清晰了:

    更了不起的Deno

    核心代码实现:

    更了不起的Deno

    最后来到core/bingding.rs的initialize_context,在这里是deno初始化核心方法的地方(都是挂在Deno.core这个对象下),send和recv也是在这里注入到JS中的

    更了不起的Deno

    用一张流程图,将上述的片段串一下:

    更了不起的Deno

    生态篇

    接下开看下正在茁壮成长的Deno,都有哪些周边生态

    开发框架-oak

    很明显,oak对标的是koa,连取名都这么刚,实现功能基本一样,使用它可以开发基础的web应用,不过相比koa和node,第三方插件就少一些。还有像MongoDB,Redis等数据库,都有对应的Deno版本,开发一些服务端应用还是可以的,国内很火的企业级开发框架egg并没有对应的Deno版本,开发大型企业应用这块,目前还是一个探索领域

    跨端

    Electron包含chromium,node.js,原生Api三大部分,借助它,我们可以开发跨端的桌面应用

    更了不起的Deno

    Electron目前还不支持Deno,如果在Deno中想开发桌面应用,可以使用webview_deno这个第三方库,它基于webview_rust实现,虽然在功能上与Electron有一定差距,但是可以让我们去探索Deno中的桌面世界

    Serverless

    Serverless代表一种无服务器架构(也被称为“无服务器计算”),并不表示不需要物理服务器,而是指不需要关注和管理服务器,直接使用服务即可,其实就是一种服务外包或者说服务托管,这些服务由第三方供应商提供。

    Serverless领域最具代表性的是阿里云和亚马逊,不过他们均不原生支持Deno的运行时。我们可以通过自定义运行时来实现,以阿里云为例,实现一个 Custom Runtime,只需要满足以下 3 个条件:

    • 创建一个 HTTP Server,监听在固定端口(端口可以读取环境变量 FC_SERVER_PORT,默认为 9000)。

    • HTTP Serverr 需要在 15 秒内完成启动。

    • connection 最好设置为 keep alive,请求超时时间至少设置在 15 分钟以上

    借助官方命令行工具funcraft初始化一个函数,然后配置Deno的运行时就可以了。就是学习Deno时,本机安装的过程,放到了云服务器上。执行流程大概是:

    更了不起的Deno

    RPC(远程过程调用)

    RPC基于 TCP/IP 来实现调用远程服务器的方法,在后端分布式架构中应用被广泛应用,实现RPC的三种方式:

    • Thrift:Go、Haskell、Java、Node.js、C、Perl、php、Python、Ruby ...
    • HTTP
    • MQ

    目前,Thrift还不支持Deno,所以构建的Deno应用想使用RPC,只能使用下面的两种形式

    WebAssembly

    WebAssembly是一种新的编码方式,可以在现代的网络浏览器中运行 - 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如C / C ++等语言提供一个编译目标,以便它们可以在Web上运行。它也被设计为可以与JavaScript共存,允许两者一起工作。(MDN官方定义)

    更了不起的Deno

    浏览器端一般通过js中间代码(胶水代码)做兼容,将其他语言编写的功能,用js来进行模拟。比如用web worker模拟多进程,用localStorage模拟存储等等

    如果想在非浏览器端实现WebAssembly应用,需要提供WASI(WebAssembly System Interface)

    WASI 通过增加“抽象层”的方式,解决了 Wasm 抽象机器(V-ISA)与实际操作系统调用之间的可移植性问题,这可以保证我们基于 WASI 编写的 Wasm 应用(模块)真正做到“一次编译,到处运行”。抽象出的“Wasm 系统调用层”将交由具体的底层基础设施(虚拟机 / 运行时)来提供实现和支持

    更了不起的Deno

    Deno原生内置了WASI模块,在Deno可以执行运行wasm代码,上一个官网示例:

    const wasmCode = new Uint8Array([
      0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
      3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
      5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
      128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
      105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
      65, 42, 11
    ]);
    const wasmModule = new WebAssembly.Module(wasmCode);
    const wasmInstance = new WebAssembly.Instance(wasmModule);
    const main = wasmInstance.exports.main as CallableFunction
    console.log(main().toString());		//42
    

    可以看到wasm代码就是Uint8Array的数组,运行时按照WebAssembly MVP标准去解析它,拿到exports导出函数,去执行我们的逻辑

    总结

    最后,做一个简短的Deno总结:

    • 弥补了node的一些设计缺陷,和node共存一段时间
    • 兼容浏览器,让前端代码在deno中直接运行
    • 内置工具集待完善,周边生态建设中
    • 拥抱Rust生态圈,主打安全和高并发
    • 拓展前端边界,让JS在服务端大展拳脚

    Ryan基于近些年业界的技术发展和实践,倾注了很多心血,创造了Deno。我们可以从中学习到一些设计和实现,这一点是最关键的。至于大家争论的未来是node还是Deno主导,会不会替代node,就显得没那么重要了。技术车轮一直向前,我们需要保持自己的学习力和理解力,多主动思考。


    起源地下载网 » 更了不起的Deno

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元