Node.js (一)

node.js简介

官网: https;//nodejs.org/en/

1.Node.js 不是一种独立的语言
2.Node.js 使用JavaScript 进行编程
3.Node.js 跳过了 Apache Naginx IIs 等HTTP服务器,它自己不用建设在任何服务器软件之上
4.Node.js 是花费最小的硬件成本,追求更高的并发,更高的处理性能.
.
.
.

Node.js的特点

1.单线程
2.非阻塞I/O
3.事件驱动

单线程

在Java、PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让Web应用程序支持更多的用户,就需要增加服务器的数量,而Web应用程序的硬件成本当然就上升了。
Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。
另外,带线程的带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。
坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。


在这里插入图片描述

在这里插入图片描述

也就是说,单线程也能造成宏观上的“并发”多线程、单线程的一个对比。


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

例如,当在访问数据库取得数据的时候,需要一段时间。在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程都将暂停下来,等待数据库返回结果,才能执行后面的代码。也就是说,I/O阻塞了代码的执行,极大地降低了程序的执行效率。
由于Node.js中采用了非阻塞型I/O机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率。
当某个I/O执行完毕时,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数。为了处理异步I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次予以处理。
阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%。所以,这是一种特别有哲理的解决方案:与其人多,但是好多人闲着;还不如一个人玩命,往死里干活儿。



事件驱动event-driven

在Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件。在Node中,在一个时刻,只能执行一个事件回调函数,但是在执行一个事件回调函数的中途,可以转而处理其他事件(比如,又有新用户连接了),然后返回继续执行原事件的回调函数,这种处理机制,称为“事件环”机制。
Node.js底层是C++(V8也是C++写的)。底层代码中,近半数都用于事件队列、回调函数队列的构建。用事件驱动来完成服务器的任务调度,这是鬼才才能想到的。针尖上的舞蹈,用一个线程,担负起了处理非常多的任务的使命。


在这里插入图片描述


Nodejs特点的总结

单线程:
		单线程的好处,减少了内存开销,操作系统的内存换页。
		如果某一个事情,进入了,但是被I/O阻塞了,所以这个线程就阻塞了。
		
非阻塞I/O: 
		不会傻等I/O语句结束,而会执行后面的语句。
		非阻塞就能解决问题了么?比如执行着小红的业务,执行过程中,小刚的I/O回调完成了,此时怎么办??
		
事件机制:
		事件环,不管是新用户的请求,还是老用户的I/O完成,都将以事件方式加入事件环,等待调度。


Node.js中所有的I/O都是异步的,回调函数,套回调函数。



Nodejs适合开发什么

Node.js适合用来开发什么样的应用程序呢?
善于I/O,不善于计算。因为Node.js最擅长的就是任务调度,如果你的业务有很多的CPU计算,实际上也相当于这个计算阻塞了这个单线程,就不适合Node开发。
当应用程序需要处理大量并发的I/O,而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常适合。Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。
比如:
● 用户表单收集
● 考试系统
● 聊天室
● 图文直播
● 提供JSON的API(为前台Angular使用)


Node.js无法挑战老牌3P(JSP ASP PHP)

在这里插入图片描述


Nodejs简单的服务器搭建

Node.js是服务器的程序,写的js语句,都将运行在服务器上。返回给客户的,都是已经处理好的纯html。

1.HelloWorld.js


// require 表示引包,引包就是引用自己的一个特殊技能
var http = require('http');
//创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
var server = http.createServer(function(req,res){
    //req表示请求,request; res表示响应,response
    //设置HTTP头部,状态码是200,文件类型是html,字符集是utf-8
    res.writeHead(200,{'Content-type':'text/html;charset=UTF-8'});
    res.end('This is my first Nodejs page!');

});

//运行服务器 监听8000端口(端口号可以任改)
server.listen(8000,"127.0.0.1");



2.打开命令行cmd 找到js文件下的目录

此时服务器已经创建成功并运行中

在这里插入图片描述

任何一个js文件,都可以通过node来运行。也就是说,node就是一个js的执行环境。
我们现在,要跑起来一个服务器,这个服务器的脚本,要以.js存储。是一个js文件。用node命令运行这个js文件罢了。

3.浏览器访问服务器

在这里插入图片描述


Nodejs 的路由设计功能

Node.js没有根目录的概念,因为它根本没有任何的web容器!
让node.js提供一个静态服务,都非常难!
也就是说,node.js中,如果看见一个网址是
127.0.0.1:3000/fang
别再去想,一定有一个文件夹,叫做fang了。可能/fang的物理文件,是同目录的test.html
URL和真实物理文件,是没有关系的。URL是通过了Node的顶层路由设计,呈递某一个静态文件的。

Node 有优秀的路由设计功能,可以将需要访问的页面设计成自定义路由

127.0.0.1:8000/yuan 访问了同目录下的haha.html

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码块

StaticRender.js


//require表示引包,引包就是引用自己的一个特殊功能
var http = require("http");
// 引用一个可以读文件的fs
var fs = require("fs");

//创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
var server = http.createServer(function(req,res){
    if(req.url == "/fang"){
        fs.readFile("./test/xixi.html",function(err,data){
            //req表示请求,request;  res表示响应,response
            //设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
            res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
            res.end(data);
        });
    }else if(req.url == "/yuan"){
        fs.readFile("./test/haha.html",function(err,data){
            //req表示请求,request;  res表示响应,response
            //设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
            res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
            res.end(data);
        });
    }else if(req.url == "/0.jpg"){
        fs.readFile("./test/0.jpg",function(err,data){
            //req表示请求,request;  res表示响应,response
            //设置HTTP头部,状态码是200,文件类型是image,字符集是utf8
            res.writeHead(200,{"Content-type":"image/jpg"});
            res.end(data);
        });
    }else if(req.url == "/bbbbbb.css"){
        fs.readFile("./test/aaaaaa.css",function(err,data){
            //req表示请求,request;  res表示响应,response
            //设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
            res.writeHead(200,{"Content-type":"text/css"});
            res.end(data);
        });
    }else{
        res.writeHead(404,{"Content-type":"text/html;charset=UTF-8"});
        res.end("嘻嘻,没有这个页面呦");
    }
});

//运行服务器,监听3000端口(端口号可以任改)
server.listen(8000,"127.0.0.1");



在这里插入图片描述

在这里插入图片描述


http模块的简单运用

Node.js中,将很多的功能,划分为了一个个mudule,大陆的书翻译为模块;台湾的书,翻译为模组。
这是因为,有一些程序需要使用fs功能(文件读取功能),有一些不用的,所以为了效率,你用啥,你就require啥。

在这里插入图片描述

所以Nodejs没有Web容器

.
.
.
设置一个响应头:
res.writeHead(200,{“Content-Type”:“text/plain;charset=UTF8”});

在这里插入图片描述

http.js



// 案例,简单讲解http模块
//引用模块
var http = require('http');

//创建一个服务器,回调函数表示接收到请求之后做的事情
var server = http.createServer(function(req,res){
    //req 参数表示请求,res 参数表示响应
    console.log('服务器接收到请求: ' + req.url);
    // 每一个请求必须对应一个响应(res.end),否则服务器一直挂起
    res.end();
});

// 服务器监听
server.listen(8000,'127.0.0.1');



在这里插入图片描述

http.js



// 案例,简单讲解http模块
//引用模块
var http = require('http');

//创建一个服务器,回调函数表示接收到请求之后做的事情
var server = http.createServer(function(req,res){
    //req 参数表示请求,res 参数表示响应
    console.log('服务器接收到请求: ' + req.url);

	//设置一个响应头:
    res.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});
    res.write('<h1>我是h1标签</h1>');
    res.write('<h2>我是h2标签</h2>');
    res.write('<h3>我是h3标签</h3>');
    res.write('<h4>我是h4标签</h4>');

    // 每一个请求必须对应一个响应(res.end),否则服务器一直挂起
    res.end();
});

// 服务器监听
server.listen(8000,'127.0.0.1');





在这里插入图片描述


url模块的介绍

最关键的就是req.url属性,表示用户的请求URL地址。所有的路由设计,都是通过req.url来实现的。
我们比较关心的不是拿到URL,而是识别这个URL。

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

url.js




// 引用http 模块和 url 模块
var http = require('http');
var url = require('url');

var server = http.createServer(function(req,res){
    //设置响应头
    res.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});

    // 获取路径
    var pathname = url.parse(req.url).pathname;

    // 获取查询结果(路径后面的数据)
    // 如果url.parse()第二个参数是true,那么获取得到的是一个对象集合
    var query = url.parse(req.url,true).query;
    var age = query.age // 此时query为集合

    console.log('pathname --> ' + pathname);
    console.log('query --> ' + query.sex);
    console.log('age -->' + age);

    res.end('查询成功!!!');

});

server.listen(8000,'127.0.0.1');




在这里插入图片描述


案例之表单提交至本地服务器

任意目录下创建的html表单

1.demo表单提交.html


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
</head>
<body>

    <!-- 提交到http://127.0.0.1本地服务器上 -->
    <form action="http://127.0.0.1:8000/" method="GET">
        <p><span>姓名  :</span><input type="text" name="name"></p>
        <p><span>用户名:</span><input type="text" name="user"></p>
        <p><span>密码  :</span><input type="password" name="pwd"></p>
        <p><span>性别:</span><input type="radio" name='sex' value="男">男<input type="radio" name='sex' value="女">女</p>
        <p><input type="submit" value="提交"></p>
    </form>

</body>
</html>



2.demo.js



// 案例 表单数据提交到本地服务器并打印
var http = require('http');
var url = require('url');

var server = http.createServer(function(req,res){
    //设置响应头
    res.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});

    //获取查询部分的对象结果集(GET方式将数据显示在url地址栏上)
    var queryObj = url.parse(req.url,true).query;
    var name = queryObj.name;
    var user = queryObj.user;
    var pwd = queryObj.pwd;
    var sex = queryObj.sex;

    console.log('服务器接收到数据\n' + 'name --> ' + name  + '\nuser --> ' + user  +
        '\npwd --> ' + pwd  + '\nsex --> ' + sex);
    res.end('提交成功!!!');


});

// 此处服务器监听的端口号与IP地址应该与表单提交的地址相一致
server.listen(8000,'127.0.0.1');




3.cmd命令行下找到demo.js 所在目录
在这里插入图片描述

4.进入表单html 输入注册信息并提交

在这里插入图片描述


案例之顶层路由设计

适用于用户通过学生id或者老师id查询具体信息

demo1.js



var http = require('http');
var url = require('url');

var server = http.createServer(function(req,res){
    //获取url
    var userurl = req.url;

    //设置响应头
    res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});

    //substr函数判断此时的开头,第0位到第8位
    if( userurl.substr(0,9) == '/student/' ){
        var stuId =userurl.substr(9);
        if( /^\d{10}$/.test(stuId) ){
            res.end('您要查询的学生id为: ' + stuId);
        }else{
            res.end('您要查询的学生不存在!');
        }

    }else if( userurl.substr(0,9) == '/teacher/' ){
        var teacherId = userurl.substr(9);
        if( /^\d{6}$/.test(teacherId) ){
            res.end('您要查询的老师id为: ' + teacherId);
        }else{
            res.end('您要查询的老师不存在!');
        }
    }else{
        res.end('输入的id不存在!');

    }

});


server.listen(8000,'127.0.0.1');







在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


案例之探求事件环机制

单线程多事件,非阻塞I/O,事件驱动循环 特点
Node 处理多客户端访问是单线程多事件形式
当多客户端访问时不会I/O阻塞,
如果回调函数内容事件处理时间较长而多用户同时访问会并发进行并将用户回调函数放入事件循环机制中再执行

fs.js



var http = require('http');
var fs = require('fs');

var server = http.createServer(function(req,res){
    var userId = parseInt(Math.random() * 89999 ) +10000;

    console.log('欢迎' + userId);

    //设置响应头
    res.writeHead(200,{"Content-Type":"text/html;charset=UTF-8"});
    // 两个参数,第一个是完整路径,当前目录写./  因为node是跨平台的, ./可以适用linux和window,这样写通用
    // 第二个参数是回调函数,表示文件读取成功后做的事情
    fs.readFile('./test/1.txt',function(err,data){
        if(err)throw err;
        console.log(userId + '文件读取完毕!!!');
        res.end(data);
    });

});

server.listen(8000,'127.0.0.1');



此处多客户端访问node 将回调函数事件重新放入循环机制再执行(多用户并发执行时候出现的现象)

在这里插入图片描述

正常的先执行事件在执行回调函数内容(因为回调函数事件执行过快,在下一个用户访问之前已经处理完回调函数所以没有出现并发执行的状态)

在这里插入图片描述


案例之fs模块的功能

fsDemo.js


var http = require('http');
var fs = require('fs');

var server = http.createServer(function(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=UTF-8'});

    //不处理小图标
    if(req.url == '/favicon.ico'){
        return;
    }

    fs.mkdir('./album/ooo',function(){
        res.end('创建成功');
    });
    fs.rmdir('./album/ooo',function(){
        res.end('删除文件夹成功!!!');
    });


    stat检测状态
    fs.stat('./album/ha',function(err,data){
        //检测这个路径,是不是一个文件夹
        console.log(data.isDirectory());
    });

    fs.readdir('./album',function(err,files){
        //files是个数组,表示 ./album这个文件夹中的所有东西
        //包括文件丶文件夹
        console.log(files);
    });

});

server.listen(8000,'127.0.0.1');



在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


案例之查询album文件夹下的所有文件,将异步变同步

由于for 循环下有太多异步语句,所以遍历出来的值可能为空

迭代器将异步变成同步

for.js



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 是一个存放文件或文件夹名的数组
        //存放文件夹的数组
        var wenjianjia = [];

        //定义一个迭代器,从0开始
        //迭代器就是强行把异步的函数变成同步的函数
        (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(8000,'127.0.0.1');





在这里插入图片描述


案例之处理静态文件管理

Nodejs可以单线程多事件异步处理并发,这是php javaee jsp等没有的功能,但是Nodejs也有缺点,
Apache服务器环境有默认的静态文件管理,即url地址栏输入网址对应了本地文件的目录路径
而Nodejs没有这项功能,此案例即自己写出静态文件管理的功能,实现url地址栏输入文件目录来访问实际目录下的文件

statics.js


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);
    console.log(extname);

    //真的读取这个文件
    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=UTF-8'});
                res.end(data);
            })
            //退出服务器挂起
            return;

        };
        //MIME类型:
        //  网页文件: text/html
        //  jpg文件: image/jpg
        var mime = getMime(extname);
        res.writeHead(200,{'Content-Type':mime});
        res.end(data);
    });

}).listen(8000,'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;
    }

}



static目录下
在这里插入图片描述

在这里插入图片描述

此时通过自定义函数将响应头类型Content-Type 设置为text/html
在这里插入图片描述


总结


Node.js开发服务器,数据、路由。本地关心的效果,交互;
Node.js实际上是极客开发出的一个小玩具,不是银弹。有着别人不具备的怪异特点:
单线程、Non-blocking I/O、Event Driven。 实际上是一个特点。
首先,Node不为每个用户开辟一个线程,所以非常极端的选择了单线程。单线程,要照顾所有的用户,那么就必须有非阻塞I/O,否则一个人的I/O就把别人、自己都阻塞了。一旦有非阻塞I/O,一个人如果I/O去了,就会放弃CPU的使用权,换成另一个人使用CPU(或者执行此人后面的语句)。所以CPU的利用率100%。第一个人I/O结束了,就要用事件来通知线程,执行回调函数。此时必须有事件环,就有一个排队调度机制。Node中有超过半数的C++代码,在搭建事件环。

Node.js和别的老牌3P不一样: 
1) 没有自己的语法,使用V8引擎,所以就是JS。V8引擎解析JS的,效率非常高,并且V8中很多东西都是异步的。Node就是将V8中的一些功能自己没有重写(别人做了,自己就站在巨人肩膀上),移植到了服务器上。
2) 没有web容器,就是安装配置完成之后,没有一个根目录。


系统中,80端口,就是默认http端口。所以当没有端口号的时候,就是80端口。
server.listen(80,"127.0.0.1");



猜你喜欢

转载自blog.csdn.net/qq_39469688/article/details/82979929