问题
1.什么是Node.js,有哪些特征?于其他服务器端对比?有哪些缺点?
简单来说Node.js是运行在服务端的JavaScript。是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行JavaScript的速度非常快,性能非常好。用于搭建高性能的web服务器。
特征:
- 它是一个JavaScript运行环境
- 依赖于Chrome V8引擎进行代码解释
- 事件驱动
- 非阻塞I/O
- 轻量,可伸缩,适于实时数据交互应用
- 单进程,单线程
对比:
- node无法直接渲染静态页面,提供静态服务
- node没有根目录的概念
- node必须通过路由程序指定文件才能渲染文件
- node比其他服务端性能更好,速度更快
缺点:
-
不适合CPU密集型应用:由于单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起
解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起。
-
只支持单核CPU,不能充分利用CPU
-
可靠性低,一旦代码某个环节崩溃,整个系统都崩溃。
原因:单进程,单线程。
解决方案:Nginx反向代理,负载均衡,开多个进程,绑定多个端口。开多个进程监听同一个端口,使用cluster模块。
-
开源组件库质量参差不齐,更新快,向下不兼容
-
Debug不方便,错误没有stack trace
2.介绍一下Node事件循环Event Loop的流程
Node.js.是单线程应用程序,通过V8引擎提供的异步执行回调接口,可以处理大量的并发,性能非常高。几乎每一个API都支持回调函数。
- 在进程启动时,Node便会创建一个类似while(true)的循环,每执行一次循环体的过程我们称为Tick。
- 每个Tick的过程就是查看是否有事件待处理。如果有就取出事件及相关回调函数。然后进入下一个循环,如果不再有事件处理,就退出循环。
3.什么是事件驱动模型(非阻塞I/O)?
当服务端接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。当第一个请求处理完毕后,就放回处理队列,当到达队列开头,就返回结果给用户。
好处:高效,扩展性强,因为服务端一直接受请求而不等待任何读写操作。(也称之为非阻塞I/O或者事件驱动I/O)
4.如何判断当前脚本运行在浏览器还是node环境中?
通过判断global对象是否为window,如果不是,则没有运行在浏览器中。
5.Node.js能解决什么问题?
对于PHP,JAVA,Python等服务端语言来说,为每个客户端连接都需要创建一个新的线程,而每个线程需要大约2M的内存,理论上,一台具有8G内存的服务器可以同时连接的最大用户数为4000个左右,要让web应用程序支持更多的用户,就需要增加服务器的数量,这样就增加了硬件的成本。服务器所能支持的同时连接最大用户量是一个主要的瓶颈。
6.Node.js的两种机制
- 非阻塞型I/O
- 事件循环
Node入门
模块化
引用,定义,标识。
模块定义
在 Node 中,一个js文件就是一个模块。定义一个模块就是创建一个js。 在 Node 中,每一个js文件中的js代码都是独立运行在一个函数中。而不是全局作用于,所以一个模块中的变量和函数在其他模块中无法访问。我们可以通过exports来向外部暴露变量和方法,只需要将需要暴露给外部的变量或方法设置为exports的属性即可。
exports.x = "我是x"
exports.y = "我是y"
exports.fn = function() {
}
模块引用
通过 require() 函数来引入外部的模块。如果使用相对路径,必须以 . 或 .. 开头。使用 require() 引入模块后,该函数会返回一个对象,这个对象代表的是引入的模块。
模块标识
我们使用 require() 引入外部模块时,使用的就是模块标识,我们可以通过模块标识来找到我们指定的模块。
模块分为两大类:
- 核心模块:由 node 引擎提供的模块,核心模块的标识就是模块的名字。
var fs = require("fs");
- 文件模块:由用户自己创建的模块。文件模块的标识就是文件的路径(绝对,相对)。相对路径使用 ./ 或 ../ 开头
var md = require("./md.js");
声明全局标量
在node中有一个全局对象 global,它的作用和网页中的window类似。
在全局中创建的变量都会作为global的属性保存。
在全局中创建的函数都会作为global的方法保存。
在函数中定义一个全局变量:
a = 10;
console.log(global.a);//10
var a = 10;//依然是局部标量
console.log(global.a);//undefined
番外
arguments.callee:这个属性保存的是当前执行的函数对象
console.log(arguments.callee)//[Function]
console.log(arguments.callee+"")//拼串会调用 toString,把函数结果打印出来
实际上模块中的代码都是包装在一个函数中执行的,并且在函数执行时,同时传递了5个实参。
//当 node 在执行模块中的代码时,它会首先在代码的最顶部,添加如下代码:
function (exports, require, module, _filename, _dirname){
//在代码的最底部,添加如下代码
}
- exports:该对象用来将变量或函数暴露到外部
- require:是函数,用来引入外部的模块
- module:module代表的是当前模块本身,exports就是module的属性。也可用module.exports导出。
module.exports == exports - _filename:当前模块的完整路径
- _dirname:当前模块所在文件夹的完整路径
exports和module.exports区别
- 通过exports只能使用 . 的方式来向外暴露内部变量。
- 而module.exports既可以通过 . 的形式,也可用直接赋值。module.exports={}
Buffer(缓冲区):存储二进制数据
Buffer的结构和数组很想,操作的方法也和数组类似。
数组中不能存储二进制的文件,而Buffer就是专门用来存储二进制数据。
使用buffer不需要引入模块,可直接使用。
在buffer中存储的都是二进制数据,但是在显示时都是以16进制的形式。
buffer中的每一个元素的范围是从 00 - ff(十进制的 0 - 255)(二进制的 00000000 - 11111111),占内存的8位也就是一个字节。
Buffer的大小一旦确定,则不能修改。uBffer实际上是对底层内存的直接操作。
计算机中 一个 0 或一个 1 我们称为1位(bit)
8bit = 1byte(字节)
1024byte = 1kb
1024kb = 1mb
- 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数。
- 实际上一个元素就表示内存中的一个字节。
- 实际上Buffer中的内存不是通过JS分配的,而是在底层通过C++申请的。
- 也就是我们可以直接通过Buffer来创建内存中的空间。
将一个字符串转换成buffer:Buffer.from(str)
var str = "hello";
//将一个字符串保存在buffer中
var buf = Buffer.from(str);
console.log(buf); //<Buffer 68 65 6c 6c 6f>
var cstr = "你好"
console.log(buf.length); // 6 占用内存的大小 一个汉字占用3个字节
console.log(str.length); // 2 字符串的长度
创建一个指定大小的buffer:Buffer.alloc(size) 分配空间并清空数据
var buf2 = Buffer.alloc(5) //创建一个5字节的buffer
//通过索引来操作buf中的元素
buf2[0] = 88; // <Buffer 58 00 00 00 00> 58是88的十六进制
buf2[1] = 0xaa; // <Buffer 58 aa 00 00 00>
//只要数字在控制台或页面输出,一定是10进制
console.log(buf2[1]) // 170
//转换成16进制字符串
console.log(buf2[1].toString(16)) // aa
// 遍历和数组操作一样,输出也是10进制
for(var i = 0; i < buf2.length; i++){
console.log(buf2[i])
}
创建一个指定大小的buffer,但Buffer中可能含有敏感数据:Buffer.allocUnsafe(size)
var buf2 = Buffer.alloc(5) //分配空间并清空数据
console.log(buf2) //<Buffer 00 00 00 00 00>
var buf3 = Buffer.allocUnsafe(5) // 分配空间,但不清空数据
console.log(buf3) //<Buffer 21 00 ac 00 00>
将缓冲区中的数据转换为字符串:buf.toString()
var buf4 = Buffer.from("我爱你呀") // 将一个字符串转换成buffe
console.log(buf4.toString()) // 我爱你呀