一、前端架构四种方式
这里我们以vue为例讨论:
- vue打包dist文件后交给后端处理
- swig + node
- vue + node
- vue + node(真假路由形式,通过设置http头)
二、node用途
- node可以削减api
- node ssr服务端渲染
- node更好的实现前后端分离,使前端项目可以独立上线
- 适用与异步IO,大吞吐量,聊天等
三、node对异步IO的实现
我们先来看一张经典的图:
image.png
image.png
中间部分是操作系统平台的绑定,在每个平台不一样,所以node在编译时会先判定下系统,来看右边,我们知道,node是单线程的,这里LIBUV辅助node进行EVENT LOOP,EVENT QUEUE会将异步操作先读取过来,然后事件环将操作送到右边处理,所以node可以大批量吞吐异步操作,如果EVENT QUEUE存放满了,这时就不会进入到EVENT LOOP了。
这里来扩展一下,我们平常说的CPU满了,实际上是并发数量太多,服务器处理不过来。而内存满了,是因为变量太多,或者变量泄漏。
cpu密集:压缩,解压,加密,解密
i/o密集:文件操作,网络操作,数据库读写
四、几个特殊的API
setTimeout(function () {
console.log(1);
}, 0);
setImmediate(function () {
console.log(2);
});
process.nextTick(() => {
console.log(3);
});
new Promise((resovle,reject)=>{
console.log(4);
resovle(4);
}).then(function(){
console.log(5);
});
console.log(6);
我们来看下输出结果:4 6 3 5 1 2
这时因为 同步 > tick > microtask > macrotask
然后我们顺便实现一个sleep方法,如下:
async function test() {
console.log('Hello')
await sleep(1000)
console.log('world!')
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
test()
五、函数式编程在Node中的应用
高阶函数:可以将函数作为输入或者返回值,形成一种 后续传递风格的结果接受方式,而非单一的返回值形式。 后续传递风格的程序将函数业务重点从返回值传递到回调 函数中。
app.use(function(){//todo})。
var emitter = new events.EventEmitter;
emitter.on(function(){//..........todo})
匿名函数: 我们可以把一个函数作为变量传递。
// 匿名函数
var http = require("http");
http.createServer(function(request, response) {
response.whiteHead(200, {"COntent-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
// 先定义后传递
var http = require("http");
function.onRequest(request, response) {
response.whiteHead(200, {"COntent-Type": "text/plain"});
response.write("Hello World");
response.end();
}
http.createServer(onRequest).listen(8888);
六、 V8的垃圾回收机制
Node使用JavaScript在服务端操作大内存对象受到了一定 的限制,64位系统下约为1.4GB,32位操作系统下是 0.7G.
V8的垃圾回收策略主要基于分代式垃圾回收机制。在自 动垃圾回收的演变过程中,人们发现没有一种垃圾回收 算法能够胜任所有场景。V8中内存分为新生代和老生代 两代。新生代为存活时间较短对象,老生代中为存活时 间较长的对象。
在分代基础上,新生代的对象主要通过Scavenge算法进行垃圾回收,再具体实现时主要采用Cheney算法。Cheney算法是一种采用复制的方式实现的垃圾回收算法。它将内存一分为二,每一个空间称为semispace。这两个semispace 中一个处于使用,一个处于闲置。处于使用的称之为 From,闲置的称之为To.分配对象时先分配到From,当开始进行垃圾回收时,检查From存活对象赋值到To,非存活被释放。然后互换位置。再次进行回收,发现被回收过直接晋升,或者发现To空间已经使用了超过25%。他的缺点是只能使用堆内存的一半,这是一个典型的空间换时间的办法,但是新生代声明周期较短,恰恰就适合这个算法。
image.png
我们来简单解释下上图,这里会将内存一分为二,然后将对象存放到from中,当进行垃圾回收时,将还使用的对象放到to中,不使用的就删掉,然后互换位置,当被回收过或者TO空间超过25时%,就推到老生代中。
V8老生代主要采用Mark-Sweep和Mark-compact,再使用Scavenge不合适。一个是对象较多需要赋值量太大而且还是没能解决空间问题。Mark-Sweep是标记清清除,标记那些死亡的对象,然后清除。但是清除过后出现内存不连续的情况,所以我们要使用Mark- compact,他是基于Mark-Sweep演变而来的,他先将活着的对象移到一边,移动完成后,直接清理边界外的内存。当CPU空间不足的时候会非常的高效。V8 后续还引入了延迟处理,增量处理,并计划引入并 行标记处理。
什么时候会出现内存泄漏呢,常见的有以下四种:
- 无限制增长的数组
- 无限制设置属性和值
- 任何模块内的私有变量和方法均是永驻 内存的 a = null
- 大循环,无GC机会
通过浏览器Memory,我们可以看到GC回收情况,或者通过node-inspector
npm root -g #显示npm全局安装的目录
node --inspect-brk demo.js #断点调试
sudo npm install supervisor -g #node自动更新
浏览器中打开 chrome://inspect
七、node中的多线程
Master进程均为主进程,Fork可以创造主从进程。
通过child_process可以和NET模块组合,可以创建多个线程并监 听统一端口。通过句柄传递完成自动重启、发射自杀信号、 限量重启、负载均衡。
Node默认的机制是采用操作系统的抢占式策略。闲着的进程争 抢任务,但是会造成CPU闲置的IO暂时并未闲置。Node后来引 入了Round-Robin机制,也叫轮叫调度。主进程接受任务,在发给每个子进程做好自己的事,然后通过进程间通信来将他们连 接起来。这符合Unix的设计理念,每个进程只做一件事,并做好。将复杂分解为简单,将简单组合成强大。
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
require('os').cpus().forEach(function(){
cluster.fork();
});
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
cluster.on('listening', function(worker, address) {
console.log("A worker with #"+worker.id+" is now connected to " + address.address + ":" + address.port);
});
} else {
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
console.log('Worker #' + cluster.worker.id + ' make a response');
}).listen(8000);
}
八、获取请求头
var forPound = req.headers['x-forwarded-for-pound'];
九、node架构
这部分会在node部署模块详细介绍
网友评论