首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Edge.js:让.NET和Node.js代码比翼齐飞

Edge.js:让.NET和Node.js代码比翼齐飞

作者头像
张善友
发布2018-01-30 11:43:05
3.4K0
发布2018-01-30 11:43:05
举报
文章被收录于专栏:张善友的专栏张善友的专栏

通过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#PythonPowerShell的支持。通过语言扩展模型你能很容易的添加其他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代码比翼齐飞

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2013-09-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为何要使用Edge.js?
  • .NET欢迎Node.js
  • 数据和功能
  • 让我们一起动手
  • 路线图
  • 关于作者
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档