Node.js在淘宝的应用实践

27
Node 在在在在在在在在 这这这 这这这 这这这这这 ,一 Node.js By @ 这这 1

Transcript of Node.js在淘宝的应用实践

Page 1: Node.js在淘宝的应用实践

Node在淘宝的应用实践这些年,我们一起开发过的 Node.jsBy @ 朴灵

1

Page 2: Node.js在淘宝的应用实践

关于我

•CNode 社区

•前端 at SAP for Mobile Web

•前端 at 淘宝数据产品部

连连 IE6IE6 都都能兼容的能兼容的

男人男人

2

Page 3: Node.js在淘宝的应用实践

议程•我为什么要做 Node 开发

•准备工作与作品

•Node.js 带来的新问题与如何逆袭• 异步编程

• 缓存与内存

• Buffer

•Node.js 在淘宝产品中的一点实践

3

Page 4: Node.js在淘宝的应用实践

长达半天的欢乐

4

前端屌丝的坎坷路

icons powered by morcha design

Page 5: Node.js在淘宝的应用实践

Node 与前端的亲缘

5

Page 6: Node.js在淘宝的应用实践

Node 与前端的亲缘

6

Page 7: Node.js在淘宝的应用实践

左手 HTML5 右手Node.js

•熟知的 JavaScript 执行原理 / 事件循环

•熟悉的 API 、事件、单线程、回调

•Ajax/ 异步

•相比 HTML5 , Node 将开启更多的可能性

7

Page 8: Node.js在淘宝的应用实践

好奇心 & 满足感•HTTP 协议栈:深入后端,反哺前端• Status code

• Cookie & Session

• Request & Response

• Web Framework

•高性能 JavaScript 平台

•拓宽视野

8

Page 9: Node.js在淘宝的应用实践

Go, go, go!!!

9

var http = require('http');http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(1337, '127.0.0.1');console.log('Server running at http://127.0.0.1:1337/');

var http = require('http');http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(1337, '127.0.0.1');console.log('Server running at http://127.0.0.1:1337/');

% node example.jsServer running at http://127.0.0.1:1337/% node example.jsServer running at http://127.0.0.1:1337/

Page 10: Node.js在淘宝的应用实践

作品

10

Page 11: Node.js在淘宝的应用实践

全 JavaScript 堆栈的产品

WebGhost

Should

WebGhost

MongoSkin

ITier

Redis & MRedis

Connect

11

Page 12: Node.js在淘宝的应用实践

前端工程师到 Web 工程师

12

结果重了 10 斤

如何摆脱前端屌丝的身份让女神青睐

Page 13: Node.js在淘宝的应用实践

$.get("template", function (template) { // something $.get("data", function (data) { // something $.get("l10n", function (l10n) { // something render(template, data); }); });});

问题:异步协作•嵌套还是并行?

13

var proxy = new EventProxy();proxy.all("template", "data", "l10n", render);$.get("template", function (template) { // something proxy.trigger("template", template);});$.get("data", function (data) { // something proxy.trigger("data", data);});$.get("l10n", function (l10n) { // something proxy.trigger("l10n", l10n);});

var proxy = new EventProxy();proxy.all("template", "data", "l10n", render);$.get("template", function (template) { // something proxy.trigger("template", template);});$.get("data", function (data) { // something proxy.trigger("data", data);});$.get("l10n", function (l10n) { // something proxy.trigger("l10n", l10n);});

Page 14: Node.js在淘宝的应用实践

问题:异步还是同步•复杂的异步编程

var proxy = new EventProxy();var status = "ready";var _getFile = function (callback) { proxy.once("template", callback); if (status === "ready") { fs.readFile("views/index.html", function (err, file) { status = "pending"; proxy.fire("template", err, file); }); }};

var _template;var getTemplate = function (callback) { if (_template) { callback(null, _template); } else { _getFile(function (err, file) { if (!err && !_template) { _template = file.toString(); } callback(null, _template); }); }};

var proxy = new EventProxy();var status = "ready";var _getFile = function (callback) { proxy.once("template", callback); if (status === "ready") { fs.readFile("views/index.html", function (err, file) { status = "pending"; proxy.fire("template", err, file); }); }};

var _template;var getTemplate = function (callback) { if (_template) { callback(null, _template); } else { _getFile(function (err, file) { if (!err && !_template) { _template = file.toString(); } callback(null, _template); }); }};

同步 + 缓存,妥妥滴

var view = fs.readFileSync("../views/index.html", "utf8");var view = fs.readFileSync("../views/index.html", "utf8");

14

Page 15: Node.js在淘宝的应用实践

问题:缓存的使用var map = {};var get = function (key) { return map[key];};var set = function (key, value) { map[key] = value;};// 检查缓存if (!get(key)) { // 从数据库或别的地方获取了对象后,放进缓存中 set(key, value);}

var map = {};var get = function (key) { return map[key];};var set = function (key, value) { map[key] = value;};// 检查缓存if (!get(key)) { // 从数据库或别的地方获取了对象后,放进缓存中 set(key, value);}

var LimitableMap = require('limitablemap');var map = new LimitableMap(1000);map.set("key1", "key1");map.get("key1");

var LimitableMap = require('limitablemap');var map = new LimitableMap(1000);map.set("key1", "key1");map.get("key1");

15

Page 16: Node.js在淘宝的应用实践

问题: Session

•V8 内存堆栈限制

•分布式中, Session 需要共享(Redis)

•重启应用不丢失 session

•多点 Redis ,备份容灾

16

Page 17: Node.js在淘宝的应用实践

问题: Buffer 对象var data = ""; res.on('data', function (chunk) { // chunk 是一个 Buffer 对象 data += chunk;// 隐藏的 toString ()}) .on("end", function () { // 对 data 转码 });

var data = ""; res.on('data', function (chunk) { // chunk 是一个 Buffer 对象 data += chunk;// 隐藏的 toString ()}) .on("end", function () { // 对 data 转码 });

17

// 正确的方法var chunks = []; var size = 0; res.on('data', function (chunk) { chunks.push(chunk); size += chunk.length; }); res.on('end', function () { var data = null; switch(chunks.length) { case 0: data = new Buffer(0); break; case 1: data = chunks[0]; break; default: data = new Buffer(size); for (var i = 0, pos = 0, l = chunks.length; i < l; i++) { var chunk = chunks[i]; chunk.copy(data, pos); pos += chunk.length; } break; } });

// 正确的方法var chunks = []; var size = 0; res.on('data', function (chunk) { chunks.push(chunk); size += chunk.length; }); res.on('end', function () { var data = null; switch(chunks.length) { case 0: data = new Buffer(0); break; case 1: data = chunks[0]; break; default: data = new Buffer(size); for (var i = 0, pos = 0, l = chunks.length; i < l; i++) { var chunk = chunks[i]; chunk.copy(data, pos); pos += chunk.length; } break; } });

// 简单且正确的方法var bufferHelper = new BufferHelper();req.on("data", function (chunk) { bufferHelper.concat(chunk);}).on('end', function () { var html = bufferHelper.toBuffer().toString();});

// 简单且正确的方法var bufferHelper = new BufferHelper();req.on("data", function (chunk) { bufferHelper.concat(chunk);}).on('end', function () { var html = bufferHelper.toBuffer().toString();});

Page 18: Node.js在淘宝的应用实践

问题: String 传输的性能

•7k 大小的静态文件,需做替换

•String ➛ Buffer

•缓存 Buffer , 4 倍性能提升

18

Page 19: Node.js在淘宝的应用实践

问题:多核 CPU 的利用•单线程与多核 CPU

•单线程因为异常退出?

•仿若熟悉的 Web Worker: child_process

•进程与消息

var cluster = require('node-cluster');

var master = new cluster.Master();master.register(8080, 'app.js');master.dispatch();

var cluster = require('node-cluster');

var master = new cluster.Master();master.register(8080, 'app.js');master.dispatch();

var http = require('http');var cluster = require('node-cluster');

var worker = new cluster.Worker();var server = http.createServer(function (req, res) { // server});

worker.ready(function (socket) { server.emit('connection', socket);});

var http = require('http');var cluster = require('node-cluster');

var worker = new cluster.Worker();var server = http.createServer(function (req, res) { // server});

worker.ready(function (socket) { server.emit('connection', socket);}); 1

9

负载均衡多核利用提升稳定

Page 20: Node.js在淘宝的应用实践

小结

•异步编程问题?EventProxy 、 JScex 等

•内存限制问题?第三方存储 Redis

•CPU 消耗问题?缓存中间结果

•单线程 CPU 利用不足问题?多进程

•单线程稳定性问题? Node-Cluster

20

Page 21: Node.js在淘宝的应用实践

实践:运维•异常

•日志

•监控

•部署

•备份容灾

21

// 异步方法中 try catch 是不靠谱滴// 异步方法的异常async(function (err, data) { if (err) { logger.error(err); return; // TODO } // TODO});

// 异步方法中 try catch 是不靠谱滴// 异步方法的异常async(function (err, data) { if (err) { logger.error(err); return; // TODO } // TODO});

进程数量CPU内存Load磁盘 IO流量

进程数量CPU内存Load磁盘 IO流量

双机房双 Redis MRedis 模块双 MongoDB MongoSkin数据源集群

双机房双 Redis MRedis 模块双 MongoDB MongoSkin数据源集群

Page 22: Node.js在淘宝的应用实践

实践:测试•测试• 单元测试

•自动化测试

• 性能测试

•持续集成 WebGhost

Should.js

22

Page 23: Node.js在淘宝的应用实践

实践: CommonJS & Node & NPM

CommonJCommonJSS

NodeNode

NPMNPM

23

Page 24: Node.js在淘宝的应用实践

实践:公司范围内共享代码

•如何保护隐私代码

•如何重用散乱代码

•如何告别复制粘贴

24

Page 25: Node.js在淘宝的应用实践

实践:公司范围内共享代码

25

本地本地 NPMNPM 官方官方 NPMNPM

项目项目

单向同步

私有模块私有模块 公有模块公有模块

Page 26: Node.js在淘宝的应用实践

展望•深度发掘前端开发和用户体验

•无需与开发沟通,节省成本

•知晓细节,更易改进产品体验

•感谢伟大的 github

•感谢伟大的 NPM促成的生态圈

•感谢 Node 这件美妙的礼物26

Page 27: Node.js在淘宝的应用实践

Q&A

屌丝のぎゃくしゅ屌丝のぎゃくしゅうう

27