阻塞 线程在执行中如果遇到磁盘读写或网络通信(统称为I/O操作)通常要消耗很长时间 这时操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他工作线程 异步I/O 非阻塞IO 针对所有的IO操作不采用阻塞的策略,当线程遇到IO操作时,不会以阻塞的方式等待IO操作完成或数据的返回,而只是将IO请求发送给操作系统,继续执行下一条语句。当操作系统完成IO操作时,以事件的形式通知执行IO操作的线程,线程会在特定时候处理这个事件。 为了处理异步IO 线程必须有事件循环 不断地检查有咩有未处理的事件 依次处理 单线程事件驱动的异步式I/O比较传统的多线程阻塞式I/O优势? 异步式I/O就是少了多线程的开销 对于操作系统来说,创建线程的代价十分昂贵 需要分配内存 列入调度 同时在线程切换时还要执行内存换页 CPU的缓存被清空 切换回来还要从中心从内存中读信息 破坏了数据的局部性 同步式I/O(阻塞式) 异步I/O(非阻塞式) 利用多线程提供吞吐量 单线程即可实现高吞吐量 通过事件片段分割和线程调度利用多核CPU 通过功能的划分利用多核CPU 需要由操作系统调度多线程受用多核CPU 可以将单进程绑定到单核CPU 难以充分利用CPU资源 可以充分利用CPU资源 内存轨迹大 数据局部性弱 内存轨迹小 数据局部性强 符合线性的编程思维 不符合传统编程思维 fs.readFile("fileName", "编码",回调函数) //fileName不能是中文 var data = fs.readFileSync("fileName", "编码") 以上两种分别为异步读文件 和 同步读文件 Node.js的事件循环机制(有疑问 具体流程还是不了解) Node.js在什么时候会进入事件循环呢? Node.js程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以Node.js一直处在事件循环中,程序入口就是事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会发出I/O请求或直接发射(emit)事件,执行完毕后再返回事件循环,事件循环会检查事件队列中未处理的事件,直到程序结束 模块 创建模块 一个文件就是一个模块 问题点如何在其他文件中获取这个模块 node exports 和 require两个对象 exports 模块公开的接口 require 用于从外部获取一个模块的接口 即所获取模块的exports对象 require加载模块 只加载一次 都是同一个实例 类似于 单例模式 警告: 不可以通过对exports直接赋值代替对module.exports赋值 exports实际上只是一个和module.exports指向同一个对象的变量 exports本身会在模块执行结束后释放 但是module不会,因此只能通过指定的module.exports来访问接口 exports = Hello; 报错 Hello 不是一个函数 写成 modules.exports = Hello;-->var Hello = require("./Hello") exports.hello = Hello; ---> var Hello = require("./Hello").Hello; 创建包 创建包 需要符合CommonJS规范 CommonJS规范如下 package.json 必须在包的顶层目录下; 二进制文件应该在 bin 目录下; JavaScript 代码应该在 lib 目录下; 文档应该在 doc 目录下; 单元测试应该在 test 目录下。 node getpackage.js 控制台打印出Hello Node.js在调用某个包时,会首先检查包中package.json文件的main字段,将其作为包的接口模块 未配置package.json 时 会尝试寻找index.js或index.node为入口文件 我们使用这种方法可以把文件夹封装为一个模块,即所谓的包。包通常是一些模块的集合,在模块的基础上提供了更高层的抽象, 相当于提供了一些固定接口的函数库 通过定制package.json 我们可以创建更复杂的 规范 完善的包用语发布 package.json是用来描述CommonJS规范的,完全符合package.json文件应该含有一下字段 name 包的名称 必须是唯一的 由小写英文字母 数字 下划线组成 不能包含空格 description 包的简要说明 version 符合语义化版本识别规范的版本字符串 keywords 关键字数组 通常用于搜索 maintainers 维护者数组 每个元素包含 name email(可选) web(可选) contributors 贡献者数组 格式同上 包的作者应该是贡献者数组的第一个元素 bugs 提交bug的地址 可以是网址或者电邮 licenses 许可证数组 每个元素要包含type(许可证的名称)和url(链接到许可证文本的地址)字段 repositories 仓库托管地址数组 每个元素要包含type(仓库的类型 如git) url(仓库地址) path(相对于仓库的路径 可选) 字段 dependencies 包的依赖 一个关联数组 由包的名称和版本号组成 example { "name": "mypackage", "description": "Sample package for CommonJS. This package demonstrates the required elements of a CommonJS package.", "version": "0.7.0", "keywords": [ "package", "example" ], "maintainers": [ { "name": "Bill Smith", "email": "bills@example.com", } ], "contributors": [ { "name": "BYVoid", "web": "http://www.byvoid.com/" } ], "bugs": { "mail": "dev@example.com", "web": "http://www.example.com/bugs" }, "licenses": [ { "type": "GPLv2", "url": "http://www.example.org/licenses/gpl.html" } ], "repositories": [ { "type": "git", "url": "http://github.com/BYVoid/mypackage.git" } ], "dependencies": { "webkit": "1.2", "ssl": { "gnutls": ["1.0", "2.0"], "openssl": "0.9.8" } } } Node.js的require在加载模块时会尝试搜索 node_modules 子目录 因此使用npm本地安装的包可以直接引用 使用npm全局安装的包(example supervisor) 主要是为了在环境变量中注册supervisor 可以在命令行中使用 npm本地安装仅仅 把包安装到node_modules子目录下 其中的bin目录没有包含在PATH环境变量中 不能直接在命令中调用 使用全局模式安装 npm会将包安装到系统目录 譬如 /usr/local/lib/node_modules 同时package.json文件中的bin字段包含的文件会被链接到/usr/local/bin /usr/local/bin是在PATH环境变量中默认定义的 因此可以直接在命令行中使用 提示:使用全局安装的包并不能直接在js文件中用require获得 因为require不会搜索/usr/local/node_modules/ npm link npm link express 可以在node_modules子目录中发现一个指向安装全局包的符号链接 通过这种方法可以使全局包当本地宝使用(不支持window) npm link 可以将本地的包链接到全局 使用方法:在包目录(package.json所在目录)中运行npm link 命令 发布包 发布之前需要获得一个账号用于维护自己的包 使用npm adduser 根据提示输入用户名 密码 邮箱 npm whoami 检测帐号是否存在 在package.json所在目录使用npm publish 发布 npm unpublish取消发布 如果包有更新 package.json文件中修改version即可 ?????????????疑惑????????????? 1: exports本身会在模块执行结束后释放( 被释放exports.Hello = Hello怎么还会被访问到呢???? ) 解答 Node不允许重写exports exports = Hello; 如果创建的返回 既有exports.hello 又有 module.exports = 则以module为准 上面的问题可以理解为 导出的究竟是什么 最终程序导出的事module.exports; exports只是对module.exports的一个全局引用 最初被定义为一个可以添加属性的空对象 所以exports.Hello 只是module.exports.Hello 的简写 如果把exports定义成别的 就打破了module.exports 和 exports之间的引用关系 感觉像是A关联B B关联A 最后倒出A 但是B指向了C 与A断开了联系 导出A的时候A找不到B 所以报错 2: 语义化版本识别 3: Node.js调试 还未接触