[Node.js] 01 - How to learn node.js

基本概念javascript

连接:https://www.zhihu.com/question/47244505/answer/105026648php

连接:How to decide when to use Node.js?html

JavaScript 是一种(最好的)编程语言, 主要做为前端开发中用来增长网页的动态功能,好比操做DOM, 读取用户输入, 动画效果, 提 交服务器请求(Ajax). JavaScript 是什么? - 前端开发前端

NodeJs 是基于JavaScript的,能够作为后台开发的语言. 提供了不少系统级的API,如文件操做、网络编程等. 用事件驱动, 异步编程,主要是为后台网络服务设计. Node.js是用来作什么的? - 编程vue

ReactJS 为Facebook开发的,更多的像一个JS的库.主要是在前端Web开发中, 对MVC中的V进行操做. AngularJS和ReactJS分别是干什么的?不会Javascript是否能够学习 - 前端开发java

React Native 它基于开源框架ReacJS,并可用来开发iOS和Android原生应用, 主要为移动端服务. 深刻浅出 React Native:使用 JavaScript 构建原生应用 - 前端外刊评论 - 知乎专栏
 
 

学习指导node

The ideal candidate will have:git

  • Expereienced in web application developement using node.js
  • Experience using Chinese API, i.e. WeChat Open API

The following skills are an advantage:github

  • Experience in React

Experience:web

  • node.js: 1 year (Required)

 


做者:厂长

连接:https://www.zhihu.com/question/33578075/answer/56951771
原文地址 Node.js is the New Black
 
经过JavaScript语言开发web服务端的东西”。
node.js有非阻塞,事件驱动I/O等特性,从而让高并发(high concurrency)在的轮询(Polling)和comet构建的应用中成为可能。
 
传统方式:

浏览器给网站发请求的过程一直没怎么变过:

  1. 当浏览器给网站发了请求。
  2. 服务器收到了请求,而后开始搜寻被请求的资源。若是有须要,服务器还会查询一下数据库
  3. 最后把响应结果传回浏览器。

不过,在传统的web服务器中(好比Apache),每个请求都会让服务器建立一个新的进程来处理这个请求

 

部分页面请求:

后来有了Ajax。有了Ajax,咱们就不用每次都请求一个完整的新页面了,取而代之的是,每次只请求须要的部分页面信息就能够了。这显然是一个进步。

 

案例:

你的好友会随时的推送新的状态,而后你的新鲜事会实时自动刷新。要达成这个需求,咱们须要让用户一直与服务器保持一个有效链接。目前最简单的实现方法,就是让用户和服务器之间保持长轮询(long polling)

  长轮询

HTTP请求不是持续的链接,你请求一次,服务器响应一次,而后就完了。

长轮训是一种利用HTTP模拟持续链接技巧

具体来讲,只要页面载入了,无论你需不须要服务器给你响应信息,你都会给服务器发一个Ajax请求。【客户端先发了再说】

这个请求不一样于通常的Ajax请求,服务器不会直接给你返回信息,而是它要等着,直到服务器以为该给你发信息了,它才会响应。

好比,你的好友发了一条新鲜事,服务器就会把这个新鲜事当作响应发给你的浏览器,而后你的浏览器就刷新页面了。

浏览器收到响应刷新完以后,再发送一条新的请求给服务器,这个请求依然不会当即被响应。

因而就开始重复以上步骤。利用这个方法,可让浏览器始终保持等待响应的状态。虽然以上过程依然只有非持续的Http参与,可是咱们模拟出了一个看似持续的链接状态

传统的长链接方式很差

咱们再看传统的服务器(好比Apache)。每次一个新用户连到你的网站上,你的服务器就得开一个链接。每一个链接都须要占一个进程,这些进程大部分时间都是闲着的(好比等着你好友发新鲜事,等好友发完才给用户响应信息。或者等着数据库返回查询结果什么的)。虽然这些进程闲着,可是照样占用内存。这意味着,若是用户链接数的增加到必定规模,你服务器没准就要耗光内存直接瘫了。

非阻塞事件驱动

这种状况怎么解决?解决方法就是刚才上边说的:非阻塞事件驱动

你把非阻塞的服务器想象成一个loop循环,这个loop会一直跑下去。一个新请求来了,这个loop就接了这个请求,把这个请求传给其余的进程(好比传给一个搞数据库查询的进程),而后响应一个回调(callback)。完事了这loop就接着跑,接其余的请求。

这样下来。服务器就不会像以前那样傻等着数据库返回结果了。

若是数据库把结果返回来了,loop就把结果传回用户的浏览器,接着继续跑。在这种方式下,你的服务器的进程就不会闲着等着。

从而在理论上说,同一时刻的数据库查询数量,以及用户的请求数量就没有限制了。服务器只在用户那边有事件发生的时候才响应,这就是事件驱动。


FriendFeed是用基于Python的非阻塞框架Tornado (知乎也用了这个框架) 来实现上面说的新鲜事功能的。

不过,Node.js就比前者更妙了。Node.js的应用是经过javascript开发的,而后直接在Google的变态V8引擎上跑。用了Node.js,你就不用担忧用户端的请求会在服务器里跑了一段可以形成阻塞的代码了。

由于javascript自己就是事件驱动的脚本语言。你回想一下,在给前端写javascript的时候,更多时候你都是在搞事件处理和回调函数。javascript自己就是给事件处理量身定制的语言。

Node.js仍是处于初期阶段。若是你想开发一个基于Node.js的应用,你应该会须要写一些很底层代码。可是下一代浏览器很快就要采用WebSocket技术了,从而长轮询也会消失。在Web开发里,Node.js这种类型的技术只会变得愈来愈重要。

 

读罢,引出若干问题。

 

Ref: WEB通信技术之短轮询、长轮询(comet)、长链接(SSE)、WebSocket

实现Web端即时通信的方法:实现即时通信主要有四种方式,它们分别是短轮询长轮询(comet)长链接(SSE)WebSocket

①短轮询

服务器端在收到请求后,不管是否有数据更新,都直接进行响应。

实现简单;但连接频繁,洪水攻击的危险。

②comet 长轮询

当服务器收到客户端发来的请求后,不会直接进行响应,而是先将这个请求挂起,而后判断服务器端数据是否有更新。

若是有更新,则进行响应,若是一直没有数据,则到达必定的时间限制(服务器端设置)后关闭链接。

明显减小了不少没必要要的http请求次数,相比之下节约了资源。However,链接挂起也会致使资源的浪费。

③SSE

SSE是HTML5新增的功能,全称为Server-Sent Events。容许服务推送数据到客户端。

SSE在本质上就与以前的长轮询、短轮询不一样,虽然都是基于http协议的,可是轮询须要客户端先发送请求。而SSE最大的特色就是不须要客户端发送请求

④WebSocket

WebSocket是Html5定义的一个新协议,与传统的http协议不一样,该协议能够实现服务器与客户端之间全双工通讯

简单来讲,首先须要在客户端和服务器端创建起一个链接,这部分须要http。链接一旦创建,客户端和服务器端就处于平等的地位,能够相互发送数据,不存在请求和响应的区别。

四种Web即时通讯技术比较

从兼容性角度考虑,短轮询 > 长轮询 > 长链接SSE > WebSocket;

从性能方面考虑,WebSocket > 长链接SSE > 长轮询 > 短轮询。

 

 

Ref: 七种WebSocket框架的性能比较【实验数据比较】

测试结果分析

  • Netty, Node.js, Undertow, Vert.x都能正常创建百万链接。 Jetty, Grizzly 和 Spray未能完成百万链接
  • Netty表现最好。内存占用很是的少, CPU使用率也不高。 尤为内存占用,远远小于其它框架
  • Jetty, Grizzly和Spray会产生大量的中间对象,致使垃圾回收频繁。Jetty表现最差
  • Node.js表现很是好。 尤为是测试中使用单实例单线程,创建速度很是快,消息的latency也很好。 内存占用也不错
  • Undertow表现也不错,内存占用比Netty高一些,其它差很少
  • 这里还未测到Spray另外一个很差的地方。 在大量链接的状况小,即便没有消息发送,Spray也会占用40% CPU 时间
  • 来自:http://www.tuicool.com/articles/veIvue

 

 

Ref: HTML5:WebSocket是否会取代HTTP

“下一代浏览器很快就要采用WebSocket技术,长轮询(long polling)也就会消失掉”

  • 什么是WebSocket?
  • WebSocket跟普通的HTTP有什么不同?
  • WebSocket是否会取代HTTP?

(1).

关于第一个问题,简单来讲,WebSocket主要提供了一个全双工(Full-Duplex)和持久化(Long lived)的链接,它跟HTTP同样工做在TCP层上,它主要解决了长轮询的问题,那什么是长轮询问题呢?

首先看一个应用场景:浏览器(客户端)想从服务器端接收实时数据。

在之前,客户端就会采起长轮询(long polling)机制:客户端发送请求之后,若是没有等到服务器的响应,该请求就阻塞等待服务器返回消息,直到服务器返回响应之后,客户端才会发起新的HTTP轮询请求 (注意:若是是HTTP1.1,那么新的HTTP请求会复用前一个HTTP请求的TCP连接),因为是阻塞等待模式,若是这时候,客户端还须要再一次发送消息,就须要创建新的TCP链接到服务器,从而形成资源的浪费;

另外一方面,若是服务器想要再次推送新的响应消息给客户端,它须要等待客户端发起新的请求,才能给予。。。(HTTP的Request只能有一个Response,服务器不是想给就能给,不便于"推送"

 

实际上HTTP Streaming技术是能够知足服务器发送多个消息到客户端的:

HTTP streaming: a variety of techniques (multipart/chunked response) that allow the server to send more than one response to a single client request. The W3C is standardizing this as Server-Sent Events using a text/event-stream MIME type. The browser API (which is fairly similar to the WebSocket API) is called the EventSource API.

可是,并不能解决客户端阻塞等待或者创建新链接的问题。

而WebSocket协议,容许客户端和服务器端能够在任什么时候间任意发送消息,而且异步处理接收到的消息,很好的解决了长轮询这种相似hacks服务器的简单粗暴行为。

 

(2).

关于第二个问题,WebSocket在第一次跟服务器握手的时候,会用到HTTP协议,在Upgrade里指明该次握手是用于创建WebSocket (Upgrade: WebSocket),握手以后,客户端和服务器之间的通讯,将会按照WebSocket的协议报文来通讯。

实际上,HTTP和WebSocket这两种协议在初次跟服务器握手时,都采用同样的建连方式,可是WebSocket只须要创建一次TCP链接,随后只须要发送较小的WebSocket报文(该报文只有6个字节的开销,2个字节header,4个字节的mask value)。

顺便插一句,很喜欢stackoverflow上面的一段话:

The latency overhead is not so much from the size of the headers, but form the logic to parse/handle/store those headers. In addtion, the TCP connection setup latency is probably a bigger factor than the size or processing time for each request.

这里讲TCP建连的开销比处理header大小须要的开销更多,其实,这个也适用于其余方面,消息大小长短可能不是形成开销的主要缘由,更为重要的是咱们用什么方式(逻辑)去解析/处理这些消息?

 

(3). 

关于第三个问题,WebSocket并不会取代HTTP协议,它只是HTTP的一个扩展,它主要应用的场景是,当javascript应用运行的浏览器(客户端)想从服务器端接收实时数据,服务器能够随时推送消息到客户端,客户端也不须要阻塞或者从新创建链接到服务器。

最后说下,为何WebSocket不直接采用HTTP,那是由于WebSocket须要异步通讯模式,若是采用普通HTTP,会让服务器感到困惑,因此,采用了与HTTP彻底不同的报文格式。

 

另外,我我的很喜欢的一篇颇有意思的解读:

WebSocket 是什么原理?为何能够实现持久链接?

 

HTTP的弊端

请记住 Request = Response , 在HTTP中永远是这样,也就是说一个request只能有一个response。并且这个response也是被动的,不能主动发起。

 

Client发送请求

GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com
首先, Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器: 泥煤,不要忽悠窝,我要验证尼是否是真的是Websocket助理。
而后, Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不一样的服务所须要的协议。简单理解: 今晚我要服务A,别搞错啦~
最后, Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本)
 
Server返回请求
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat

 

同时由客户主动询问,转换为服务器(推送)有信息的时候就发送(固然客户端仍是等主动发送信息过来的。。),
没有信息的时候就交给接线员(Nginx),不须要占用自己速度就慢的客服(Handler)了

【Nginx :一个高性能的HTTP和反向代理服务器】
 
至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:不能
可是能够经过上面说的 long poll 和 ajax 轮询来 模拟出相似的效果

 

Node.js 建立第一个应用

 Node.js 应用是由哪几部分组成的:

  1. 引入 required 模块:咱们可使用 require 指令来载入 Node.js 模块。

  2. 建立服务器:服务器能够监听客户端的请求,相似于 Apache 、Nginx 等 HTTP 服务器。

  3. 接收请求与响应请求 服务器很容易建立,客户端可使用浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。

 
一个能够工做的 HTTP 服务器,简单示范:
var http = require('http'); http.createServer( function (request, response) { // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead (200, {'Content-Type': 'text/plain'}); // 发送响应数据 "Hello World"
    response.end ('Hello World\n'); }).listen(8888); // 终端打印以下信息
console.log('Server running at http://127.0.0.1:8888/');

 

NPM 使用介绍

From: http://www.runoob.com/nodejs/nodejs-npm.html

随同NodeJS一块儿安装的包管理工具,能解决NodeJS代码部署上的不少问题,常见的使用场景有如下几种:

  • 容许用户从NPM服务器下载别人编写的第三方包到本地使用。【借用他人代码】
  • 容许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。【借用他人命令行程序】
  • 容许用户将本身编写的包或命令行程序上传到NPM服务器供别人使用。【分享本身的代码或命令行程序】

 

unsw@unsw-UX303UB$ node -v v8.9.4
unsw@unsw-UX303UB$ npm -v 5.6.0 

╭─────────────────────────────────────╮
│                                     │
│ Update available 5.6.0 → 5.8.0      │
│ Run npm i -g npm to update          │
│                                     │
╰─────────────────────────────────────╯

 

$ npm list -g  // 查看全部全局安装的模块

 

package.json文件

From: package.json文件 - from ruanyifeng 

  1. node,npm 都要用。
    1.1  node在调用require的时候去查找模块,会按照一个次序去查找,package.json会是查找中的一个环节。【见阮一峰的require分析 http://www.ruanyifeng.com/blog/2015/05/require.html
    1.2  npm用的就比较多,其中的 "dependencies" 字段就是本模块的依赖的模块清单。每次npm update的时候,npm会自动的把依赖到的模块也下载下来。当npm install 本模块的时候,会把这里提到的模块都一块儿下载下来。经过package.json,就能够管理好模块的依赖关系。

  2. 若是是应用,没必要编写package.json

 

(1). 每一个项目的根目录下面,通常都有一个package.json文件,定义了这个项目所须要的各类模块,以及项目的配置信息(好比名称、版本、许可证等元数据)。

$ npm init
// 以使用命令自动生成。npm init

(2). 有了package.json文件,直接使用npm install命令,就会在当前目录中安装所须要的模块。

// 根据这个配置文件,自动下载所需的模块,也就是配置项目所需的运行和开发环境。$ npm install

(3). 若是一个模块不在package.json文件之中,能够单独安装这个模块,并使用相应的参数,将其写入package.json文件之中。

$ npm install express --save $ npm install express --save-dev

 

一个更完整的package.json文件,以下所示:

{ "name": "Hello World", "version": "0.0.1", "author": "张三", "description": "第一个node.js程序", "keywords":["node.js","javascript"], "repository": { "type": "git", "url": "https://path/to/url" },
"license":"MIT", "engines": {"node": "0.10.x"}, "bugs":{"url":"http://path/to/bug","email":"bug@example.com"}, "contributors":[{"name":"李四","email":"lisi@example.com"}], "scripts": { "start": "node index.js"    // npm run start },
"dependencies": {          // 运行 所依赖模块 "express": "latest", "mongoose": "~3.8.3", "handlebars-runtime": "~1.0.12", "express3-handlebars": "~0.5.0", "MD5": "~1.2.0" },
"devDependencies": {        // 开发 所须要的模块 "bower": "~1.2.8", "grunt": "~0.4.1", "grunt-contrib-concat": "~0.3.0", "grunt-contrib-jshint": "~0.7.2", "grunt-contrib-uglify": "~0.2.7", "grunt-contrib-clean": "~0.5.0", "browserify": "2.36.1", "grunt-browserify": "~1.3.0", } }
 

peerDependencies

须要一种机制,在模板安装的时候提醒用户,若是A和B一块儿安装,那么B必须是2.0模块。

从npm 3.0版开始,peerDependencies再也不会默认安装了。

{
  "name": "chai-as-promised",    // 安装模块时 "peerDependencies": {         // 主程序必须一块儿安装
"chai": "1.x"    // 并且的版本必须是
}
}chai-as-promisedchaichai1.x

bin字段

指定各个内部命令对应的可执行文件的位置

"bin": { "someTool": "./bin/someTool.js" }

main字段

指定了加载的入口文件,require('moduleName')就会加载这个文件。这个字段的默认值是模块根目录下面的index.js

"main": "index.js",

config 字段

{ "name" : "foo", "config" : { "port" : "8080" }, "scripts" : { "start" : "node server.js" } }

get(): 用户执行npm run start命令时,脚本就能够获得该引用config值,以下:

http .createServer(...) .listen(process.env.npm_package_config_port)

set(): 用户也能够改变这个值。

$ npm config set foo:port 80

 

其余字段,详见连接。

package.json的加载与解析。

  

require 的源码:在 Node 的 lib/module.js 文件。 

模块的加载实质上就是,

    1. 注入exports、require、module三个全局变量,
    2. 而后执行模块的源码,
    3. 而后将模块的 exports 变量的值输出。

 

require执行逻辑:

当 Node 遇到 require(X) 时,按下面的顺序处理。

(1)若是 X 是内置模块(好比 require('http')) 
  a. 返回该模块。 
  b. 再也不继续执行。

 

(2)若是 X 以 "./" 或者 "/" 或者 "../" 开头 
  a. 根据 X 所在的父模块,肯定 X 的绝对路径。 
  b. 将 X 当成文件,依次查找下面文件,只要其中有一个存在,就返回该文件,再也不继续执行。

  • X
  • X.js
  • X.json
  • X.node

  c. 将 X 当成目录,依次查找下面文件,只要其中有一个存在,就返回该文件,再也不继续执行。

  • X/package.json(main字段)
  • X/index.js
  • X/index.json
  • X/index.node

 

(3)若是 X 不带路径 
  a. 根据 X 所在的父模块,肯定 X 可能的安装目录。 
  b. 依次在每一个目录中,将 X 当成文件名或目录名加载。

 

例子:require('bar') 

首先,肯定 x 的绝对路径多是下面这些位置,依次搜索每个目录。

/home/ry/projects/node_modules/bar /home/ry/node_modules/bar /home/node_modules/bar /node_modules/bar 

搜索时,Node 先将 bar 当成文件名,依次尝试加载下面这些文件,只要有一个成功就返回。

bar
bar.js bar.json bar.node 

若是都不成功,说明 bar 多是目录名,因而依次尝试加载下面这些文件。

bar/package.json(main字段) bar/index.js bar/index.json bar/index.node 

若是在全部目录中,都没法找到 bar 对应的文件或目录,就抛出一个错误。

 

(4) 抛出 "not found"

 


以上即是基本的背景和配置内容。
相关文章
相关标签/搜索