Edge.js:让.NET和Node.js代码比翼齐飞
通过Edge.js项目,你可以在一个进程中同时运行Node.js和.NET代码。在本文中,我将会论述这个项目背后的动机,并描述Edge.js提供的基本机制。随后将探讨一些Edge.js应用场景,它在这些场景中可以为你开发Node.js程序提供帮助。
为何要使用Edge.js?
虽然许多应用程序只能用Node.js编写,不过有些情况下又需要综合Node.js和.NET两者的优点。基于以下几个理由,你想要在程序中使用.NET和Node.js:.NET框架和NuGet包提供了一个丰富的功能生态系统,它很好地补充了Node.js和NPM模块;可能你希望在Node.js程序中重用某些现成的.NET组件;也可能想使用多线程CLR运行CPU密集型的计算,而这绝非是单线程的Node.js所擅长的;又或者你可能优先选择使用.NET框架和C#而不是使用C/C++编写原生的Node.js扩展来访问那些尚未通过Node.js暴露的操作系统机制。
一旦你决定在程序中使用Node.js和.NET,那么你必须将Node.js和.NET的组件用进程壁垒将两者分离开来,并建立某种形式的进程间通信的机制,比如说HTTP:
Edge.js提供另一种类似的组建异构系统的方式。它允许你在单一进程中同时运行Node.js和.NET代码,并且提供了V8和CLR之间的互操作机制。
使用Edge.js可以在一个进程中运行Node.js和.NET,而不用将其分割为两个进程,这样有两个主要的好处:更好的性能和更低的复杂性。
某个场景的性能测试显示,从Node.js向C#发出的进程内Edge.js请求比两个进程间通过HTTP发送的相同请求快32倍。与两个进程和进程间的通信信道相比,只处理一个单独的进程,明显降低了你需要解决的部署和维护的复杂性。
.NET欢迎Node.js
接下来我将用一个基础实例讲解Edge.js的关键概念,这个例子是从Node.js向C#发送请求。
第1行引入事先从NPM安装的edge模块。Edge.js是一个原生的Node.js组件。Edge.js的特殊之处在于,它被加载的时候便在node.exe进程内部开始代管CLR。
edge模块暴露了一个名为func的单函数。在高层次上,该函数以CLR代码为参数,然后返回一个JavaScript函数作为CLR代码的代理。func函数接受多种格式的CLR代码,从源代码,文件名,到预编译的CLR都可以。在上面的3-8行中,程序指定了一个异步的Lambda表达式作为C#文本代码。Edge.js提取出那段代码并将其编译为内存中的CLR程序集。然后它围绕着第3行的CLR代码(分配给hello变量的)创建并返回了一个JavaScript代理函数。需要注意的是,这个编译过程在每次调用edge.func函数时都会执行一次并将结果缓存。此外,如果你用同样的字符串变量调用edge.func函数两次,那么就会从缓存中获得相同的Func<object,Task<object>>实例。
Edge.js创建的hello函数是C#代码的代理函数,它在第10行由标准的Node.js异步模式调用。这个函数接收一个单独参数(Node.js字符串),并且还有一个接收错误和返回结果的回调函数。输入的参数在第4行被传递到C#异步Lambda表达式中,这个表达式在第6行将传入值附加到“.NET welcomes”字符串之后。当调用第10行的JavaScript回调函数的时候,这个C#中新构造的字符串被Edge.js作为result参数传递进去。JavaScript回调函数则将其打印在控制台上:“.NET welcomes Node.js”。
Edge.js提供了一套进程内Node.js和.NET代码之间规范的互操作模型。它不允许JavaScript直接调用任何CLR函数。CLR函数必须是一个Func<object,Task<object>> 委托。这种机制为Node.js和.NET互相传递数据提供了足够的灵活性。同时,它需要.NET代码异步执行,以便于和单线程的Node.js代码自然地集成在一起。这是Func<object,Task<object>>委托如何映射于Node.js异步模型概念:
互操作模式并不禁止你访问.NET framework的任何部分,但是它往往会要求你额外编写一个适配器层以暴露所需的.NET功能如同Func<object,Task<object>> 委托。这个适配器层要求你明确地定位.NET中的阻塞APIs的问题所在,它可能将这些运算运行在CLR线程池中以避免阻塞Node.js事件循环。
数据和功能
虽然Edge.js仅仅允许你在Node.js和.NET之间传递一个参数,但是这个参数可能是个复杂类型的。当从Node.js请求.NET代码的时候,Edge.js可以封送(marshal)所有标准的JavaScript类型:从基类型到对象和数组。当从.NET向Node.js传递数据的时候,Edge.js不但可以封送所有的基本CLR类型,而且还可以处理CLR对象实例、列表、集合和字典类型。从概念上讲,你可以认为在V8和CLR之间的数据传递就像是在一个环境中将数据序列化为JSON,而在另一个环境中对JSON进行反序列化。但是,Edge.js并没有在进程中进行实际的JSON序列化过程。相反,它直接在内存中进行V8和CLR类型系统之间的数据封送,而省略了字符串型中间代码,这个过程远比JSON序列化和反序列化更加高效。
Edge.js通过值进行数据封送,所以当执行过程跨越V8/CLR边界时,它会在V8或者CLR的堆中另外创建一份数据拷贝。这个规则有一处显著的例外:与通过值进行数据封送不同,Edge.js通过引用来封送函数。让我们通过下面这个例子来说明这个强有力的概念:
在这个例子中,Node.js调用addAndMultiplyBy2的C#中运行的函数。这个函数获取两个数字,而后返回它们总和的2倍。鉴于这个例子的目的,我们假设C#知道如何做加法但是却并不清楚如何做乘法。C#代码在计算和之后需要回调至JavaScript以进行乘法运算。
为了实现这个场景,Node.js应用程序在第18-20行定义一个multiplyBy2函数,并在第23行调用addAndMultiplyBy2函数时将其随同两个运算对象传递至C#代码。注意multiplyBy2函数是如何满足Edge.js规范的互操作模式的。这使得Edge.js可以在给multiplyBy2这个JavaScript函数创建.NET代理,就像是.NET中的Func<object,Task<object>>委托。这个JavaScript函数代理接下来被C#代码在第10行调用,用于对第8-9行中得到的和执行乘法运算。
遵守规范的互操作模式的函数也可以从.NET被封送到Node.js。能够在V8和CLR中双向封送函数是很强有力的概念,尤其是当掺杂着闭包的时候更是如此。请看下面这个例子:
在第1-7行,Edge.js创造了一个JavaScript函数createCounter,这个是C# Lambda表达式的代理。第9行中传给createCounter函数的的参数在第3行被强制转化为一个C#的本地变量。第4-5行的代码比较有趣:C#异步Lambda表达式的结果是一个Func<object,Task<object>>型的委托实例,它(第5行)的实现包含了第3行在闭包中定义的本地变量。当Edge.js将这个Func<object,Task<object>>实例封送为JavaScript函数回传给Node.js,并将其分配给第9行的counter变量的时候,这个JavaScript的counter函数有效的涵盖了CLR状态下的闭包。这点在第10-11行得到了充分的证明。这两行两次调用counter函数,结果返回的是一个不断增加的值。这是由于每次调用第5行实现的Func<object,Task<object>>都会使得第3行的本地变量的数值增加。
在V8和CLR之间封送函数的能力加上闭包的概念是个很强有力的机制。这样.NET代码就能够暴露CLR对象的功能给Node.js。第三行的本地变量在最后的例子中是一个Person类的实例。
让我们一起动手
我们来看几个实际的例子以便了解如何在Node.js应用程序中使用Edge.js。
Node.js是单线程的架构。如果要保持响应性,那么应用程序中就不能执行阻塞的代码。大部分Node.js程序都是在进程外执行CPU密集型的运算。外部进程通常使用的技术并不是Node.js。Edge.js使得这种场景非常容易实现。它允许你的Node.js程序在Node.js进程内部的CLR线程池中执行CPU密集型的逻辑运算。当CPU密集型的计算在CLR线程池的线程中运行时,V8线程上的Node.js程序仍然是可响应的。一旦CPU密集型操作结束,Edge.js同步线程就在V8线程上执行JavaScript回调函数。请看这个使用.NET功能转换图片格式的例子:
convertImageToJpg函数使用了.NET中的System.Drawing的功能将PNG图片转换为JPG格式。这是计算密集型的操作,因此第6行创建的C#实现(implementation)调用了Task.Run在CLR线程池中运行这个转换。当计算执行的时候,进程中的单例(singleton)V8线程可以处理后续的事件。C#代码随第6行的await关键字而等待图片转换的完成。只有在图片转换完成之后,convertImageToJpg在V8线程上执行第14-15行JavaScript回调代码,整个函数才算完成。
另一个让Edge.js大显身手的例子是在MS SQL中读取数据。现在Node.js开发者还没有什么读取MS SQL数据的方法可以比.NET Framework中的ADO.NET更加完善和成熟。Edge.js提供给你一个简单的在Node.js程序中利用ADO.NET的方法。请看下这个Node.js程序:
在第1行中,Edge.js通过编译sql.csx文件中的ADO.NET代码创建了sql函数。这个sql函数接受一个T-SQL命令构成的字符串,并使用ADO.NET异步执行它,然后将结果返回给Node.js。sql.csx文件用C#编写了不到100行的ADO.NET代码,它支持对MS SQL数据库执行CRUD四种操作:
在sql.csx文件中的实现(implementation)使用异步ADO.NET的API来访问MS SQL数据并执行Node.js传给它的T-SQL命令。
上面的两个例子仅仅代表了Edge.js帮你编写Node.js程序的一小部分场景。更多的例子可以参见Edge.js的GitHub站点。
路线图
Edge.js是一个遵循Apache 2.0协议的开源项目。它目前的开发很活跃,欢迎前来贡献代码。你可以用你的时间和经验来检查工作项目列表。
尽管本文中所有的例子都是使用C#写的,Edge.js支持在Node.js程序中运行任何CLR语言的代码。目前的扩展提供了对脚本语言F#、Python和PowerShell的支持。通过语言扩展模型你能很容易的添加其他CLR语言的编译器。
Edge.js目前需要.NET Framework环境,因此只能运行在Windows上。但是对Mono的支持也在积极的开发中,不久就可以在MacOS和*nix上运行Edge.js程序了。
关于作者
Tomasz Janczuk是微软的一名软件工程师。他目前主要关注Node.js和Windows Azure。在此之前他从事.NET Framework和网络服务(web services)方面的工作。业余时间里,他在太平洋等地参加了很多户外活动。你可以在Twitter上关注他,@tjanczuk,也可以访问他的GitHub页面或者阅读他的博客以获得更多的资讯。
查看英文原文:Run .NET and Node.js code in-process with Edge.js
查看中文原文:Edge.js:让.NET和Node.js代码比翼齐飞
Edge.js:让.NET和Node.js代码比翼齐飞的更多相关文章
- node.js入门系列(一)--Node.js简介
什么是NodeJS JS是脚本语言,脚本语言都需要一个解析器才能运行.对于写在HTML页面里的JS,浏览器充当了解析器的角色.而对于需要独立运行的JS,NodeJS就是一个解析器. 每一种解析器都是一 ...
- [js高手之路]Node.js+jade抓取博客所有文章生成静态html文件
这个周末,恶补了一下jade模板引擎,就为生成静态html文件,这篇文章需要知道jade以及看过我的上篇文章,我先给出他们的参考链接: [js高手之路]Node.js模板引擎教程-jade速学与实战1 ...
- Node.js的安装以及Node.js的模块管理
索引: Node.js的安装以及Node.js的模块管理Node.js开发环境搭建以及对ES6的支持Node.js构建Vue.js项目Vue.js单文件组件的开发基于Vue.js的UI组件(Eleme ...
- 【Node.js】2.开发Node.js选择哪个IDE 开发工具呢
安装完Node.js之后,就要为它选择一个有利的IDE用于开发. 相比较了多个IDE之后,定位在webstrom和sublime上. 有一个简单的比较: webstorm功能很丰富,前端开发工具的集大 ...
- node.js系列笔记之node.js初识《一》
node.js系列笔记之node.js初识<一> 一:环境说明 1.1 Linux系统CentOS 5.8 1.2 nodejs v0.10.15 1.3 nodejs源码下载地址 htt ...
- [js高手之路]Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件
接着这篇文章[js高手之路]Node.js+jade抓取博客所有文章生成静态html文件继续,在这篇文章中实现了采集与静态文件的生成,在实际的采集项目中, 应该是先入库再选择性的生成静态文件.那么我选 ...
- [js高手之路]Node.js+jade+mongoose实战todolist(分页,ajax编辑,删除)
该系列文章索引: [js高手之路]node js系列课程-创建简易web服务器与文件读写 [js高手之路]node js系列课程-图解express+supervisor+ejs用法 [js高手之路] ...
- Installing Node.js via package manager | Node.js
Installing Node.js via package manager | Node.js i386 (32-bit)
- Node.js入门教程:Node.js如何安装配置并部署第一个网站
前言:作为一个资深的前端开发人员,不懂的Node.js 那你绝对是不能跟别人说你是资深的前端程序猿滴! 今天洋哥就来和大家一起学习被大牛称之为前端必学的技能之一Node! 那么Node到底是什么呢? ...
- [js高手之路]node js系列课程-创建简易web服务器与文件读写
web服务器至少有以下几个特点: 1.24小时不停止的工作,也就是说这个进程要常驻在内存中 2.24小时在某一端口监听,如: http://localhost:8080, www服务器默认端口80 3 ...
随机推荐
- Nginx 相关
删除 access.log 之后,要让 Nginx 重新加载一下,命令 killall -s USR1 nginx 无需新建 access.log ,这个命令会自动创建该文件 Nginx 的日志文件轮 ...
- TFS源代码管理的8大注意事项
TFS源代码管理的8大注意事项 目录 源代码管理的8大注意事项... 1 1. 使用TFS进行源代码管理... 2 2. 如果代码没放在源代码管理软件里,等于它不存在... 2 3. 要早提交,常提交 ...
- Flume(3)source组件之NetcatSource使用介绍
一.概述: 本节首先提供一个基于netcat的source+channel(memory)+sink(logger)的数据传输过程.然后剖析一下NetcatSource中的代码执行逻辑. 二.flum ...
- 浅入tomcat
前言:学习笔记,以供参考 1.什么是服务器 所谓的服务器其实就是一段别人写好的程序,服务器有两个能力. a.可以帮助我们来管理资源. b.可以将资源向外界发布以便于外界来访问这个资源. 2.资源有哪些 ...
- cmake cannot find package
cmake 找不到package,如 find_package (OpenMesh REQUIRED) 出现错误 在项目的文件夹中找到 FindOpenMesh.cmake 文件,将其所在路径添加到 ...
- Python-面向对象(类)一
一.如何创建类 class cls: pass 二.创建方法 构造方法: __init__(self, arg) obj = 类('a1') 普通方法: obj = 类('xxx') obj.普通方法 ...
- python strip()函数 介绍
python strip()函数 介绍,需要的朋友可以参考一下 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除 ...
- 【APICloud】利用sublimetext3编写apicloud
下载sublime text 3 安装插件 使用模糊搜索apicloud有三个插件全部下载下来 安装海马玩模拟器,这是一个安卓的模拟器,进入官网下载后直接安装就可以了. 打开sublime text ...
- VIew-CoordinatorLayout 笔记
CoordinatorLayout 协调者:一般会是两个控件,一个Dependency一个child ,CoordinatorLayout的主要功能就是协调这两个控件,使child跟随Dependen ...
- .net开源后可以查看的源代码
通过此网站可以直接查询基于.net framework4.6的源码 也可以下载到本地通过VS 查看.