d2js 运行于 servlet 容器,如tomcat,由于容器自身支持并发,似乎 d2js 只要使用 nashorn 运行脚本即可。这样我们得到最简单的实现方式:

在该方式中,nashorn引擎仅存在于Servlet.service调用栈,在调用完成后即释放。Hotspot 将栈上对象(局部变量、牵连的函数调用中的局部变量)也分配在堆里,但是栈上对象存活时间很短,只要新生代空间足够,其永远不会进入年老代,回收策略简单。对于高并发少持久数据的网站系统,加大新生代当是有效做法。

为每个请求创建ScriptEngine的方式缺陷明显:

1.每次都创建一个 ScriptEngine,输出响应后即释放,代价很高

2.每次脚本都要编译(nashorn 确实编译了js),花销很大

以上缺陷导致该方式没有实用价值,仅在开发原型时有意义。但该方式也有它的参考意义:隔离是并发的秘诀,充分的隔离=完美的并发,该方式对每个请求使用独立的ScriptEngine,开发者在 js 中可以尽情使用全局变量而不用担心全局污染,这也使人员要求大为降低。

本轮优化前 d2js 使用的是这个方案的升级版本,采用“每个 d2js 文件一个 ScriptEngine Pool” 的方案,可以方便的使用全局变量,对开发人员要求较低。这个办法资源消耗巨大,行不通。

性能优化的目标是:让所有d2js文件运行于同一引擎实例!!每个d2js文件只加载一次,除非文件发生写入。

同一引擎不存在难度,就是个单例。但总有一些弯路让我们技痒难耐。比如有一个貌似合理的替代方案:ThreadLocal<ScriptEngine>,这个方案假设 ScriptEngine 只适合单线程,考虑到目前 jdbc 都是同步的,在 d2js函数调用期间,不会发生线程切换,采用 ThreadLocal 即使使用全局变量也是安全的——既然引擎是线程安全的,引擎内的变量自然也是线程安全的。但该方式实测性能不及使用同一引擎。

使用同一引擎后,原来通过全局变量表示的 d2js, request, response,session 等都需要重构。

首先是 d2js 对象。d2js 文件中接口是以 d2js.func = function(){} 形式插拔的。使用同一引擎后,d2js 不再是全局变量,要保持该表达方式,目前采用的办法是对每个d2js文件套用一个模板代码,该模板是一个外层闭包,将 d2js 化为该闭包的变量:

function createD2js(){

    var d2js/**/ = new D2JS();

    // ------- 用户代码插入模板-----------------
d2js.func = function(){}   // 将用户代码置入模板内
// --------以上用户代码--------------------- … … return d2js; } 

加载 d2js 文件时套用模板、执行该模板函数并获得返回值。每个 d2js 文件都得到一个 D2JS 实例,所有D2JS实例存放于 ConcurrentHashMap<String filename, Object D2JS Instance> allD2js 。

处理HTTP请求时,根据文件名找到 D2JS 实例,由相应实例提供服务。

d2js 函数中还需要访问 request, response, session 等对象。这些对象是HTTP请求带来的,每个请求都不同,同一 d2js 对象要支持对并发请求提供服务。由于使用的是同一个ScriptEngine,全局变量表达方式显然行不通了。可行的有两种做法:

1. 扩充参数栈,增加一个参数:d2js.func = function(params, http){}

http 是封装了 {request:request, response: response, session: session} 的一个对象,一个复合粒子。

这个方式的好处是,有时 d2js.func 不需要使用 request 等对象,该形参就可以留空,重构代价不大。缺点是,当函数需要更多参数时(如给其它函数调用的私有函数),很难确切知道应该在哪个位置提供该参数。

2.将 request, response 等表达为 this.request, this.response。这个做法更有效,最终选择了这种方式。

怎么才能让同一个 d2js 实例分别对应不同的 request、response 呢?

办法很有趣。我将上述存放在 allD2js 中的 D2JS 实例作为 prototype,每次请求时创建一个基于该 prototype 的新实例为该请求服务,服务完毕后该实例即释放。

该方式流程如下:

在原理上这种一次性的附带上当次调用信息的镜像实例与闭包的偏函数(柯里化)本质相同。该实现方式发挥了 js 语言原型链思想,特别适合按上下文偏分服务的并发情形,和 erlang 将偏分信息全部放置于栈相比,该方式具备基于原型链的面向对象特点,但又延续了栈隔离的特色。未来 nashorn 实现 async/await、 jdbc 实现 async-jdbc 后,该并发表达方式将会获得更强的生命力。

采取该方式后,每次请求仅在栈上生成一个镜像 D2JS 对象,内存消耗少,业务间互相隔绝,除修改D2JS文件导致的并发冲突外,无其它潜在冲突资源,不使用任何锁。实测效率与 JSP 相比约为 JSP 的 50%-90%,对于运行于 JVM 的脚本语言这已经非常不错了。

D2js 是如何处理并发的的更多相关文章

  1. iis如何处理并发请求

    文章:IIS是怎么处理同时到来的多个请求的? 文章:你真的了解:IIS连接数.IIS并发连接数.IIS最大并发工作线程数.应用程序池的队列长度.应用程序池的... 文章:IIS最大工作进程数设置引发串 ...

  2. flask如何处理并发

    1.使用自身服务器的多进程或者多线程,参考werkzeug的run_simple函数的入参.注意,进程和线程不能同时开启 2.使用gunicorn使用多进程,-w worker 进程数,类型于运行多个 ...

  3. Code First开发系列之管理并发和事务

    返回<8天掌握EF的Code First开发>总目录 本篇目录 理解并发 理解积极并发 理解消极并发 使用EF实现积极并发 EF的默认并发 设计处理字段级别的并发应用 实现RowVersi ...

  4. [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序处理并发

    这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第十篇:为ASP.NET MVC应用程序 ...

  5. Code First开发系列之管理并发和事务(转)

    转自:http://www.cnblogs.com/farb/p/ConcurrencyAndTransctionManagement.html 返回<8天掌握EF的Code First开发&g ...

  6. EntityFramework_MVC4中EF5 新手入门教程之七 ---7.通过 Entity Framework 处理并发

    在以前的两个教程你对关联数据进行了操作.本教程展示如何处理并发性.您将创建工作与各Department实体的 web 页和页,编辑和删除Department实体将处理并发错误.下面的插图显示索引和删除 ...

  7. linux设备驱动第五篇:驱动中的并发与竟态

    综述 在上一篇介绍了linux驱动的调试方法,这一篇介绍一下在驱动编程中会遇到的并发和竟态以及如何处理并发和竞争. 首先什么是并发与竟态呢?并发(concurrency)指的是多个执行单元同时.并行被 ...

  8. Contoso 大学 - 7 – 处理并发

    原文 Contoso 大学 - 7 – 处理并发 By Tom Dykstra, Tom Dykstra is a Senior Programming Writer on Microsoft's W ...

  9. [翻译][MVC 5 + EF 6] 10:处理并发

    原文:Handling Concurrency with the Entity Framework 6 in an ASP.NET MVC 5 Application 1.并发冲突: 当一个用户编辑一 ...

随机推荐

  1. win7下安装配置nodejs、使用npm安装express

    1.下载node http://nodejs.cn/download/ 这里下载自己需要的 我的环境是win764,下载地址是:https://nodejs.org/dist/v6.2.0/win-x ...

  2. [LeetCode] Unique Word Abbreviation 独特的单词缩写

    An abbreviation of a word follows the form <first letter><number><last letter>. Be ...

  3. [LeetCode] Find the Celebrity 寻找名人

    Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exist o ...

  4. 一个简单的ASP.NET MVC异常处理模块

    一.前言 异常处理是每个系统必不可少的一个重要部分,它可以让我们的程序在发生错误时友好地提示.记录错误信息,更重要的是不破坏正常的数据和影响系统运行.异常处理应该是一个横切点,所谓横切点就是各个部分都 ...

  5. 【URAL 1519】Formula 1

    http://acm.timus.ru/problem.aspx?space=1&num=1519 调了好久啊.参考(抄)的iwtwiioi的题解. 如果想要题解,题解在<基于连通性状态 ...

  6. 让PDF.NET支持不同版本的SQL Server Compact数据库

    最近项目中需要用到嵌入式数据库,我们选用的数据开发框架是PDF.NET(http://www.pwmis.com/SqlMap/),之前的博文已经总结了让PDF.NET支持最新的SQLite,今天我们 ...

  7. 贝塔阶段html及pdf模块测试

    这次虽然工作内容是将c#的html及pdf处理程序移植到java中,但是由于重新编写代码使得先前的工作成果得不到利用,于是将其编写为dll,再在java端调用. 这使得在java端即便每个分支都到达, ...

  8. ORB-SLAM(五)优化

    ORB-SLAM作为单目SLAM,其精度很大程度上决定于帧与帧之间的位姿优化的是否准确.因此优化(optimization)在ORB-SLAM里面扮演了很重要的角色.这一小节探讨一下ORB-SLAM里 ...

  9. C#事务

    看了很多关于事务的概念,还是觉得维基百科上说的最好: 数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成. 一个数据库事务通常包含了一个序列的对数据库的读 ...

  10. php artisan常用方法

    https://my.oschina.net/u/1186749/blog/643850