个人喜欢使用的后端服务器配置及部分小结: Node express

个人喜欢使用的后端服务器配置及部分小结: Node express

const express = require('express')
const http = require('http')
const app = express()
var router = express.Router()
const path = require('path')

// 提供来自多个目录的静态文件,但优先于“/public”:
app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, 'files')))
app.use(express.static(path.join(__dirname, 'uploads')))


app.use(express.json())
app.use(express.urlencoded({ extended: false }))

router.get('/',function(req,res,next) {
    console.log('经过路由 /')
    res.send('can you see me?')
    next()
})
router.get('/user/:id', (req, res) => {
    res.send('user:' + req.params.id)
})

app.use(router)
http.createServer(app).listen(3000, () => {
    console.log('localhost:3000')
})

<1>、为什么要用 app.use(express.urlencoded({ extended: false }))

1.1、那么先解释说明一下urlencoded/JSON两种格式及区别

urlencoded格式详解

【urlencoded格式】:又叫form格式,或者是x-www-form-urlencoded格式。

表单格式是由键值对组成。键和值之间用=。多个键值对之间用&。例如:name=ZhangSan&age=16

JSON格式详解

先说说什么是JSON:

​ JSON 是存储和交换文本信息的语法。类似 XML
​ JSON 比 XML 更小、更快,更容易解析
​ JSON是独立于语言
​ JSON 具有自我描述性,更易理解

再具体说说使用

1:JSON有对象、数组两种结构,有字符串、数字(整数、浮点数)、逻辑值(true/false)、空值(null)、四种数据类型
2:用大括号{}表示对象。对象是由属性组成的。属性是由键值对组成的。键和值之间用冒号隔开。属性之间用逗号隔开。键必须用双引号引起来。
3:用中括号[] 表示数组。数组由单独的值组成
4:JSON的灵活就灵活在:JSON可以嵌套

示例:

1、简单示例,两种方式表达

内容:我的名字叫,年龄35岁,不抽烟
urlencoded格式表述为:name=呱呱&age=35&smoke=false
JSON格式表述为:{ “name” :“呱呱”, “age”:35, “smoke”:false }

2、复杂应用,只能用JSON

内容:我的名字叫呱呱,年龄35岁,不抽烟,我有三本书(语文、数学、英语),我的领导是小猪老师,他年龄35岁。我有2个孩子,分别是:呱小呱,3岁,呱唧呱 ,5岁

urlencoded格式表述为:表述不出来

JSON格式表述为:

{ “name” :“呱呱”,
“age”:35,
“smoke”:false,
“books” : [“语文”,“数学”,“英语”] ,
“leader”: {“name” :“小猪”, “age”:35},
“sons”:[ {“name” :“呱小呱” ,“age”:3 },{“name” :“呱唧呱”, “age”:5} ]
}


1.2、经过上面的一番解释,明白了两者的区别,但说了半天这些是用来干嘛用的呢?

其实个人直接理解成,就是接收解析前端发过的来请求体就可以了,node通过使用这个中间件,就可以接收到传过来的参数或内容了(数组或对象),然后方例下面路由对数据进行处理

此处补充一下,关于请求体相关的知识:

HTTP协议:超文本传输协议
基于TCP/IP通信协议来传递数据
基于客户端/服务端(C/S)架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求响应协议

特点:
HTTP是无连接、媒体独立的、无状态的

HTTP请求报文由3部分组成:请求头+请求体+请求行
请求行:是请求方法,get和post是最常见的HTTP方法,除此以外还包括delete、head、options、put、trace;
为请求对应的url地址,它和报文头的host属性组成完整的请求url;
是协议名称及版本号

请求头:是HTTP的报文头,报文头包含若干个属性,格式为:属性名:属性值:,服务器端据此获取客户端的信息;
与缓存相关的规则信息均包含在header中

请求体:是报文体,它将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1¶m2=value2”的方式传递请求参数。

路由对数据进行处理:

在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据

  1. 默认情况下,如果不配置解析表单数据中间件,则 req.body 默认等于 undefined
  2. 通过 express.urlencoded() 这个中间件,,解析表单中的 urlencoded格式的数据
  3. 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据

1.3、补充一下,Express 内置了3个常用的中间件,上面就是其中两个,另外一个就是express.static

官方文档上是这样说的

从应用程序目录中的“public”目录为应用程序提供静态内容:

// GET /style.css etc
app.use(express.static(path.join(__dirname, 'public')))

将中间件装载到“/static”以仅在其请求路径前缀为“/static”时提供静态内容:

// GET /static/style.css etc.
app.use('/static', express.static(path.join(__dirname, 'public')))

通过在静态中间件之后加载记录器中间件,禁用静态内容请求的日志记录:

app.use(express.static(path.join(__dirname, 'public')))
app.use(logger())

提供来自多个目录的静态文件,但优先于“/public”:

app.use(express.static(path.join(__dirname, 'public')))
app.use(express.static(path.join(__dirname, 'files')))
app.use(express.static(path.join(__dirname, 'uploads')))
看了以上总结,那么express的自带的三个中间件,就都明白了文章开头的代码我为什么这样写了,尤其是免去单独使用bodyParser的原因。

<2> 通过上面的几行代码,可以了解到node非常适合开发人员快速作为后端使用

2.1、node有三个特点

  • 1、单线程

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

    ​ Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。另外,单线程的带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。

  • 2、非阻塞I/O non-blocking I/O

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

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

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

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

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

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

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

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

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

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

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

2.2、优点:

    1. 高并发(最重要的优点)
    1. 适合I/O密集型应用
    1. 对服务器要求低,节约硬件资源

2.3、缺点:

  • 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
    解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
  • 只支持单核CPU,不能充分利用CPU;
  • 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
    原因:单进程,单线程
    解决方案:(1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
    (2)开多个进程监听同一个端口,使用cluster模块;
  • 开源组件库质量参差不齐,更新快,向下不兼容
  • Debug不方便,错误没有stack trace
posted @ 2022-05-20 12:07  k19  阅读(118)  评论(0)    收藏  举报