Node.js 是一个基于 Google Chrome V8 JavaScript Engine 的开源项目。它为未使用浏览器运行的服务器端 JavaScript 应用程序提供了一个平台。 事件驱动 、 非阻塞 I/O 模型使它既是轻量化的,又能高效使用。它还提供了一些内置模块来简化编程,尤其是网络应用程序。还有许多第三方模块也可以使用内置的 npm(node packaged modules,Node 封装模块)工具来轻松地安装,以便扩展 Node.js。
备注:Node.js 是 Joyent 的官方商标。
现在支持在 IBM i 平台上使用 Node.js。为了在 IBM i 上应用开源技术,还创建了 IBM i Open Source Solutions 授权程序产品 (LPO) 5733OPS。它包含在 IBM i 操作系统的 Bonus Pack 中。在安装之后,这个免费的 LPO 通过 IBM® HTTP Server for i Group PTF 进行维护。
图 1. Node.js 将 JavaScript 代码转码为机器代码
除了其核心功能之外,IBM i 上的 Node.js 还提供了以下两个 IBM i 独有的扩展:
图 2. Node.js for IBM i 基础架构
本文将介绍 Node.js 的基本知识,还将介绍如何开始在 IBM i 上使用它。文中将指导您在 IBM i 上设置 Node.js,使用一些示例来指导您逐步完成该过程。
回页首
Node.js for IBM i 打包在 5733OPS 中,后者仅支持 IBM i 7.1 及更高版本。Node.js 是在这个新 LPO 的选项 1 中提供的。除了安装新 LPO 之外,还需要安装其他一些软件程序才能在 IBM i 上使用 Node.js。
对于 Python 和 PASE 中的其他开源库安装,请参阅 开源二进制程序 在线手册和文档。
回页首
从物理 DVD 介质或镜像文件安装 5733OPS 选项 1 产品。然后应用最新的 IBM HTTP Server for i PTF Group 来获得最新的 Node.js 支持:
成功安装所有软件后,将创建 /QOpenSys/QIBM/ProdData/Node/ 目录。
准备好软件环境后,执行以下步骤来验证安装:
QSH
或者使用下面这条 CL 命令启动 PASE 会话。
CALL QP2TERM
node –v
输出必须是系统上的有效的 Node.js 版本,比如 v0.10.29。
npm –v
输出必须是系统上有效的 npm 版本,比如 1.4.14。
图 3. Node.js for IBM i 安装验证
如果版本信息看起来有效,则安装已成功。
备注:在 Qshell 会话中,一些 Node.js 应用程序可能抛出 signal 5 错误。此错误是 Qshell 的线程限制所导致的。请参阅IBM 知识中心文档,了解有关的更多细节。
要删除此限制,可以添加环境变量 QIBM_MULTI_THREADED 和值 Y ,然后重新启动 Qshell 会话。或者可以对您的应用程序使用 PASE 会话。
下面这条 CL 命令启动了一个支持多线程的 Qshell 进程。
ADDENVVAR ENVVAR(QIBM_MULTI_THREADED) VALUE(Y)
图 4. 为 Qshell 启用多线程功能
回页首
前几节介绍了如何在 IBM i 上设置 Node.js 运行时。在剩余几节中,我们将通过一个编程示例来展示 Node.js for i 的功能。这个示例将创建一个 Web 服务器,允许用户查询 DB2 for i 数据库,并使用 Web 浏览器运行 CL 命令。
备注:Node.js 不支持使用 EBCDIC 编码的源文件。可使用 UTF-8 编码或兼容 UTF-8 的 CCSID,比如 819 (ISO 8859-1 ASCII)。
回页首
IBM i 支持所有 Node.js 核心功能。一个核心功能是使用 IBM i 的 Node.js 支持轻松地创建 Web 服务器。
var http = require('http'); var ip = "IP_Address"; var port = Port; var webserver = http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World/n'); }); webserver.listen(port, ip); console.log('Server running at http://' + ip + ':' + port);
node /home/njs/sample.js
如果显示下面这条消息,您就会知道 Web 服务器在成功运行。
Server running at http:// IP_address : port /
现在您可以打开浏览器访问 Node.js 启动的 Web 服务器。此时将会显示 “Hello World” 网站,如下图所示。
图 5. 验证 Web 服务器
在本例中,Web 服务器监听端口 8081 来接受请求并响应。要终止这个 Web 服务器,需要手动终止该作业。要结终止 Web 服务器作业,可在 QSH 会话中按 Esc 键,选择选项 2 来处理当前作业。接下来,终止运行 node 程序的选项为 4 的作业,如图 6 所示。
图 6. 手动终止 Node.js 作业
要自动化 Node.js Web 服务器的管理,可创建自定义的 CL 命令来执行这些操作。可在文章 在 IBM i 上加速运行 Tomcat 服务器 的 “第 9 步 - 创建自定义 CL 命令来启动 Tomcat 服务器” 中找到创建自定义 CL 命令的更多信息。
在前面的 JavaScript 代码中,Web 服务器仅使用 “Hello World” 响应所有请求。现在,我们将扩展 sample.js 程序,以便在浏览器请求 URL http://<hostname>:port/sample.html 时,从一个静态文件读取内容并响应客户端。
要使用静态网页来响应客户端,Node.js 需要使用 “fs” 文件系统模块和 “url” 模块来解析请求字符串。您需要更新 sample.js 文件,使之包含这些额外的模块。请记住更新 IP 地址和端口值。可以使用您的值更新下面这个 sample.js 文件:
var http = require('http'); var fs = require('fs'); var url = require('url'); var ip = "IP_Address"; var port = Port; var webserver = http.createServer(function (req, res) { var realPath = __dirname + url.parse(req.url).pathname; fs.exists(realPath, function(exists){ if(!exists){ res.writeHead(404, {'Content-Type': 'text/plain'}); res.end("404 Not Found"); } else { var file = fs.createReadStream(realPath); res.writeHead(200, {'Content-Type': 'text/html'}); file.on('data', res.write.bind(res)); file.on('close', res.end.bind(res)); file.on('error', function(err){ res.writeHead(500, {'Content-Type': 'text/plain'}); res.end("500 Internal Server Error"); }); } }); }); webserver.listen(port, ip); console.log('Server running at http://' + ip + ':' + port);
备注:__dirname 是一个 Node.js 内部变量,用于获取当前运行的脚本所在的目录名称。
然后,创建一个名为 sample.html 且包含以下内容的静态 HTML 文件,将它上传到 sample.js 文件所在的相同目录。
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Node.js for i Sample</title> </head> <style> input { height:30px; border:#ccc solid 1px } input[type="text"] { width:500px } input[type="submit"] { margin:1em; width:120px } </style> <body> <form name="input" action="query" method="get"> <div>SQL Command </div> <input type="text" name="sql" placeholder="SELECT * FROM ..."/> <input type="submit" value="Query"/> </form> <form name="input" action="cmd" method="get"> <div>CL Command </div> <input type="text" name="cl" placeholder="WRKSYSSTS"/> <input type="submit" value="Run"/> </form> </body> </html>
首先,结束正在运行的服务器,然后像第 2 步描述的那样重新启动该 Web 服务器。现在我们可以启动浏览器来访问示例页面 http://ip:port/sample.html
图 7. 提供静态网页
尽管目前它只提供了一个静态页面,但是,一个非常简单的 Web 应用程序框架已经准备好了。您可以看到开始使用它有多简单。不需要额外的应用服务器设置,也不需要特殊的部署步骤。在下面几节中,将使用更多 JavaScript 代码来扩充示例,以完成一些有意义的功能,运行 SQL 语句和 CL 命令。
回页首
在本节中,可以扩充示例来使用 DB2 for i 访问库 API 访问 DB2 for i 数据。该 Web 应用程序允许您在网页上运行任何 SQL 语句并显示结果。
警告:下面的示例演示了通过 Node.js 访问数据的功能。实际上,您不希望将命令或 SQL 接口向外暴漏给某个用户。Node.js 服务器在启动 Web 服务器的用户的授权下运行。
DB2 for i Extension 是一个 JavaScript API 集合,用于在 IBM i 上执行 DB2 数据库操作。它提供了与 DB2 for i SQL 调用级接口 (CLI) 对应的 JavaScript 接口。所有 DB2 CLI API 都已在该扩展中公开。Node.js for IBM i 随带了访问库,它位于: /QOpenSys/QIBM/ProdData/Node/os400/db2i/ 。
要支持使用 DB2 for i API,您的源代码中只需包含 db2.js 文件。
var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2');
要连接到数据库,需要设置该数据库的名称或别名。可发出 CL 命令 WRKRDBDIRE 来获取目标数据库的名称。在图 9 中所示的示例中,本地数据库名为 G0488C55 。如果需要连接到本地数据库,可在 db.conn() API 上使用特殊值 ‘*LOCAL’。
图 8. 找到本地数据库名称
除了 CL 命令之外,数据库名称也可由 SQL 确定。您可以发出 CL 命令 STRSQL 来启动 SQL 交互式会话,运行以下 SQL。
SELECT CATALOG_NAME, CATALOG_STATUS, CATALOG_TYPE FROM QSYS2.SYSCATALOGS
请参考以下输出。
CATALOG_NAME | CATALOG_STATUS | CATALOG_TYPE |
---|---|---|
G0488C55 | AVAILABLE | LOCAL |
IASP1 | VARYOFF | LOCAL |
LP21UT24 | UNKNOWN | REMOTE |
现在让我们扩展 sample.js 文件,从请求查询字符串读取 SQL 语句,运行该 SQL 语句,然后将结果集返回给浏览器。
var http = require('http'); var fs = require('fs'); var url = require('url'); var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2'); var DBname = "*LOCAL"; var ip = "IP_Address "; var port =Port ; var webserver = http.createServer(function (req, res) { var realPath = __dirname + url.parse(req.url).pathname; fs.exists(realPath, function(exists){ if(!exists){ var sql = url.parse(req.url, true).query.sql; res.writeHead(200, {'Content-Type': 'text/plain'}); if(sql && sql.length > 0) { db.init(); db.conn(DBname); // Connect to the DB db.exec(sql, function(rs) { // Query the statement res.write(JSON.stringify(rs)); }); db.close(); } res.end(); } else { var file = fs.createReadStream(realPath); res.writeHead(200, {'Content-Type':'text/html'}); file.on('data', res.write.bind(res)); file.on('close', res.end.bind(res)); file.on('error', function(err){ res.writeHead(500, {'Content-Type':'text/plain'}); res.end("500 Internal Server Error"); }); } }); }); webserver.listen(port, ip); console.log('Server running at http://' + ip + ':' + port);
有两种数据库连接模式 - 服务器模式和非服务器模式。默认情况下,使用的是非服务器模式。
在 db.conn() 上使用 *LOCAL 数据库时,非服务器模式不需要或不用验证用户 ID 和密码。相反,它会使用 *CURUSR 值来处理连接,这对应于启动 node 服务器的用户。这正是前面的示例不包含任何用户个人信息的原因。
使用服务器模式,使用一个特定的用户个人信息连接到 *LOCAL 数据库。对于服务器模式,需要输入用户 ID 和密码并验证。但是,如果用户 ID 或密码设置为 NULL,连接将使用对当前运行 Node.js 程序的作业有效的用户 ID。
如果程序需要连接到一个远程数据库或一个独立 ASP (iASP),那么服务器模式和非服务器模式都需要有效的用户 ID 和密码。
您可以在初始化函数的回调函数中启用服务器模式,如下所示。
db.init(function(){ db.serverMode(true); //Enable Server Mode if needed });
在更新源代码时,结束 Node.js 程序并重新启动 Web 服务器,像前几节中介绍的那样。
接下来,将下面这个 URL 输入到浏览器中:http://ip:port/sample.html。上面添加的代码会让 SQL 函数发挥作用。在第一个字段中输入任何 SQL 语句并单击 Query 。Node.js 服务器对指定的数据库运行 SQL 语句。例如,下面这条 SQL 语句将会查找最近 7 天内增长到最大大小的文件:
select SYS_NAME, SYS_ONAME, CURRENT_VALUE from qsys2.syslimits where sizing_name = 'MAXIMUM NUMBER OF VALID ROWS' AND lastchg > current timestamp - 7 days order by current_value desc
图 9. 从 DB2 数据库查询数据
输出的结果集为 JSON 格式。您可以使用 JSON.stringify() 函数将它转换为容易阅读的文本,或者直接访问键值对。
图 10. SQL 查询的结果
回页首
Node.js Toolkit for IBM i API 集基于 XMLSERVICE 来轻松地访问 IBM i 原生对象,比如 PTF 信息、数据队列和程序。目前 Node.js Toolkit for IBM i 提供了以下类。
Node.js for IBM i 随带了 Node.js Toolkit for IBM i,它位于 /QOpenSys/QIBM/ProdData/Node/os400/os400/ 目录中。要使用 Node.js Toolkit for IBM i,首先需要 itoolkit.js 文件。itoolkit.js 文件将 XMLSERVICE 支持与 Node.js 联系起来,它是其他所有 Node.js Toolkit 函数的基础。
var xt = require('/QOpenSys/QIBM/ProdData/Node/os400/xstoolkit/lib/itoolkit');
现在让我们继续修改 sample.js,使用 itoolkit 类来运行 CL 命令。
var http = require('http'); var fs = require('fs'); var url = require('url'); var db = require('/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2'); var xt = require('/QOpenSys/QIBM/ProdData/Node/os400/xstoolkit/lib/itoolkit'); var DBname = "*LOCAL "; var ip = "IP_Address"; var port = Port; var webserver = http.createServer(function (req, res) { var realPath = __dirname + url.parse(req.url).pathname; fs.exists(realPath,function(exists){ if(!exists){ var sql = url.parse(req.url, true).query.sql; var cl = url.parse(req.url, true).query.cl; res.writeHead(200, {'Content-Type': 'text/plain'}); if(sql && sql.length > 0) { console.log("SQL statement : " + sql); db.init(); db.conn(DBname); db.exec(sql, function(rs) { res.write(JSON.stringify(rs)); }); db.close(); } if(cl && cl.length > 0) { console.log("CL statement : " + cl); var conn = new xt.iConn(DBname); conn.add(xt.iSh("system -i " + cl)); function cb(str) { res.write(xt.xmlToJson(str)[0].data); } conn.run(cb); } res.end(); } else { var file = fs.createReadStream(realPath); res.writeHead(200, {'Content-Type': 'text/html'}); file.on('data', res.write.bind(res)); file.on('close', res.end.bind(res)); file.on('error', function(err){ res.writeHead(500, {'Content-Type': 'text/plain'}); res.end("500 Internal Server Error"); }); } }); }); webserver.listen(port, ip); console.log('Server running at http://' + ip + ':' + port);
Node.js Toolkit for IBM i 允许批量运行多个命令。在本例中,我们只需获取第一个结果。
现在在您的浏览器会话中刷新 http://ip:port/sample.html 页面。在第二个字段中输入一条 CL 命令并单击 Run 。Node.js Toolkit for IBM i 将会运行该命令并将结果返回给浏览器。在本例中,运行了 Display System Status (DSPSYSSTS) 命令来获取当前系统状态。
图 11. 使用 Node.js 运行 DSPSYSSTS 命令
下图显示了该 CL 命令的输出。
图 12. DSPSYSSTS 示例输出
点击查看大图
关闭 [x]
回页首
JavaScript 是一种长期被广泛使用的浏览器脚本语言。现在已在服务器端启用 Node.js,这使它对 IBM i 客户端更加有用。除了经典的 JavaScript API 之外,Node.js 还提供了多个内置的模块来轻松构建现代应用程序。请参阅 Node.js v0.12.0 手册和文档 ,解这些内置 API 的更多细节。DB2 访问库和 Node.js Toolkit for IBM i 在 IBM i 上进一步扩展了 Node.js 的功能。此外,您可以使用 npm 工具安装数千个第三方模块。祝您在 IBM i 上使用 Node.js 愉快。
回页首