pm2 status error 解决

 Nodejs, pm2  pm2 status error 解决已关闭评论
2月 202019
 

今天使用pm2  启动nodejs应用, 发现启动一会儿,pm2 中status状态就从online变成error, 日志也看没有什么记录, 后来发现可以通过下面的步骤解决:  pm2 update

如果彻底些,可以先kill 掉pm2的进程, 再执行pm2 update命令。

查看帮助

$ pm2 update -h

  Usage: update [options]

  (alias) update in-memory PM2 with local PM2

  Options:

    -h, –help  output usage information

通过上面命令查看,估计问题出在本机PM2的内存数据里

使用nvm利器,管理node版本(转)

 Nodejs  使用nvm利器,管理node版本(转)已关闭评论
5月 242018
 

node.js越来越热,应用的场景也越来越多。

但也因为是开源软件,所以具备大多数开源软件都存在的“版本问题”,版本发展很快,版本前后差异性大,老系统用新版本node跑不过,全局安装的第三方组件和node版本相关造成全局版本混乱。



nvm是解决这一问题的利器。

nvmnode版本管理工具,主要特点:

1.可安装多版本的node

2.灵活切换当前的node版本。

3.以沙箱方式全局安装第三方组件到对应版本的node中。

4.通过.vnmrc文件,方便灵活地指定各应用系统所需的node版本进行运行。

可惜目前只支持linuxOS系统。

接下来,就见证一下它的能力。 

 

安装nvm

通过下面的命令可进行一步到位的安装,下面两种方式可二选一。

curl
-o-
https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh |
bash

或者:

wget
-qO-
https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh |
bash

 

安装完后,验证安装

command
-v nvm

默认会在“/home/个人账户”目录下会生成.nvm的隐藏目录,所有的node会以沙箱的方式装到该目录下。

安装完成。

 

详细可参考官方:https://github.com/creationix/nvm 

 

安装多版本node

在命令行中运行命令,安装当前最新的稳定版。

nvm
install stable

当前最新的稳定版是6.3.0

 

运行命令,指明版本,安装早期的版本0.12.4

nvm
install 0.12.4

  

安装后,当前使用的node版本默认为最后一次安装的版本,在命令行中运行命令可查看当前版本。

nvm
current

 

切换node版本

运行命令切换。

nvm
use 6

 

显示所有安装的版本

运行命令,列出所有版本。

nvm
ls

 

左边有箭头的代表当前使用中的版本。

 

安装全局组件 

nvm管理的情况下,全局组件是安装到/usr/local/lib/node_modules下,然后通过软连接的方式把包中bin目录下的可执行文件链接到/usr/local/bin。不管用什么版本都装到这些目录下,多版本就没法玩了。

nvm管理下,以沙箱的方式,全局组件会装到.nvm目录的当前版本node下,也就是装在nvm这个沙箱里,跟在指定版本的node下,当前有什么版本的node,就有对应的全局组件。这是nvm强大的地方,在多运行环境的管理和切换极为好用。

下面,运行命令在当前6.3.0node版本下以全局的方式安装express生成器。

npm
install -g express-generator

express
-V

可以清楚的看到express生成器被安装到了当前版本的node目录下。

express默认用的是最新的版本,这里是4.13.4

 

现在,切换到0.12.4版本 

nvm
use 0

然后,全局安装express生成器3.0版。

npm
install -g express-generator@3

也可以清楚的看到express生成器装到了0.12.4node目录下,装完后express版本是3.0.0

可以再将node版本切换到6,那么当前的express版本又会切换成对应的版本。

很方便,很强大。

 

使用.nvmrc文件运行

在服务器上很多时候会运行多个应用系统,每个应用系统使用的node版本是不一样的,老系统用0.12.x甚至0.10.x,新系统用了新特新所以用最新的node版本,都很实际很正常。 

为了让不同的应用系统使用各自所需的node版本运行,我们只需在各应用系统内的根目录里生成一个.nvmrc文件,在其内写一个版本号,利用nvm
run <
系统启动文件>的方式运行系统,即可完成要求。

做个实验。

建个demo的文件夹,在其下写一个app1.js文件,其内容为:

console.log(process.versions);

只有一行代码,把当前运行的node版本输出到控制台。

 

app1.js同级,写一个.nvmrc文件,其中的版本号写“0”,目的是要在0.12.4版本下运行,可以用命令:

echo
'0' > .nvmrc

 

我们将当前的node版本切至6,然后运行:

nvm
use 6
nvm
run app1.js

运行效果:

可以看到运行demonode版本取决于.nvmrc中写的版本。

通过.nvmrc作为运行版本配置,用nvm
run
命令进行启动的方式可以方便的完成同一个服务器运行多个不同版本的应用系统,非常方便灵活。

转自:https://www.cnblogs.com/kongxianghai/p/5660101.html

切换npm的官方源为淘宝镜像源

 Nodejs  切换npm的官方源为淘宝镜像源已关闭评论
3月 192018
 

前言

npm的连接地址建议换成淘宝npm镜像的地址,原因就是下载速度快些(吐嘈下阿里云机器,下载也是龟速!!)

默认的npm下载地址:http://www.npmjs.org/ 
淘宝npm镜像的地址:https://npm.taobao.org/

临时使用淘宝源

npm --registry https://registry.npm.taobao.org install node-red-contrib-composer@latest 

全局配置切换到淘宝源

npm config set registry https://registry.npm.taobao.org 

全局配置切换到官方源

npm config set registry https://registry.npmjs.org/ 

检测是否切换到了淘宝源

npm info underscore
.......

gitHead: 'e4743ab712b8ab42ad4ccb48b155034d02394e4d',
  dist: 
   { shasum: '4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022',
     size: 34172,
     noattachment: false, // 有 registry.npm.taobao.org 等字样  说明切换成功 tarball: 'http://registry.npm.taobao.org/underscore/download/underscore-1.8.3.tgz' },
  directories: {},
  publish_time: 1427988774520 }


.......

nodejs 中使用log4js

 Nodejs  nodejs 中使用log4js已关闭评论
6月 152017
 

转自:http://blog.csdn.net/heiantianshi1/article/details/43984601

因为log4js-node从log4js移植而来,两者并不属于同一个模块,也有很多区别,下面为了区分和防止误解统一使用“log4js-node”这个名称,注意nodejs代码中引入模块时还是要用“require(‘log4js’)”。

1. 快速开始
1.1. 简单终端log
先看一下使用log4js-node 最简单的代码:
运行的结果如下:
首先log4js-node将输出的log分为六个的level,每个level的含义从字面就可以看出, 对于不同的level,log输出到终端时会使用不同的颜色(trace: 蓝色,debug: 青色, info:绿色,warn:黄色,error:红色, fatal:粉色)。终端是log4js-node的默认输出地点。
默认情况下,log4js-node的每条log输出格式有如下几个字段:日志产生时间,level,日志分类,日志内容。如果用户没有配置,日志分类字段为“default”。
1.2. 文件log
代码:
运行结果如下:
这个例子当中我们将日志存储到文件当中,并进行文件分割,具体的日志tag(或category)使用文件名。
2. 基本配置和基本分类

代码,注意用户需要先在项目目录下创建名为“logs”的目录:
终端结果:
__dirname/logs/login.log文件中输出结果,其中__dirname是项目根目录:
__dirname/logs/signup.log文件中得输出结果:
代码中我们调用了log4js-node的configure函数,该函数中我们传入了一个json对象,json对象中我们为log4js-node指定了三个appender,分别是一个终端和两个文件。我们也为两个文件appender指定了存储文件路径,和分类,路径决定了每个appender输出的log所存储文件的位置和名称,分类对应于前文提到的日志输出格式的分类字段。一个appender相当于一个日志输出目标,该目标决定了对应日志的输出格式,存储位置等。
指定不同的appender后,我们在调用log4js-node的getLogger()时需要指定对应的appedner的分类名,比如“getLogger(‘login’)”, 如果不指定默认为输出到终端的appender。
我们还可以以配置文件的方式来提供配置,只需要将文件路径传给log4js-node的configure的函数即可,效果一样。上面的配置我们改为以下实现方式,首先我们在项目的根目录下新创建一个“conf”目录(这里的名称可以根据自己的情况随意取),然后将如下配置内容存到conf目录下,并命名为“log4js_conf.json”(这里的文件名称也可以随便取,但后缀名要为.json):
然后我们将之前代码中的log4js.configure()调用语句改为:log4js.configure(__dirname + ‘/conf/log4js_conf.json’)。运行后结果与前面相同。注意文件配置时, filename对应的路径默认从app的根目录开始。
到这里,我们已经能够将日志进行分类输出,并存储到不同的文件当中,这已经能够满足一般的业务需求了。但是,如果我们想定制日志的输出格式该怎么办呢?另外,目前日志虽然分到不同的文件中,但是每个文件都是无限增长的,话句话说如果日志足够多的话,我们的单个日志文件将会非常大,这为我们打开日志文件和分析日志增加了难度,如何才能解决这个问题呢?这些问题我们将在后文种做详细介绍。
3. 高级设置
3.1. 为log设置不同的level
设置log level有两种主要的方式,一种是在代码中通过调用每个logger的setLevel函数,另外一种是在配置文件中加入levels选项。首先看第一种,我们为之前的两个logger“loginLogger4js”和“signupLogger4js”分别调用setLevel函数,具体如下:
终端输出结果如下:
然后我们将第一种方式的setLevel函数调用去掉,换用第二种方式,在之前的配置文件“log4js_conf.json”中加入level配置选项如下:
终端的输出结果如下,与第一种方式相同:
当然我们还可以,直接设置全局的level,这样我们就不用为每个logger单独配置了,如果全局配置和具体logger的配置同时存在,那么log4js-node会以具体logger的配置为准。全局level也是两种方式:代码中和文件中。首先看前者,我们将之前的每个logger的level配置代码都去掉,然后再代码中加入如下一句:
运行结果如下:
第二种全局level配置方式,我们把配置文件“log4js_conf.json”内容改为如下:
运行结果如下:
3.2. 将日志文件分开存储
这个配置并不是使用所有的appender的,比如在console就是不需要的,这里只讨论appender 的type为’file’的情况。首先我们看一下log4js内置的file appender的初始化函数:
从注释中我们就可以看书第三个参数“logSize”和第四个参数“numBackups”决定了是否要分割日志文件,每个日志文件的最大容量,以及最大的文件数。这两个参数对应于配置中的“maxLogSize”和“backups”选项, 前者单位是bytes, 后者单位是个。接着前面的例子,我们在将配置文件改为如下内容(后面内容我们统一用配置文件的方式):
生成的日志备份如下:
若果超出了总大小旧的文件会被覆盖掉。
3.3. http log
安装后log4js-node代码中自带一个“example-connect-logger.js”例子,它展示了如何使用log4js输出http log。这里不再赘述,不过要说明一下如何定制输出格式。
首先看源码中关于connect-logger.js中getLogger函数的注释:
/**

 * Log requests with the given `options` or a `format` string.
 *
 * Options:
 *
 *   – `format`        Format string, see below for tokens
 *   – `level`         A log4js levels instance. Supports also ‘auto’
 *
 * Tokens:
 *
 *   – `:req[header]` ex: `:req[Accept]`
 *   – `:res[header]` ex: `:res[Content-Length]`
 *   – `:http-version`
 *   – `:response-time`
 *   – `:remote-addr`
 *   – `:date`
 *   – `:method`
 *   – `:url`
 *   – `:referrer`
 *   – `:user-agent`
 *   – `:status`
 *
 * @param {String|Function|Object} format or options
 * @return {Function}
 * @api public

 */
从注释中我们可以知道有哪些token可以用来定制我们自己的输出格式,这里只举出一个例子来说明。这个例子是比较精简的,主要用来调试,具体如下:
var HTTP_LOG_FORMAT_DEV = ‘:method :url :status :response-time ms – :res[content-length]’;
app.use(log4js.connectLogger(logger, { level: ‘auto’, format: HTTP_LOG_FORMAT_DEV }));
其中的一个输出结果为:
[2014-12-30 11:48:35.784] [ERROR] log4jslog -GET /users 404 4 ms – 18

nodejs中使用yargs 模块处理命令行参数

 Nodejs  nodejs中使用yargs 模块处理命令行参数已关闭评论
6月 152017
 

yargs 模块

shelljs 只解决了如何调用 shell 命令,而 yargs 模块能够解决如何处理命令行参数。它也需要安装。

$ npm install --save yargs

yargs 模块提供 argv 对象,用来读取命令行参数。请看改写后的 hello 。

#!/usr/bin/env node var argv = require('yargs').argv; console.log('hello ', argv.name); 

使用时,下面两种用法都可以。

$ hello --name=tom
hello tom

$ hello --name tom
hello tom

也就是说,process.argv 的原始返回值如下。

$ node hello --name=tom [ 'node', '/path/to/myscript.js', '--name=tom' ] 

yargs 可以上面的结果改为一个对象,每个参数项就是一个键值对。

var argv = require('yargs').argv;  // $ node hello --name=tom // argv = { //   name: tom // }; 

如果将 argv.name 改成 argv.n,就可以使用一个字母的短参数形式了。

$ hello -n tom
hello tom

可以使用 alias 方法,指定 name 是 n 的别名。

#!/usr/bin/env node var argv = require('yargs') .alias('n', 'name') .argv; console.log('hello ', argv.n); 

这样一来,短参数和长参数就都可以使用了。

$ hello -n tom
hello tom
$ hello --name tom
hello tom

argv 对象有一个下划线(_)属性,可以获取非连词线开头的参数。

#!/usr/bin/env node var argv = require('yargs').argv; console.log('hello ', argv.n); console.log(argv._); 

用法如下。

$ hello A -n tom B C
hello  tom [ 'A', 'B', 'C' ] 

yargs命令行参数的配置

yargs 模块还提供3个方法,用来配置命令行参数。

  • demand:是否必选
  • default:默认值
  • describe:提示
#!/usr/bin/env node var argv = require('yargs') .demand(['n']) .default({n: 'tom'}) .describe({n: 'your name'}) .argv; console.log('hello ', argv.n); 

上面代码指定 n 参数不可省略,默认值为 tom,并给出一行提示。

options 方法允许将所有这些配置写进一个对象。

#!/usr/bin/env node var argv = require('yargs') .option('n', { alias : 'name', demand: true, default: 'tom', describe: 'your name', type: 'string' }) .argv; console.log('hello ', argv.n); 

有时,某些参数不需要值,只起到一个开关作用,这时可以用 boolean 方法指定这些参数返回布尔值。

#!/usr/bin/env node var argv = require('yargs') .boolean(['n']) .argv; console.log('hello ', argv.n); 

上面代码中,参数 n 总是返回一个布尔值,用法如下。

$ hello
hello false $ hello -n
hello true $ hello -n tom
hello true 

boolean 方法也可以作为属性,写入 option 对象。

#!/usr/bin/env node var argv = require('yargs') .option('n', { boolean: true }) .argv; console.log('hello ', argv.n); 

yargs帮助信息

yargs 模块提供以下方法,生成帮助信息。

  • usage:用法格式
  • example:提供例子
  • help:显示帮助信息
  • epilog:出现在帮助信息的结尾
#!/usr/bin/env node var argv = require('yargs') .option('f', { alias : 'name', demand: true, default: 'tom', describe: 'your name', type: 'string' }) .usage('Usage: hello [options]') .example('hello -n tom', 'say hello to Tom') .help('h') .alias('h', 'help') .epilog('copyright 2015') .argv; console.log('hello ', argv.n); 

执行结果如下。

$ hello -h

Usage: hello [options] Options: -f, --name  your name [string] [required] [default: "tom"] -h, --help  Show help [boolean] Examples: hello -n tom  say hello to Tom

copyright 2015 

yargs子命令

yargs 模块还允许通过 command 方法,设置 Git 风格的子命令。

#!/usr/bin/env node var argv = require('yargs') .command("morning", "good morning", function (yargs) { console.log("Good Morning"); }) .command("evening", "good evening", function (yargs) { console.log("Good Evening"); }) .argv; console.log('hello ', argv.n); 

用法如下。

$ hello morning -n tom
Good Morning
hello tom

可以将这个功能与 shellojs 模块结合起来。

#!/usr/bin/env node require('shelljs/global'); var argv = require('yargs') .command("morning", "good morning", function (yargs) { echo("Good Morning"); }) .command("evening", "good evening", function (yargs) { echo("Good Evening"); }) .argv; console.log('hello ', argv.n); 

每个子命令往往有自己的参数,这时就需要在回调函数中单独指定。回调函数中,要先用 reset 方法重置 yargs 对象。

#!/usr/bin/env node require('shelljs/global'); var argv = require('yargs') .command("morning", "good morning", function (yargs) { echo("Good Morning"); var argv = yargs.reset() .option("m", { alias: "message", description: "provide any sentence" }) .help("h") .alias("h", "help") .argv; echo(argv.m); }) .argv; 

用法如下。

$ hello morning -m "Are you hungry?" Good Morning
Are you hungry?

转自:http://www.ruanyifeng.com/blog/2015/05/command-line-with-node.html

12月 032014
 

好文章,收藏下。

npm 是 Node.js 的模块依赖管理工具。作为开发者使用的工具,主要解决开发 Node.js 时会遇到的问题。如同 RubyGems 对于 Ruby 开发者和 Maven 对于 Java 开发者的重要性,npm 对与 Node.js 的开发者和社区的重要性不言而喻。本文包括五点:package.json 、npm 的配置、npm install 命令、npm link 命令和其它 npm 命令。

package.json

npm命令运行时会读取当前目录的 package.json 文件和解释这个文件,这个文件基于Packages/1.1 规范。在这个文件里你可以定义你的应用名称( name )、应用描述( description )、关键字( keywords )、版本号( version )、应用的配置项( config )、主页( homepage )、作者( author )、资源仓库地址( repository )、bug的提交地址( bugs ),授权方式( licenses )、目录( directories )、应用入口文件( main )、命令行文件( bin )、应用依赖模块( dependencies )、开发环境依赖模块( devDependencies )、运行引擎( engines )和脚本( scripts )等。

对于开发者而言,开发和发布模块都依赖于他对这个文件 package.json 所包含的意义的正确理解。我们下面用一个本文共用的例子来说明:

{
    "name": "test",
    "version": "0.1.0",
    "description": "A testing package",
    "author": "A messed author <messed@example.com>",
    "dependencies": {
        "express": "1.x.x",
        "ejs": "0.4.2",
        "redis": ">= 0.6.7"
    },
    "devDependencies": {
        "vows": "0.5.x"
    },
    "main": "index",
    "bin": {
        "test": "./bin/test.js"
    },
    "scripts": {
        "start": "node server.js",
        "test": "vows test/*.js",
        "preinstall": "./configure",
        "install": "make && make install"
    },
    "engines": {
        "node": "0.4.x"
    }
}

这个例子里我们定义了应用的入口文件( main )为 index ,当其他应用引用了我们的模块 require(‘test’) 时,这个 main 的值 index.js 文件被调用。脚本( scripts )使用hash 表定义了几个不同的命令。script.start 里的定义的 node server.js 会在 npm start 时被调用,同样的 npm test 调用时对应的 scripts.test 里定义的命令被调用。在有些 native 模块需要编译的话,我们可以定义预编译和编译的命令。本例中还定义了应用依赖模块( dependencies )和开发环境依赖模块( devDependencies )。应用依赖模块会在安装时安装到当前模块的 node_modules 目录下。开发环境依赖模块主要时在开发环境中用到的依赖模块,用命令 npm 的命令 install 或 link 加上参数 —dev 安装到当前模块的 node_modules 目录下。

大家也注意到 package.json 里的版本号有些是 >= 0.6.7 有些是 1.x.x,这有什么区别?npm 使用于语义化的版本识别来进行版本管理。并不是所有的模块都会提供向后兼容性,有时候某些模块因为某些原因导致不向后兼容。所以我们需要定义一些规则来保证模块能够在某些特定的版本中可用,并且保证能用最新的版本,因为那些版本总是修改了一些 bug 或提升了性能等。我们来看一下版本定义的字段:0.4.2

  • 主版本( 0 )
  • 副版本( 4 )
  • 补丁版本( 2 )

在上面 package.json 的定义里我们确信模块在所有的 Nodejs 0.4及以上和0.5以下版本里都能运行。依赖模块 redis 在所有大于或等于0.6.7的版本上都能运行,依赖模块 ejs 只能确保运行在0.4.2版本里,依赖模块 express 确保能够兼容大于或等于1.0.0并且小于2.0.0。

npm 的配置

npm 拥有很多默认配置。你可以使用这些默认的配置,也可以修改这些默认的配置,甚至可以在环境变量或命令行下修改这些配置。配置的权重是如下顺序定义的:

  1. 命令行,使用—为前缀的参数。比如 —foo bar,设定变量 foo 的值为” bar “。—foo 后不带值的参数,设定 foo 的值为 true 。
  2. 环境变量,所有 npm_config_ 为前缀的环境变量。比如 npm_config_foo = bar ,设定变量 foo 为 “ bar “。
  3. 用户定义。所有的变量存储在 $HOME/.npmrc 文件里的变量。
  4. 全局。所有 $PREFIX/etc/npmrc 文件里的变量。$PREFIX 变量可通过 npm prefix -g 获取,一般默认是 /usr/local。
  5. 内置的配置。通过安装时运行 ./configure 所定义的变量。可通过命令curlhttp://npmjs.org/install.sh | env npm_config_foo=bar sh 设置。

使用配置能给我们带来很大的灵活性。比如我们使用 npm install 时,对默认的资源库地址https://registry.npmjs.org/ 不是很满意,我们可以使用下面的命令来更改资源库地址。

npm --registry "an other registry" install express
# 或者下面的命令
env npm_config_registry="an other registry" npm install express 

或是对 npm 默认的 vi 编辑器不满意,直接命令 npm set editor mate 。npm 的配置可通过命令 npm config ls 获取。这个命令是获取修改后的配置,要获取包括默认配置的全部配置加上 -l 参数。值得注意的是,开发者通过 npm config set registry “an other registry” 的方式修改 registry 这个属性值,一定要明白这个修改这个值所带来的负面效应。一旦设置了 registry 这个值,当你要 publish 一个模块,会把模块发布到修改后的资源库里,而不是原始默认的资源库。其他的资源库是原始默认的资源库的一个复制品,定时从默认的资源库取资源。一般来说,没有把其新家的模块同步到默认的资源库的能力。这样会导致发生你的模块在修改后的资源库里能够找到,而在其它的资源库里找不到的事情。

npm install命令

安装模块只需要 npm install express connect 命令给我们带来了很大的方便。安装模块的路径分两种:

  • 全局路径,也就是带上参数 -g 的安装模式。这个命令会把模块安装在 $PREFIX/lib/node_modules 下,可通过命令 npm root -g 查看全局模块的安装目录。 package.json 里定义的bin会安装到 $PREFIX/bin 目录下,如果模块带有 man page 会安装到 $PREFIX/share/man 目录下。
  • 本地路径,不带 -g 参数的。从当前目录一直查找到根目录/下有没有 node_modules 目录,有模块安装到这个目录下的 node_modules 目录里,如果没有找到则把模块安装到当前目录 node_modules 目录下。package.josn 定义的 bin 会安装到 node_modules/.bin 目录下,man page 则不会安装。

我们需要选择什么样的安装方式呢?全局模式可以让你不用担心找不到模块,如果不需要还是尽量避免全局模式。

  • 如果我们只是 require(‘pkg’) 一个模块,我们不需要使用全局模式。
  • 如果我们需要在命令行中调用,我们需要使用全局模式。因为这个安装把 package.josn里 bin 下的定义安装到 $PATH 目录下。

有些模块我们既需要在命令行中调用又想 require(‘pkg’) ,比如 Coffee-script 。那么我们可以使用全局模式安装,然后使用下一段要讲的命令 npm link 把它链接到本地的 node_modules 目录下。

不要担心 package.josn 里 script 中定义的命令会不会因为不是全局安装而不能运行。比如在例子里定义的 devDependencies 的 vows 。在调用 npm test 时 npm 会把 node_modules/.bin 目录放到环境变量 $PATH 的最前面。

npm link命令

对开发者而言,这算是最有价值的命令。假设我们开发了一个模块叫 test ,然后我们在 test-example 里引用这个模块 ,每次 test 模块的变动我们都需要反映到 test-example 模块里。不要担心,有了 npm link 命令一切变的非常容易。

首先我们需要把 test 链接到全局模式下:

cd ~/work/node/test # 进入test模块目录
npm link # 创建链接到$PREFIX/lib/node_modules

那么 test 的模块将被链接到 $PREFIX/lib/node_modules 下,就像我的机器上 $PREFIX 指到 /usr/local ,那么 /usr/local/lib/node_modules/test 将会链接到 ~/work/node/test 下。执行脚本 bin/test.js 被链接到 /usr/local/bin/test 上。

接下来我们需要把 test 引用到 test-example 项目中来:

cd ~/work/node/test-example # 进入test-example模块目录
npm link test # 把全局模式的模块链接到本地

npm link test 命令会去 $PREFIX/lib/node_modules 目录下查找名叫 test 的模块,找到这个模块后把 $PREFIX/lib/node_modules/test 的目录链接到 ~/work/node/test-example/node_modules/test 这个目录上来。

现在任何 test 模块上的改动都会直接映射到 test-example 上来。再比如假设我们开发很多应用,每个应用都用到 Coffee-script :

npm install coffee-script -g # 全局模式下安装coffee-script
cd ~/work/node/test # 进入开发目录
npm link coffee-script # 把全局模式的coffee-script模块链接到本地的node_modules下
cd ../test-example # 进入另外的一个开发目录
npm link coffee-script # 把全局模式的coffee-script模块链接到本地
npm update coffee-script -g # 更新全局模式的coffee-script,所有link过去的项目同时更新了。

就像你看到,npm link 对于开发时一个模块被多个模块引用时非常有用。windows 的用户会想,我这儿没有 UNIX 下的 link 工具怎么办?别担心只要你的 Node.js 支持 fs.symlink 就可用到这个特性。

其它 npm 命令

npm 命令里还有很多有用的命令。npm explore . — git pull origin master ,更新当前的 git 资源库。npm edit . ,编辑当前模块的所有依赖模块。npm docs coffee-script ,打开 coffee-script 模块的文档。npm outdated coffee-script ,查看 coffee-script 是否有新版本。npm submodule . ,你可以要求你的依赖模块是从 git 资源库安装的,而不是从 registry 安装。因为作者的 git 资源库总是最新的版本,registry 上的是模块作者发布上去的稳定版本。甚至你可以用 npm 来编程。

var npm = require('npm');
npm.load({}, function (err) {
    if (err) return commandFailed(err);
    npm.on("log", function (message) {
        if (arg) console.log(message)
    })
    var requirements = JSON.parse(fs.readFileSync('config/requirements.json'));
    npm.commands.install(requirements, function (err, data) {
        if (err) return commandFailed(err);
    });
});

做为 Node.js 的开发者工具,npm 已经为我们想到很多的应用场景。这也是 Node.js 社区一致推荐它为开发者模块依赖管理工具。

来自:http://www.infoq.com/cn/articles/msh-using-npm-manage-node.js-dependence/

 Posted by at 下午12:23  Tagged with:
8月 282014
 

收集了一些关于npm的资料,记录下。

一、安装NPM

NPM的全称是Node Package Manager,如果你熟悉ruby的gem,Python的PyPL、setuptools,PHP的pear,那么你就知道NPM的作用是什么了。没错,它就是Nodejs的包管理器。Nodejs自身提供了基本的模块。但是在这些基本模块上开发实际应用需要较多的工作。所幸的是笔者执笔此文的时候NPM上已经有了5112个Nodejs库或框架,这些库从各个方面可以帮助Nodejs的开发者完成较为复杂的应用。这些库的数量和活跃也从侧面反映出Nodejs社区的发展是十分神速和活跃的。下面我将介绍安装NPM和通过NPM安装Nodejs的第三方库,以及在大陆的网络环境下,如何更好的利用NPM。

Unix/Linux下安装NPM

就像NPM的官网(http://npmjs.org/)上介绍的那样,安装NPM仅仅是一行命令的事情:

curl http://npmjs.org/install.sh | sh

这里详解一下这句命令的意思,curl http://npmjs.org/install.sh是通过curl命令获取这个安装shell脚本,按后通过管道符| 将获取的脚本交由sh命令来执行。这里如果没有权限会安装不成功,需要加上sudo来确保权限:

curl https://npmjs.org/install.sh | sudo sh

安装成功后执行npm命令,会得到一下的提示:

Usage: npm <command>
where <command> is one of:
adduser, apihelp, author, bin, bugs, c, cache, completion,
config, deprecate, docs, edit, explore, faq, find, get,
help, help-search, home, i, info, init, install, la, link,
list, ll, ln, ls, outdated, owner, pack, prefix, prune,
publish, r, rb, rebuild, remove, restart, rm, root,
run-script, s, se, search, set, show, star, start, stop,
submodule, tag, test, un, uninstall, unlink, unpublish,
unstar, up, update, version, view, whoami

我们以underscore为例,来展示下通过npm安装第三方包的过程。

npm install underscore

返回:

underscore@1.2.2 ./node_modules/underscore

由于一些特殊的网络环境,直接通过npm install命令安装第三方库的时候,经常会出现卡死的状态。幸运的是国内CNode社区的@fire9同学利用空余时间搭建了一个镜像的NPM资源库,服务器架设在日本,可以绕过某些不必要的网络问题。你可以通过以下这条命令来安装第三方库:

npm –registry “http://npm.hacknodejs.com/” install underscore

如果你想将它设为默认的资源库,运行下面这条命令即可:

npm config set registry “http://npm.hacknodejs.com/

设置之后每次安装时就可以不用带上—registry参数。值得一提的是还有另一个镜像可用,该镜像地址是http://registry.npmjs.vitecho.com,如需使用,替换上面两行命令的地址即可。

二、NPM常用命令介绍

npm install xxx 安装模块 

npm install xxx@1.1.1 安装1.1.1版本的xxx

npm install xxx -g 将模块安装到全局环境中。

 npm ls 查看安装的模块及依赖
npm ls -g 查看全局安装的模块及依赖 

npm uninstall xxx (-g) 卸载模块 

npm cache clean 清理缓存 

npm help xxx 查看帮助 

npm view moudleName dependencies 查看包的依赖关系 

npm view moduleNames 查看node模块的package.json文件夹 

npm view moduleName labelName 查看package.json文件夹下某个标签的内容 

npm view moduleName repository.url 查看包的源文件地址 

npm view moduleName engines 查看包所依赖的Node的版本 

npm help folders 查看npm使用的所有文件夹 

npm rebuild moduleName 用于更改包内容后进行重建 

npm outdated  检查包是否已经过时,此命令会列出所有已经过时的包,可以及时进行包的更新 

npm update moduleName 更新node模块

  更多命令参考:https://npmjs.org/doc/

三、本地安装与全局安装

npm的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g而已,比如

npminstall grunt# 本地安装npminstall -g grunt-cli# 全局安装

这两种安装方式有什么区别呢?从npm官方文档的说明来看,主要区别在于(后面通过具体的例子来说明):
本地安装
1. 将安装包放在 ./node_modules 下(运行npm时所在的目录)
2. 可以通过 require() 来引入本地安装的包

全局安装
1. 将安装包放在 /usr/local 下
2. 可以直接在命令行里使用

本地安装

1、将安装包放在 ./node_modules 下(运行npm时所在的目录)

比如运行下面命令

npm install grunt–save-dev

那么,就会在当前目录下发现一个node_modules目录,进去后能够看到grunt这个包

casperchenMacBookPro:testUsemincasperchen$lltotal200drwxr-xr-x16casperchenstaff544B12 14 23:17node_modules

进入node_modules

casperchenMacBookPro:node_modulescasperchen$lltotal0drwxr-xr-x16casperchenstaff544B12 5 00:49grunt

2、可以通过 require() 来引入本地安装的包

直接来个例子,我们在项目根目录下创建test.js,里面的内容很简单

vargrunt=require(‘grunt’);grunt.log.writeln(‘hello grunt’);

然后在控制台运行test.js

node test.js

然后就会看到如下输出

casperchenMacBookPro:testUsemin casperchen$node test.js
hello grunt

全局安装

1、将安装包放在 /usr/local 下

运行如下命令

npminstall -g grunt-cli

然后进入/usr/local/bin目录,就会发现grunt-cli已经被放置在下面了

casperchenMacBookPro:bin casperchen$pwd
/usr/local/bincasperchenMacBookPro:bin casperchen$ll grunt
lrwxr-xr-x1root admin39B81821:43grunt -> ../lib/node_modules/grunt-cli/bin/grunt

可见,全局模块的真实安装路径在/usr/local/lib/node_modules/下,/usr/local/bin下的可执行文件只是软链接而已

2、可以直接在命令行里使用

实现细节在上面其实就讲到了,通过在`/usr/local/bin下创建软链接的方式实现。这里不赘述

更直观的例子

下面就直接看下,当我们在项目目录下运行grunt task(task为具体的grunt任务名,自行替换)时,发生了什么事情。这里要借助node-inspector。

首先,没接触过node-inspector的童鞋可以参考之前的文章了解下

运行如下命令开启调试

node-inspector &

见到如下输出

casperchenMacBookPro:tmp casperchen$node-inspector &
[1]14390casperchenMacBookPro:tmp casperchen$NodeInspectorv0.6.1info – socket.io startedVisithttp://127.0.0.1:8080/debug?port=5858to start debugging.

接着,在当前任务下运行grunt任务

^CcasperchenMacBookPro:testUsemincasperchen$node –debug-brk$(which grunt) dev
debugger listening on port5858

接着,打开chrome浏览器,输入网址http://127.0.0.1:8080/debug?port=5858,就会自动进入断点调试状态
。从一旁显示的tips可以看到,全局命令grunt其实就是/usr/local/lib/node_modules/grunt-cli/bin/grunt
image

按下F8接着往下跑,就会进如Gruntfile.js,此时的grunt,是本地安装的一个node包。全局命令跟本地的包名字一样,挺有迷惑性的。
image

4月 272014
 

node.js项目中有个项目发现运行一段时间后总会自动关闭,查看输出日志会看到下面的输出: error Caught exception: Error: Connection lost: The server closed the connection.  原来使用的node-mysql模块在mysql连接timeout或出现异常情况下默认是没有重连机制的,可以用下面的类似代码解决:

  var db;

  function handleConnect() {
    db = mysql.createConnection({
      host     : config.mysql.host,
      user     : config.mysql.user,
      password : config.mysql.password,
      database : config.mysql.database
    }); // Recreate the connection, since the old one cannot be reused.

    db.connect(function(err) {              // The server is either down
      if(err) {                                     // or restarting (takes a while sometimes).
        console.log(‘error when connecting to db:’, err);
        setTimeout(handleConnect, 2000); // We introduce a delay before attempting to reconnect,
      }                                     // to avoid a hot loop, and to allow our node script to
    });                                     // process asynchronous requests in the meantime.
                                            // If you’re also serving http, display a 503 error.
    db.on(‘error’, function(err) {
      console.log(‘db error’, err);
      if(err.code === ‘PROTOCOL_CONNECTION_LOST’) { // Connection to the MySQL server is usually
        handleConnect();                         // lost due to either server restart, or a
      } else {                                      // connnection idle timeout (the wait_timeout
        throw err;                                  // server variable configures this)
      }
    });
  }

  handleConnect();

7月 112013
 

先更新一条数据

UPDATE user_info SET userName = 'kk' WHERE userId = 1;

SELECT * FROM user_info

 
现在的数据
 
写一个事务,只选中前面的语句执行:
 
SET AUTOCOMMIT=0;

START TRANSACTION;
SELECT * FROM user_info FOR UPDATE;
COMMIT;

如果使用node.js执行,控制台上会看到待执行的sql处于等待状态:
 

上面报错,是因为超时了。
如果执行commit后,再执行node.js脚本(执行的是一句更新sql–UPDATE user_info SET userName = ‘张一’ WHERE userId = 1)

 

这里主要用到了node-mysql-queues,它需要先安装node-mysql

可能因为版本问题吧,它的页面给出的操作方法已经失效了。正确的应该如下:

/**
 * Created with JetBrains WebStorm.
 */
var mysql = require('mysql');

var connection = mysql.createConnection({
    host : 'localhost',
    port : 3306,
    user : 'root',
    password : '123456',
    database : 'test',
    charset : 'UTF8_GENERAL_CI',
    debug : false
});

connection.connect();

//connection.query('CALL proc_simple(1, @a, @b);', function(err, rows, fields) {
//    if (err) {
//       throw err;
//    }
//
//    var results = rows[0];
//    var row = results[0];
//    console.log("userName:",row.uName, "  count:", row.totalCount);
//});

var queues = require('mysql-queues');
const DEBUG = true;
queues(connection, DEBUG);

var trans = connection.startTransaction();
trans.query("UPDATE user_info SET userName = ? WHERE userId = ?", ["张一", 1], function(err, info) {
   if (err) {
       throw err;
       trans.rollback();
   } else {
       trans.commit(function(err, info) {
           console.log(info);
       });
   }
});
trans.execute();
console.log('execute');
//connection.end();

 

 

 

 

 

 

 

 

 

它还支持一次执行多条sql,其实就是把sql语句放入一个数组,然后循环执行该数组内的每条SQL。

var q = client.createQueue();
q.query(...);
q.query(...);
q.execute();



正常使用的话,请参考如下的代码:

var mysql = require('mysql');
var client = mysql.createConnection({//createClient(已失效) -> createConnection
    user: 'root',
    password: 'root'
});
//Enable mysql-queues
var queues = require('mysql-queues');
const DEBUG = true;
queues(client, DEBUG);
//Start running queries as normal...
client.query(...);

//Now you want a separate queue?
var q = client.createQueue();
q.query(...);
q.query(...);
q.execute();

client.query(...); //Will not execute until all queued queries (and their callbacks) completed.

//Now you want a transaction?
var trans = client.startTransaction();
trans.query("INSERT...", [x, y, z], function(err, info) {
    if(err)
        trans.rollback();
    else
        trans.query("UPDATE...", [a, b, c, info.insertId], function(err) {
            if(err)
                trans.rollback();
            else
                trans.commit();
        });
});
trans.execute();
//No other queries will get executed until the transaction completes
client.query("SELECT ...") //This won't execute until the transaction is COMPLETELY done (including callbacks)

//Or... as of version 0.3.0, you can do this...
var trans = client.startTransaction();
function error(err) {
    if(err && trans.rollback) {trans.rollback(); throw err;}
}
trans.query("DELETE...", [x], error);
for(var i = 0; i < n; i++)
    trans.query("INSERT...", [ y[i] ], error);
trans.commit(); //Implictly calls resume(), which calls execute()
/* In the case written above, COMMIT is placed at the end of the Queue, yet the
entire transaction can be rolled back if an error occurs. Nesting these queries
was not required. */

 
query方法的实现:
Connection.prototype.query = function(sql, values, cb) {
  this._implyConnect();

  var options = {};

  if (typeof sql === 'object') {
    // query(options, cb)
    options = sql;
    cb      = values;
    values  = options.values;

    delete options.values;
  } else if (typeof values === 'function') {
    // query(sql, cb)
    cb          = values;
    options.sql = sql;
    values      = undefined;
  } else {
    // query(sql, values, cb)
    options.sql    = sql;
    options.values = values;
  }

  options.sql = this.format(options.sql, values || []);

  if (!('typeCast' in options)) {
    options.typeCast = this.config.typeCast;
  }

  return this._protocol.query(options, cb);
};

如果第二个参数不是函数,调用format则转换sql,默认将?替换为数组参数的值(一一对应)
Connection.prototype.format = function(sql, values) {
  if (typeof this.config.queryFormat == "function") {
    return this.config.queryFormat.call(this, sql, values, this.config.timezone);
  }
  return SqlString.format(sql, values, this.config.timezone);
};

默认的format方法实现如下:
SqlString.format = function(sql, values, timeZone) {
  values = [].concat(values);

  return sql.replace(/?/g, function(match) {
    if (!values.length) {
      return match;
    }

    return SqlString.escape(values.shift(), false, timeZone);
  });
};

也可以使用自定义的函数进行处理,在创建连接的时候,传入queryFormat参数即可。

 

 
 
 

高性能Node.JS : 来自LinkedIn Mobile的10条优化建议

 Nodejs  高性能Node.JS : 来自LinkedIn Mobile的10条优化建议已关闭评论
6月 042013
 

一、避免使用同步代码(Avoid synchronous code)

 

Node.js是单线程的。为了使单线程可以处理多个并发请求,我们不应该让线程来等待一个阻塞的、同步的或是长时间运行的操作。Node.js的一个突出的特性就是它被完全地设计并实现为异步的。这使得其可以非常好地适用于事件驱动的程序。

遗憾的是,我们仍然有可能使用到同步/阻塞的调用。例如,很多对文件系统的操作都提供了同步和异步的两个版本,就像writeFilewriteFileSync。有时即使我们没有在自己的代码中使用同步的方法,但仍然有可能在无意中使用了包含阻塞调用的某个外部库。当您使用到同步/阻塞调用时,对性能的影响是巨大的。

  1. // Good: write files asynchronously
  2. fs.writeFile(message.txtHello Nodefunction (err) {
  3.   console.log("It’s saved and the server remains responsive!");
  4. });
  5.  
  6. // BAD: write files synchronously
  7. fs.writeFileSync(message.txtHello Node);
  8. console.log("It’s saved, but you just blocked ALL requests!");

我们最初的日志系统的实现中,不慎使用了一个写磁盘的同步调用。直到我们做性能测试之前这个情况一直没有被我们注意到。当我们在开发环境中对一个单实例的Node.js程序作基准测试的时候,这个同步调用使得吞吐率(requests per second)从几千一下子下降到了几十!

 

二、关闭socket池(Turn off socket pooling)

 

Node.js的http client自动使用socket池:在默认情况下,主机能同时并发打开的socket数量被限制为5个。虽然socket的重用也许能控制资源消耗的增长,但当我们需要处理多个从同一主机获取数据的并发请求时,这将是一个严重的瓶颈。在这些情况下,好的解决方案是增加maxSockets或者完全禁用socket池:

  1. // Disable socket pooling
  2.  
  3. var http = require(http);
  4. var options = {…..};
  5. options.agent = false;
  6. var req = http.request(options)

 

三、不要用Node.js处理静态文件(Don’t use Node.js for static assets)

 

对于静态文件,例如CSS和图片,建议使用标准的webserver来处理而非Node.js。例如LinkedIn mobiles使用的是nginx。并且我们使用CDN,将静态文件复制到遍布于全世界的各个节点。以上这些将带来两个好处:1、可以减轻Node.js服务器的负载;2、借助于CDN使得静态文件可以从离用户更近的节点传输予之,从而降低时延。

 

四、将渲染放到客户端做(Render on the client-side)

 

让我们来简单地对在服务器端和在客户端渲染一个页面进行一个比较。如果我们使用Node.js在服务器端进行渲染,在每次请求中我们将返回以下的HTML页面:

  1. <!– An example of a simple webpage rendered entirely server side –>
  2.  
  3. <!DOCTYPE html>
  4. <html>
  5.   <head>
  6.     <title>LinkedIn Mobile</title>
  7.   </head>
  8.   <body>
  9.     <div class="header">
  10.       <img src="http://mobile-cdn.linkedin.com/images/linkedin.png"alt="LinkedIn"/>
  11.     </div>
  12.     <div class="body">
  13.       Hello John!
  14.     </div>
  15.   </body>
  16. </html>

请注意到这个页面上除了用户的名称之外的所有东西都是静态的:也就是说,这些对于每个用户以及每次页面重载都是相同的。因此,一个更高效的做法是让Node.js以JSON格式只返回页面所需要的动态数据:

  1. {"name""John"}

页面的其余部分­——所有的静态HTML标记­——可以放入一个JavaScript模板(譬如一个underscore.js模板):

  1. <!– An example of a JavaScript template that can be rendered client side –>
  2.  
  3. <!DOCTYPE html>
  4. <html>
  5.   <head>
  6.     <title>LinkedIn Mobile</title>
  7.   </head>
  8.   <body>
  9.     <div class="header">
  10.       <img src="http://mobile-cdn.linkedin.com/images/linkedin.png"alt="LinkedIn"/>
  11.     </div>
  12.     <div class="body">
  13.       Hello <%= name %>!
  14.     </div>
  15.   </body>
  16. </html>

这样做的性能提升来自前述的第三条建议,静态的JavaScript模板可以用我们的webserver(譬如nginx)来处理,甚至是使用CDN就更好了。此外,JavaScript模板可以被浏览器缓存或者保存在LocalStorage中。这样一来在页面第一次被加载后,需要发送给客户端的数据就只有动态的JSON了,极大地提升了效率。这个方法大大降低了Node.js的CPU、I/O消耗和负载。

 

五、使用gzip(Use gzip)

 

大多数的服务器和客户端程序都支持用gzip来压缩请求和响应。请确保无论是在发送响应到客户端还是向远程服务器发送请求时,您都充分地利用到了它。

 

六、并行化(Go parallel)

 

尝试着将类似请求远程服务器、调用数据库和访问文件系统等将会引致阻塞的操作并行地执行。这样做的好处是,可以将完成一系列阻塞操作的总时延减小到其中最慢的某项操作所需要的时间,否则其总时延将会是完成操作序列中所有操作需要等待的时间总和。为了保持回调和错误处理函数的结构明晰,我们使用了Step来做flow control。

 

七、去session化(Go session-free)

 

LinkedIn mobile使用Express框架来处理请求/响应周期(request/response cycle)。大多数express的示例代码中包括了以下的配置:

  1. app.use(express.session({ secret"keyboard cat" }));

在默认情况下session数据存储在内存中,在用户数量增加的时候这会显著地增加服务器的开销。我们可以转而使用一个外部的session存储方案,例如MongoDB或Redis,但此时每个请求将增加远程调用获取session数据带来的额外开销。有可能的话,最好的选择是完全不在服务器端存储状态信息。不进行上述的express配置,不保存session,这样可以获得更好的性能。

 

八、使用二进制的模块(Use binary modules)

 

请尽可能地使用编译后的二进制模块而非用JavaScript编写的模块。举个例子,当我们从一个用JavaScript编写的SHA模块切换到Node.js内置的编译版本后,我们看到了巨大的性能提升:

  1. // Use built in or binary modules
  2. var crypto = require(crypto);
  3. var hash =crypto.createHmac("sha1",key).update(signatureBase).digest("base64");

 

九、使用标准的V8 JavaScript而非客户端的库(Use standard V8 JavaScript instead of client-side libraries)

 

JavaScript的版本不统一,而大部分JavaScript库是提供给web浏览器使用的:例如一款浏览器或许支持类似forEach、map和reduce这样的函数,但其他浏览器并不支持。其结果是,客户端的库常常要用很多低效的代码来掩盖浏览器间的差异。另一方面,在使用Node.js时你确切地知道哪些JavaScript函数是可用的:驱动Node.js的V8 JavaScript引擎实现的是ECMAScript的ECMA-262,第五版。通过直接使用标准的V8函数,而不是客户端的库,您将会再一次体验到显著的性能提升。

 

十、保持代码精简、轻量(Keep your code small and light)

 

面向移动设备进行开发时,设备的速度更慢而且时延更高,这迫使我们保持代码精简、轻量。您同样也应该把这种思想带到服务器端代码的开发中。时不时地进行自省,并且问自己:“我们是否真的需要这个模块?”,“为什么我们要使用这个框架?它带来的开销是值得的吗?”,“我们能否用一种更简单的方法来实现?”。更精简,更轻量的代码通常也是更高效,更快的。

原文:http://engineering.linkedin.com/nodejs/blazing-fast-nodejs-10-performance-tips-linkedin-mobile

译者及来源:http://www.rockdai.com/?p=596

 Posted by at 上午11:30  Tagged with: