2019年12月16日18:58:55

2019年10月04日12:20:59

1. nodejs简介

Node.js是一个让JavaScript运行在服务器端的开发平台,它让JavaScript的触角伸到了服务器端,可以与PHP、JSP、Python、Ruby平起平坐。

但Node似乎有点不同:

Node.js不是一种独立的语言,与PHP、JSP、Python、Perl、Ruby的“既是语言,也是平台”不同,Node.js的使用JavaScript进行编程,运行在JavaScript引擎上(V8)。

● 与PHP、JSP等相比(PHP、JSP、.net都需要运行在服务器程序上,Apache、Naginx、Tomcat、IIS。),Node.js跳过了Apache、Naginx、IIS等HTTP服务器,它自己不用建设在任何服务器软件之上。Node.js的许多设计理念与经典架构(LAMP = Linux + Apache + MySQL + PHP)有着很大的不同,可以提供强大的伸缩能力。一会儿我们就将看到,Node.js没有web容器。

Node.js自身哲学,是花最小的硬件成本,追求更高的并发,更高的处理性能。

特点:

所谓的特点,就是Node.js是如何解决服务器高性能瓶颈问题的。

1.1 单线程

在Java、PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让Web应用程序支持更多的用户,就需要增加服务器的数量,而Web应用程序的硬件成本当然就上升了。

Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。

另外,单线程的带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。

坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。

多线程、单线程的一个对比。

也就是说,单线程也能造成宏观上的“并发”。

1.2 非阻塞I/O non-blocking I/O

例如,当在访问数据库取得数据的时候,需要一段时间。在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库返回结果,才能执行后面的代码。也就是说,I/O阻塞了代码的执行,极大地降低了程序的执行效率。

由于Node.js中采用了非阻塞型I/O机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率。

当某个I/O执行完毕时,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数。为了处理异步I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。

阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%。所以,这是一种特别有哲理的解决方案:与其人多,但是好多人闲着;还不如一个人玩命,往死里干活儿。

1.3 事件驱动 event-driven

在Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件。在Node中,在一个时刻,只能执行一个事件回调函数,但是在执行一个事件回调函数的中途,可以转而处理其他事件(比如,又有新用户连接了),然后返回继续执行原事件的回调函数,这种处理机制,称为“事件环”机制。

Node.js底层是C++(V8也是C++写的)。底层代码中,近半数都用于事件队列、回调函数队列的构建。用事件驱动来完成服务器的任务调度,这是鬼才才能想到的。针尖上的舞蹈,用一个线程,担负起了处理非常多的任务的使命。

单线程,单线程的好处,减少了内存开销,操作系统的内存换页。

如果某一个事情,进入了,但是被I/O阻塞了,所以这个线程就阻塞了。

非阻塞I/O,不会傻等I/O语句结束,而会执行后面的语句。

非阻塞就能解决问题了么?比如执行着小红的业务,执行过程中,小刚的I/O回调完成了,此时怎么办??

事件机制,事件环,不管是新用户的请求,还是老用户的I/O完成,都将以事件方式加入事件环,等待调度。

2. http模块

2.1 例子 req,res
1//require表示引包,引包就是引用自己的一个特殊功能
2var http = require("http"); 3//创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
4var server = http.createServer(function(req,res){
5 //req表示请求,request; res表示响应,response
6 //设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
7 res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
8 res.end("哈哈哈哈,我买了一个iPhone" + (1+2+3) + "s");
9});
10
11//运行服务器,监听3000端口(端口号可以任改)
server.listen(3000,"127.0.0.1");

req表示请求,request; res表示响应,response

如果想修改程序,必须中断当前运行的服务器,重新node一次,刷新,才行。

ctrl+c,就可以打断挂起的服务器程序。

你会发现,我们本地写一个js,打死都不能直接拖入浏览器运行,但是有了node,我们任何一个js文件,都可以通过node来运行。也就是说,node就是一个js的执行环境。

1//这个案例简单讲解http模块
2//引用模块
3var http = require("http");
4
5//创建一个服务器,回调函数表示接收到请求之后做的事情
6var server = http.createServer(function(req,res){
7 //req参数表示请求,res表示响应
8 console.log("服务器接收到了请求" + req.url);
9 res.end();
10});
11//监听端口
server.listen(3000,"127.0.0.1");
2.2 req.url

我们现在来看一下req里面能够使用的东西。

最关键的就是req.url属性,表示用户的请求URL地址。所有的路由设计,都是通过req.url来实现的。

我们比较关心的不是拿到URL,而是识别这个URL。

识别URL,用到两个新模块,第一个就是url模块,第二个就是querystring模块

2.3 res.end(),write(),writeHead()
2.4 总结

response得先writeHead()才可以写其他语句。

res.writeHead(200,{"Content-Type":"text/html;charset=UTF8"});

3. querystring 模块

querystring.decode() == querystring.parse()

新增于: v0.1.99

querystring.decode() 函数是 querystring.parse() 的别名。

querystring.encode() == querystring.stringify()

新增于: v0.1.99

querystring.encode() 函数是 querystring.stringify() 的别名。

var querystring=require("querystring");

var string1=querystring.parse('foo=bar&abc=xyz&abc=123');
console.log(string1) var string2=querystring.decode('foo=bar&abc=xyz&abc=123');
console.log(string2)
//[Object: null prototype] { foo: 'bar', abc: [ 'xyz', '123' ] }
3.1 querystring.parse(str[, sep[, eq[, options]]])

querystring.parse() 方法将 URL 查询字符串 str 解析为键值对的集合。

例如,查询字符串 'foo=bar&abc=xyz&abc=123' 被解析为:

{
foo: 'bar',
abc: ['xyz', '123']
}

querystring.parse() 方法返回的对象不是原型继承自 JavaScript Object。 这意味着典型的 Object 方法如 obj.toString()obj.hasOwnProperty() 等都没有定义并且不起作用。

3.2 querystring.stringify(obj[, sep[, eq[, options]]])

querystring.stringify() 方法通过迭代对象的自身属性从给定的 obj 生成 URL 查询字符串。

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
// 返回 'foo=bar&baz=qux&baz=quux&corge=' querystring.stringify({ foo: 'bar', baz: 'qux' }, ';', ':');
// 返回 'foo:bar;baz:qux'
3.3总结:

parse是相当于编译成json,stringfy相当于是把json变成了字符串。

4.url模块

4.1 网址构成

URL 'http://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'

Url {
protocol: 'http:', // 指的是底层使用的协议是http
slashes: true, // 说明是否有协议的双斜线
auth: null,
host: 'imooc.com', // 指的是http协议的一个Ip地址或者域名
port: null, // 端口,默认是 80 端口,如果使用了别的端口就必须指明
hostname: 'imooc.com', // 主机名
hash: null, // hash值,通常对应页面上某个锚点,加#号之后将页面滚动到当前位置的
search: null, // 查询字符串参数
query: null, // 发送给http服务器的数据,通常是被等号分隔开的键值对称之为参数串
pathname: '/course/list', // 访问资源路径名
path: '/course/list', // 路径
href: 'http://imooc.com/course/list' // 没被解析的完整的超链接
}
4.2 url.parse()

使用url.parse()方法来将url解析成一个对象

url.parse()后面加一个true,可以将query参数解析成参数对象

let obj = url.parse(str,true);
var server = http.createServer(function (req, res) {
console.log("服务器收到请求" + req.url);
var querystring1=url.parse(req.url,true).query;
res.end(querystring1.name+"+"+querystring1.age);
});
4.3 pathname

得到路由路径

//得到用户的路径
var pathname = url.parse(req.url).pathname;
//默认首页
if(pathname == "/"){
pathname = "index.html";
}
//拓展名
var extname = path.extname(pathname);

5.fs模块

5.1 fs.readFile(),fs.readFileSync()

读文件

var fs = require("fs");

res.writeHead(200,{"Content-Type":"text/html;charset=UTF8"});

	//两个参数,第一个是完整路径,当前目录写./
//第二个参数,就是回调函数,表示文件读取成功之后,做的事情 fs.readFile("./test/1.txt",function(err,data){
if(err){
throw err;
}
console.log(userid + "文件读取完毕");
res.end(data);
});
5.2 处理图标路由的输出
//不处理小图标
if(req.url == "/favicon.ico"){
return;
}
5.3 mkdir(),mkdirSync()

异步地创建目录。 除了可能的异常,完成回调没有其他参数。

可选的 options 参数可以是指定模式(权限和粘滞位)的整数,也可以是具有 mode 属性和 recursive 属性(指示是否应创建父文件夹)的对象。

// 创建 /tmp/a/apple 目录,无论是否存在 /tmp 和 /tmp/a 目录。
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
if (err) throw err;
});
5.4 fs.stat类

stats.isDirectory()

如果 fs.Stats 对象描述文件系统目录,则返回 true

stats.isFile()

如果 fs.Stats 对象描述常规文件,则返回 true

fs.stat("./album/" + thefilename , function(err,stats){
//如果他是一个文件夹,那么输出它:
if(stats.isDirectory()){
wenjianjia.push(thefilename);
}
5.5 fs.readdir(path[, options], callback)

读取目录的内容。 回调有两个参数 (err, files),其中 files 是目录中的文件名的数组(不包括 '.''..')。

可选的 options 参数可以是指定编码的字符串,也可以是具有 encoding 属性的对象,该属性指定用于传给回调的文件名的字符编码。 如果 encoding 设置为 'buffer',则返回的文件名是 Buffer 对象。

如果 options.withFileTypes 设置为 true,则 files 数组将包含 [fs.Dirent] 对象。

path <string> | <Buffer> | <URL>
options <string> | <Object> encoding <string> 默认值: 'utf8'。
withFileTypes <boolean> 默认值: false。
callback <Function> err <Error>
files <string[]> | <Buffer[]> | <fs.Dirent[]>
5.6 异步编程思想——循环语句,异步语句

错误输出:循环语句嵌套异步,输出出错

var server = http.createServer(function(req,res){
//不处理小图标
if(req.url == "/favicon.ico"){
return;
}
//存储所有的文件夹
var wenjianjia = [];
//stat检测状态
fs.readdir("./album",function(err,files){
//files是个文件名的数组,并不是文件的数组,表示./album这个文件夹中的所有东西
//包括文件、文件夹
for(var i = 0 ; i < files.length ;i++){
var thefilename = files[i];
//又要进行一次检测
fs.stat("./album/" + thefilename , function(err,stats){
//如果他是一个文件夹,那么输出它:
if(stats.isDirectory()){
wenjianjia.push(thefilename);
}
console.log(wenjianjia);
});
}
});
});
5.7 把异步变成同步

使用迭代器iterator()

迭代器就是强行把异步的函数,变成同步的函数

var http = require("http");
var fs = require("fs"); var server = http.createServer(function(req,res){
//不处理收藏夹小图标
if(req.url == "/favicon.ico"){
return;
}
//遍历album里面的所有文件、文件夹
fs.readdir("./album/",function(err,files){
//files : ["0.jpg","1.jpg" ……,"aaa","bbb"];
//files是一个存放文件(夹)名的数组
//存放文件夹的数组
var wenjianjia = [];
//迭代器就是强行把异步的函数,变成同步的函数
//1做完了,再做2;2做完了,再做3
(function iterator(i){
//遍历结束
if(i == files.length){
console.log(wenjianjia);
return;
}
fs.stat("./album/" + files[i],function(err,stats){
//检测成功之后做的事情
if(stats.isDirectory()){
//如果是文件夹,那么放入数组。不是,什么也不做。
wenjianjia.push(files[i]);
}
iterator(i+1);
});
})(0);
});
res.end();
}); server.listen(3000,"127.0.0.1");

6. path模块

path 模块提供用于处理文件路径和目录路径的实用工具。

6.1 dirname(path)

path.dirname() 方法返回 path 的目录名,类似于 Unix 的 dirname 命令。

path.dirname('/foo/bar/baz/asdf/quux');
// 返回: '/foo/bar/baz/asdf'
6.2 path.extname(path)

path.extname() 方法返回 path 的扩展名,从最后一次出现 .(句点)字符到 path 最后一部分的字符串结束。 如果在 path 的最后一部分中没有 . ,或者如果 path 的基本名称除了第一个字符以外没有 .,则返回空字符串。

path.extname('index.html');
// 返回: '.html' path.extname('index.coffee.md');
// 返回: '.md' path.extname('index.');
// 返回: '.' path.extname('index');
// 返回: '' path.extname('.index');
// 返回: '' path.extname('.index.md');
// 返回: '.md'
6.3 path.join([...paths])

path.join() 方法使用平台特定的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径。

零长度的 path 片段会被忽略。 如果连接的路径字符串是零长度的字符串,则返回 '.',表示当前工作目录。

path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// 返回: '/foo/bar/baz/asdf' path.join('foo', {}, 'bar');
// 抛出 'TypeError: Path must be a string. Received {}'
6.4 path.parse(path)

path.parse() 方法返回一个对象,其属性表示 path 的重要元素。

path.parse('/home/user/dir/file.txt');
// 返回:
// { root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
6.5 静态资源文件管理
var http = require("http");
var url = require("url");
var fs = require("fs");
var path = require("path"); http.createServer(function(req,res){
//得到用户的路径
var pathname = url.parse(req.url).pathname;
//默认首页
if(pathname == "/"){
pathname = "index.html";
}
//拓展名
var extname = path.extname(pathname); //真的读取这个文件
fs.readFile("./static/" + pathname,function(err,data){
if(err){
//如果此文件不存在,就应该用404返回
fs.readFile("./static/404.html",function(err,data){
res.writeHead(404,{"Content-type":"text/html;charset=UTF8"});
res.end(data);
});
return;
};
//MIME类型,就是
//网页文件: text/html
//jpg文件 : image/jpg
var mime = getMime(extname);
res.writeHead(200,{"Content-type":mime});
res.end(data);
}); }).listen(3000,"127.0.0.1"); function getMime(extname){
switch(extname){
case ".html" :
return "text/html";
break;
case ".jpg" :
return "image/jpg";
break;
case ".css":
return "text/css";
break;
}
}

2019年10月04日16:56:17

最新文章

  1. Java正则认识
  2. C#编程普通型计算器 经验与感悟
  3. BackTrack5-r3硬盘安装
  4. android 抓包 使用 tcpdmp + Wireshark
  5. Linux上Eclipse项目右键菜单没有Maven
  6. HDU4758 Walk Through Squares AC自动机&amp;&amp;dp
  7. 《ENVI下遥感影像自然真彩色合成方法》——TM、spot5
  8. UIView上添加了一个按钮和一个单击手势的事件相应,互相不影响的处理方法。。
  9. hihocoder #1290 : Demo Day (2016微软编程测试第三题)
  10. InstallShield 创建自己的Dialog
  11. Chapter 4. Using the Gradle Command-Line 使用gradle命令行
  12. [Hapi.js] Replying to Requests
  13. Azure上Linux VM防DDOS攻击:使用Apache mod_evasive
  14. 初探Lambda表达式/Java多核编程【0】从外部迭代到内部迭代
  15. VopSdk一个高逼格微信公众号开发SDK(源码下载)
  16. Linux中常用目录作用
  17. MySQL 练习题 附答案,未完
  18. nvm 淘宝镜像
  19. 158A Next Round
  20. Web API与JWT认证

热门文章

  1. Oracle10g 64位 在Windows 2008 Server R2 中的安装 DBconsole无法启动
  2. ImageView的src与background及ScaleType
  3. RAM: Residual Attention Module for Single Image Super-Resolution
  4. 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第1节 Scanner类_2-概述及其API文档
  5. beego 注解路由无效问题分析
  6. 应用安全-安全设备-Waf系列-软Waf-云锁
  7. 反弹,反向穿越防火墙,NAT网关等乱七八糟的术语
  8. debian下重装mysql
  9. SSM001/构建maven多模块项目
  10. 兼容IE浏览器保存Cookie