Nodejs
脱离了浏览器的Javascript

Who am I?

Twitter: @fengmk2

Weibo: @Python发烧友 , @FaWave

来自淘宝EDP,花名@苏千

内容

  1. Hello world 性能对比

    为什么Nodejs会比其他语言的Hello world性能要好?

  2. String = Buffer => Stream

    从String到Buffer, 从Buffer到Stream,文件数据流,网络数据流
    Javascript可以轻松地通过Socket实现各种网络协议,还有功能完善的HTTP模块。

  3. 脱离了浏览器的 Javascript

    Nodejs能带来更多什么魔法呢?

  4. 第三方模块

    第三方模块的持续增长和完善,让Nodejs的世界变得越来越有趣。
    模块管理神器:npm

Hello world
性能对比

Nodejs的特点

Nodejs , Tornado , Go , Netty

Python: Tornado

from tornado.ioloop import IOLoop
from tornado.web import RequestHandler, \
                        Application

class MainHandler(RequestHandler):
    def get(self):
        self.write("Hello, world")

application = Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8080)
    IOLoop.instance().start()

Go:

package main
import (
   "http"
   "io"
)
func handler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Hello, world")
}
func main() {
     http.HandleFunc("/", handler)
     http.ListenAndServe(":8080", nil)
}

Java: Netty

private void handleHttpRequest(
        ChannelHandlerContext ctx, 
        HttpRequest req) throws Exception {
    HttpResponse res = 
        new DefaultHttpResponse(HTTP_1_1, OK);
    res.setHeader(CONTENT_TYPE, 
        "text/html; charset=UTF-8");
    setContentLength(res, 11);
    res.setContent(
        ChannelBuffers.copiedBuffer(
            "hello world", 
            CharsetUtil.US_ASCII));
    sendHttpResponse(ctx, req, res);
}

Nodejs:

var http = require('http');
http.createServer(function(req, res){
    res.end('Hello, World');
}).listen(8080);

Nodejs: 4CPU

var cluster = require('cluster')
  , http = require('http');
var server = 
  http.createServer(function(req, res) {
    res.end('Hello World');
});
cluster(server)
.set('workers', 4)
.use(cluster.stats())
.listen(8080);

测试环境

CPU:

$ cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c

8 Intel(R) Xeon(R) CPU E5410 @ 2.33GHz

内存:16GB

操作系统:

$ cat /etc/issue | grep Linux

Red Hat Enterprise Linux Server release 5.4 (Tikanga)

测试脚本

$ ab -c 30 -n 1000000 http://127.0.0.1:8080/

测试结果对比

Name 30 threads rps 100 rps 1000 rps 5000 rps
Nodejs 7,287 7,038 6,786 6,912
Tornado 2,967 2,966 2,967 2,982
Go 5,214 5,157 5,080 5,164
Netty 13,526 13,351 11,332 7,921
Nodejs 4P 14,826 14,332 12,161 8,287

** Nodejs性能很不错! **

String
=> Buffer =>
Stream

Javascript数据处理的局限性

Buffer

Buffer

// buffer.js
var a = new Buffer(10);
console.log(a, a.toString());
var b = new Buffer('QCon2011杭州');
console.log(b, b.toString());
$ node buffer.js
<Buffer 05 08 4e 00 2f 0f 26 05 04 4e> '\u0005\bN\u0000/\u000f&\u0005\u0004N'
<Buffer 51 43 6f 6e 32 30 31 31 e6 9d ad e5 b7 9e> 'QCon2011杭州'

fs, net, http(s)

fs, net, http(s)

fs的同步方法

var fs = require('fs');
// $ touch /tmp/helloSync
fs.renameSync('/tmp/helloSync', '/tmp/worldSync');
var stats = 
    fs.statSync('/tmp/worldSync');
console.log('statsSync:' 
    + JSON.stringify(stats));

fs, net, http(s)

fs的异步方法

var fs = require('fs');
// $ touch /tmp/hello
fs.rename('/tmp/hello', '/tmp/world', function (err) {
  if (err) throw err;
  
  fs.stat('/tmp/world', 
    function (err, stats) {
      if (err) throw err;
      console.log('stats: ' 
          + JSON.stringify(stats));
    });
});

fs, net, http(s)

最简单的telnet聊天室(1)

// chat.js
var net = require('net');
var clients = [];
net.createServer(function(client) {
    client.write('Enter your name:\n');
    client.once('data', function(data) {
        var username = data.toString().trim();
        clients.push(client);
        broacast(username + ' : Join!\n');
        client.on('data', function(data) {
            var text = username + ' : ' + data;
            broacast(text);
        });
    });
}).listen(11021);

fs, net, http(s)

最简单的telnet聊天室(2)

// 单进程的优势。。。
function broacast(text) {
    console.log(text.trim());
    var i = 0, l = clients.length;
    for(; i < l; i++) {
        var c = clients[i];
        c.write(text);
    }
};

fs, net, http(s)

最简单的telnet聊天室(3)

Server: $ node chat.js

mk2 : Join!
mk2 : Hello qcon2011 hangzhou!

Client: $ telnet 192.168.1.xxx 11021

Enter your name:
mk2
mk2 : Join!
Hello qcon2011 hangzhou!
mk2 : Hello qcon2011 hangzhou!

fs, net, http(s)

http server

var http = require('http');
http.createServer(function(req, res) {
    if(req.url === '/') {
        res.end('Hello world');
    } else {
        res.end('HTTP ' + req.method 
            + ' : ' + req.url);
    }
}).listen(8080);

fs, net, http(s)

http client

var http = require('http');
var options = {
  host: 'www.google.com',
  port: 80,
  path: '/'
};
http.get(options, function(res) {
  console.log("Got response: " 
    + res.statusCode);
  res.on('data', function(data) {
    console.log(data.toString());
  });
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});

Stream: 流的方式

上传文件:fs.ReadStream

创建只读文件流

var readstream = 
    fs.createReadStream(uploadfile);

通过监听文件的data事件,获取数据,就像它自己会吐数据出来一样 而不用自己去调用read方法,一点一点地去取数据

上传文件:fs.ReadStream

readstream.on('data', function(chunk) {
    console.log('write', chunk.length);
    // 向服务器发送数据
    req.write(chunk);
});

通过end事件可以判断文件数据是否全部读取完了

readstream.on('end', function() {
    req.end();
});

Pipe:将水管连接起来

嫌既监听data又要监听end事件很麻烦?那就试试pipe吧,简直像安装水管那么简单。

直接使用pipe,想象两端水管,我们只需将他们按照水流方向连接起来即可(吐数据 ==> 收数据) 当数据读取完,会自动触发req.end()

readstream.pipe(req);

你没眼花,就是一行代码这么简单,所有数据就会自动发出去了。

通过readStream读取大文件并发送到网络中去:upload_file.js

Pipe:将水管连接起来

:) 呵呵,原来
程序员
也是
水电工

下载文件:fs.WriteStream

创建只写文件流

var writestream = 
    fs.createWriteStream(savefile);

继续做水电工,安装水管,还是以水流的方向安装
(吐数据 ==> 收数据) 这次网络流变成吐数据,文件流变成收数据

res.pipe(writestream);

下载文件:fs.WriteStream

文件句柄已关闭,回调结果

writestream.on('close', function() {
    callback(null, res);
});

通过WriteStream接收网络中得到的数据:download_file.js

程序员水电工:Pipe

脱离了浏览器的Javascript

Nodejs能带来更多什么魔法呢?

服务端程序

特别适合各类 网络IO密集型 应用

常规Web应用

淘job: 基于微博的企业化招聘网站

实时性强的网络应用

Trello: 团队协作工具,
它的3个核心原则:实时更新,设备独立性,和最小摩擦。

实时性强的网络应用

实时性强的网络应用

PaintChat: 可以协作绘图的聊天室。

实时性强的网络应用

实时性强的网络应用

Instagram Real-time API Demo: Instagram 官方给出的一个实时应用,服务器实时推送最新上传的照片到浏览器上显示。 基于Socket.io模块实现的。

实时性强的网络应用

Cloud9: Web IDE,随时随地写代码。

网络中间层服务: Nodefox

Nodefox: 一个数据处理中间件,负责从一个MySQL集群中提取数据,计算,并输出统计结果。

网络中间层服务: Nodefox

Nodefox的查询过程

网络中间层服务: Loggly

Loggly: 收集syslog和HTTP日志的Web服务。

网络中间层服务

NAE Proxy: Node App Engine的Http proxy, 将应用的端口映射到本地socket。

桌面程序: TermKit

TermKit: next gen terminal / command application, built out of WebKit and Node.js.

桌面程序: TermKit

桌面程序: node-gui

@小型笨蛋 在尝试使用
nodejs驱动GTK+ demo

第三方模块

Nodejs官方收集的第三方模块页面上, 共列出了
1152
个模块。

第三方模块

涵盖了
Web,Database,Templating,CSS Engines,CMS,Build and Deployment,Openssl, Hashing,SMTP, TCP/IP,RPC,Web Sockets & Ajax,Message Queues,Testing,JSON,XML,Debugging,Compression, Graphics,Payment Gateways,API clients,Control flow / Async goodies,I18n and L10n modules 等等, 几乎涉及了网络开发中需要的功能模块。

模块管理工具: npm

http://npmjs.org/

模块管理工具: npm

在npm上目前管理着
4427+
个nodejs模块, 只需要简单一行命令,即可安装你需要得模块,例如我想使用nodejs访问mysql数据库:

$ npm install mysql

常用模块(1)

常用模块(2)

Node's goal

Provide an easy way to build scalable network programs.

提供一种便捷
的方式构建网络程序。

Q & A
知乎者也

next("Thanks ^_^")

/

#