Nodejs in action 总结(一)

node基本概念

什么是node?

node源于js的流行,因而出现性能极强的V8。Node是JavaScript程序的平台,不要把它跟框架相混淆。很多人都误把Node当做JavaScript 上的Rails或Django,实际上它更底层。

使用node开发服务端的几个好处

  • 减少客户端服务端的语言切换,实现代码共享
  • JSON是JavaScript原生支持格式
  • 有些NoSQL数据库(MongoDB和CouchDB)用的就是js
  • js是一门编译目标语言,很多语言可以编译成js
  • Node用的虚拟机(V8)紧跟ECMAScript标准
  • Node非常适合数据密集实时型系统(data-intensive real-time)

Node的异步IO原理图

image

基本模块

除了实现宿主环境(浏览器)中的一些常用对象,Node还有一组用来处理多种网络和文件I/O的核心模块。 其中包括用于HTTP、 TLS、 HTTPS、文件系统(POSIX) 、数据报(UDP)和NET(TCP)的模块。这些核心模块刻意做得很小、底层并且简单,只包含要给基于I/O的程序用的组成部分。第三方模块基于这些核心模块,针对常见的问题进行了更高层的抽象。

Node编程基础

Node新手两大基本问题:

  • 如何组织代码
  • 如何异步逻辑顺序执行

如何组织代码

Node模块采用CommonJS 模块规范

路径引用

image

关于require和同步

require是Node中少数几个同步I/O操作之一。因为经常用到模块,并且一般都是在文件顶端引入,所以把require做成同步的有助于保持代码的整洁、有序,还能增强可读性。但在程序中I/O密集的地方尽量不要用require。所有同步调用都会阻塞Node,直到调用完成才能做其他事情。比如你正在运行一个HTTP服务器,如果在每个进入的请求上都用了require,就会遇到性能问题。所以通常都只在程序最初加载时才使用require和其他同步操作。

用node_modules重用模块

Node中有一个独特的模块引入机制, 可以不必知道模块在文件系统中的具体位置。这个机制就是使用node_modules目录。
如果引用模块并未告知模块位置,require(common.js)则采用以下的方法来搜寻这个模块。

是否核心->递归向上查找node_modules目录->环境变量NODE_PATH->抛出异常

注意

  1. 引用本地文件夹指定输出文件


    image
  2. 模块多引用缓存(monkey patching)
    Node能把模块作为对象缓存起来。 如果程序中的两个文件引入了相同的模块, 第一个文件会把模块返回的数据存到程序的内存中, 这样第二个文件就不用再去访问和计算模块的源文件了。实际上第二个引入有机会修改缓存的数据。这种“猴子补丁” (monkey patching)让一个模块可以改变另一个模块的行为,开发人员可以不用创建它的新版本。

异步编程技术

概念

事件会触发响应逻辑,在node世界里,存在两种:回调与事件监听
回调通常用来定义一次性、单个响应的逻辑。事件监听本质上也是回调,不同的是,它跟一个概念实体(事件)相关联,能添加多个响应实体。

一个Node HTTP服务器实例就是一个事件发射器,一个可以继承、能够添加事件发射及处理能力的类(EventEmitter) 。
Node的异步回调惯例:Node中的大多数内置模块在使用回调时都会带两个参数:第一个是用来放可能会发生的错误的,第二个是放结果的。

    var fs = require('fs'); 
    fs.readFile('./titles.json', function(er, data) {
       if (er) throw er;   // do something with data if no error has occurred 
       }); 

异步逻辑的顺序化

让一组异步任务顺序执行的概念被Node社区称为流程控制。这种控制分为两类:串行和并行。

image

串行流程控制
可以使用一堆嵌套回调,但是可读性差。
使用第三放库,如Nimble。
TODO:underscore,backbone
并行流程控制
社区中的很多附加模块都提供了方便好用的流程控制工具。其中比较流行的有Nimble、Step 和Seq三个。

Node Web

image

基本用法:

const http = require('http')
//req,res均为数据流,详情见http模块
const server = http.createServer((req,res)=>{
    res.end('Hello World')
})
server.listen(3000)

构建RESTful Web服务

RESTful Web服务,一个使用HTTP方法谓词提供精简API的服务。

POST(其余暂略)

const http = require('http')
const server = http.createServer((req, res) => {
    // node的http解析器读入并解析请求数据时,会将数据解析成data事件的形式
    // 只要读入了新的数据块,就会触发data事件
    // data事件会提供Buffer对象,这是Node版的字节数组。最好将流编码设定为ascii或utf8;这样data事件会给出字符串
    req.setEncoding('utf8')
    req.on('data',(chunk) => {
    })
    req.on('close',() => {
        res.end()
    })
})

设定Content-Length头
为了提高响应速度,如果可能的话,应该在响应中带着Content-Length域一起发送。设定Content-Length域会隐含禁用Node的块编码,因为要传输的数据更少,所以能提升性能。
res.setHeader('Content-Length,Buffer.byteLength(body))`

Content-Length的值应该是字节长度,不是字符长度,并且如果字符串中有多字节字符,两者的长度是不一样的。为了规避这个问题,Node提供了一个Buffer.byteLength()方法。

用formidable处理上传的文件

提供静态文件服务

通过管道,将req需要的文件,读入输入流,管道到res输出流。

res.setHeader('Content-Lenght',stat.size)
let stream = fs.createReadStream(path)
stream.pipe(res)
stream.on('error',(err)=>{
    sendResult(res, 500,'Internal Server Error');
})

存储Node程序中的数据

选择合适的存储机制取决于以下五个因素:

  • 存储什么数据;
  • 为了保证性能,要有多快的数据读取和写入速度;
  • 有多少数据;
  • 要怎么查询数据;
  • 数据要保存多久,对可靠性有什么要求。

为此,一般有三种选择:

  • 存储数据而无需安装和配置DBMS:文件、内存(并发问题)
  • 用关系型数据库存储数据,具体说就是MySQL和PostgreSQL
  • 用NoSQL数据库存储数据,具体说就是Redis、MongoDB和Mongoose

关系型数据库管理系统

var http = require('http')
var work = require('./lib/timetrack')
var mysql = require('mysql')
var qs = require('querystring')
// 1.创建数据库
var db = mysql.createConnection({
    host:'127.0.0.1',
    user:'myuser',
    password:'mypassword',
    database:'timetrack',
    port:3000
})
// 2.创建server
var server = http.createServer((req,res)=>{
    switch(req.method){
        case 'POST':
            switch(req.url){
                case '/':
                    // 一般是路由函数,内部会有一些解析请求的辅助函数,用于实现模块化
                    work.add(db,req,res)
                    break
                case 'archive':
                    work.archive(db,req,res)
                    break
                case 'delete':
                    work.delete(db,req,res)
                    break
            }
            break
        case 'GET':
            switch(req.url){
                case '/':
                    work.show(db,res)
                    break
                case 'archive':
                    work.archive(db,res)
            }
            break
    }
})
// 3.初始化数据库,建表
db.query(
    "GREATE TABLE IF NOT EXISTS work ("
    + "id INT(10) NOT NULL AUTO_INCREMENT, "
    + "hours DECIMAL(5,2) DEFAULT 0, "
    + "date DATE, "
    + "archived INT(1) DEFAULT 0, "
    + "description LONGTEXT, "
    + "PRIMARY KEY(id)",
    (err) => {
        if(err) throw err
        console.log('Server started...')
        server.listen(3000,'127.0.0.1')
    }
)

MongoDB & Redis

尽管关系型DBMS为可靠性牺牲了性能,但很多NoSQL数据库把性能放在了第一位。因此,对于实时分析或消息传递而言,NoSQL数据库可能是更好的选择。NoSQL数据库通常也不需要预先定义数据schema, 对于那种要把数据存储在层次结构中, 但层次结构却会发生变化的程序而言,这很有帮助。

Redis

Redis非常适合处理那些不需要长期访问的简单数据存储,比如短信和游戏中的数据。教程尝试Redis是个很好的起点。要深入学习如何使用Redis,请看Josiah L. Carlson的Redis in Action一书(Manning, 2013)。

MongoDB

MongoDB数据库把文档存在集合(collection)中。它们不需要相同的schema,每个文档都可以有不同的schema。 这使得MongoDB比传统的RDBMS更灵活,因为你不用为预先定义schema而操心。


image

Mongoose

MongoDB是一个强大的数据库,而node-mongodb-native提供了高性能的MongoDB访问,但你可能想用一个抽象的数据库访问API,在底层帮你处理细节。这可以让你加快开发速度,同时维护更少的代码。这些API中最流行的是Mongoose。
Mongoose的模型(模型视图控制器中的说法)提供了一个到MongoDB集合接口,以及一些实用的功能,比如schema层次结构,中间件以及数据校验。 schema层次结构可以让一个模型跟其他模型关联,比如说,让一篇博客文章包含相关的评论。中间件可以转换数据,或在操作模型数据的过程中触发逻辑,让删除父数据时对子数据的修剪这样的任务变成自动化的。 Mongoose的校验支持让你可以在schema层面决定什么样的数据是可接受的,而不是必须手工处理它。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 面试题一:https://github.com/jimuyouyou/node-interview-questio...
    R_X阅读 1,648评论 0 5
  • mongoose入门 MongoDB是一个开源的NoSQL数据库,相比MySQL那样的关系型数据库,它更显得轻巧、...
    huilegezai阅读 4,483评论 0 14
  • 原文地址 本文简单的介绍了数据库,以及如何在 Node/Express 中应用他们。之后展示如何使用Mongoos...
    前端幼儿班阅读 5,202评论 1 5
  • 原文链接:https://github.com/jimuyouyou/node-interview-questio...
    R_X阅读 9,524评论 0 26
  • MongoDB 1. MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用...
    Kevinr阅读 1,603评论 0 3