Node.js是独立的运行时,为了JavaScript代码运行在服务器上,提供了JavaScript语言引擎和一系列API,其中许多允许应用与底层操作系统和外部世界进行通信。
JavaScript的单线程特性
JavaScript与大多数语言类似,是重函数的,一个一个组成功能模块,调用,放入函数栈中,如果不小心写了个无限递归调用,会出现超过最大调用栈的问题。
注意:
通常情况下,不需要担心会出现超过调用栈的问题,V8引擎的可容纳数是15000个
与其它语言不同,JavaScript在应用的生命周期中不会限制自己使用单一的函数调用栈
JavaScript的并发处理:通过Node.js的事件循环处理。
大多数情况下JavaScript应用中不允许在同一时刻出现两个函数调用栈,这意味着应用程序如果需要同时运行多个副本,需要进行应用程序扩展。
在node.js中,方法在将来新的调用中被调用称为异步调用,一个方法调用另一个方法在同一个调用栈中被调用称为同步调用。
node.js底层由C++编写,node.js可以使用多线程运行,其中包括第三方工具libuv,可以处理系统操作、I/O操作和一些第三方模块,只有最上层是用JavaScript编写的。
Node.js事件循环
JavaScript运行在浏览器和JavaScript运行在Node.js这二者都需要实现事件循环,它们的相似之处是都在独立的调用栈中异步执行,不同之处是前者一般用在浏览器单页面应用,后者是运行在服务端。
理解基本的事件循环有利于对整个程序的调度,错误的理解事件循环会非常损耗性能。
事件循环
运行后不会终止,循环的运行,可以控制一系列事件的回调方法何时触发,推动应用程序队列的执行,事件循环相关API提供了JavaScript访问底层C++相关控制。
事件循环的几个阶段
1、轮询(Poll)
轮询阶段会执行I/O回调方法,当主程序启动,这阶段便启动
2、检查(Check)
在这个阶段回调方法会被setImmediate()方法触发执行
3、关闭(Close)
这个阶段通过执行EventEmitter触发关闭的回调方法,例如关闭TCP服务,发送关闭事件,运行关闭这个回调方法
4、定时器(Timers)
这个阶段可以使用setTimeout() 和 setInterval()调整回调时序
5、挂起(Pending)
特殊的系统运行阶段,出现错误情况下会挂起,比如TCP出现连接错误比如解决连接,
小贴士
搭建Node.js应用时,不用在意事件循环的细节,许多情况下都只是工作而已。
不用担心回调方法是否在第一时间被执行
不要在单一栈里面执行过多代码,会导致事件循环的停滞,影响其他方法的调用
Demo--食谱
两部分,第一部分:recipe-API,不能被外部访问,可以被内部的服务或者应用程序访问。
第二部分:web-API公开的服务,可以通过HTTP请求被第三方访问,这部分可以访问recipe-API。
大致结构如下图
producer_http_basic.js
//fastify创建服务
const server = require('fastify')()
const HOST = process.env.HOST || '127.0.0.1'
const PORT = process.env.PORT || 4000
console.log(`work pid =${process.pid}`);
server.get('/recipe/:id', async(req, reply) => {
console.log(`work pid =${process.pid}`);
const id = Number(req.params.id)
if (id !== 42) {
reply.statusCode = 404
return {error: 'not found'}
}
return {
producer_id:process.pid,
recipe: {
id, name: "Chicken Tikka Masala",
steps: "Throw it in a pot...",
ingredients: [
{ id: 1, name: "Chicken", quantity: "1 lb", },
{ id: 2, name: "Sauce", quantity: "2 cups", }
]
}
}
})
server.listen(PORT, HOST, () => {
console.log(`running at http:${HOST}${PORT}`);
})
consumer-http-basic.js
const server = require('fastify')();
const fetch = require('node-fetch'); //2.6版本
const HOST = process.env.HOST || '127.0.0.1'
const PORT = process.env.PORT || 3000
const TARGET = process.env.TARGET || 'localhost:4000'
server.get('/', async() => {
const req = await fetch(`http://${TARGET}/recipe/42`)
const producer_data = await req.json()
return {
consumer_pid: process.pid,
producer_data
}
})
server.listen(PORT, HOST, () => {
console.log(`Consumer running at http://${HOST}:${PORT}/`);
})
请求3000端口,内部的根请求会请求4000端口,返回结果如下
一个简单的分布式服务器就搭建完成了。