Node.js 实现的简易服务器 (一) (非阻塞处理存在问题)
index.js
const server = require('./server');
const router = require('./router');
server.start(router.route);
server.js
const http = require('http'); // Node.js 内置的 http 模块
const url = require('url'); // Node.js 内置的 url 模块
function start(route, port=8080) {
/**
* 请求事件发生时调用的回调函数 (Node.js 是执行单线程异步非阻塞事件驱动的)
*/
function requestListener(req, resp) {
let pathname = url.parse(req.url).pathname;
console.log(`Request for ${pathname} received.`);
let content = route(pathname);
resp.writeHead(200, { 'Content-Type': 'text/html' }); // 响应头
resp.write(content); // 响应主体
resp.end(); // 完成响应
}
/**
* 创建一个基础的 HTTP 服务器
*
* 请求 http 模块提供的函数 createServer 创建一个服务器对象, 并调用该对象的 listen 方法监听 8080 端口
*
* See Also:
* createServer 函数的源码解析: https://blog.csdn.net/THEANARKH/article/details/88385964
* Node.js 是事件驱动: https://segmentfault.com/a/1190000014926921
*/
http.createServer(requestListener).listen(port);
console.log(`Server has started on ${port}`);
}
module.exports = {
start
}
router.js
const requestHandlers = require('./requestHandlers');
// 路由映射/注册
const handler = {
'/': requestHandlers.start,
'/start': requestHandlers.start,
'/upload': requestHandlers.upload
};
// 路由处理
function route(pathname) {
console.log(`About to route a request for ${pathname}`);
let handle = handler[pathname];
if (typeof handle === 'function') {
let result = handle();
console.log(`${handle.name} was called`);
return result;
} else {
console.log(`No request handle found for ${pathname}`);
return '404 Not Found';
}
}
module.exports = {
route
}
requestHandlers.js
function start() {
const exec = require('child_process').exec;
let result = 'empty';
exec(`echo 'hello'`, function (error, stdout, stderr) {
result = stdout;
console.log(result);
});
return result;
}
function upload() {
return 'upload';
}
module.exports = {
start,
upload
}
特点
- 请求处理程序进行阻塞操作时, 会阻塞其他请求的处理
(原因: 主(执行)线程被阻塞代码阻塞后, 其余所有请求必须等待该阻塞代码处理完毕之后才能执行)
缺点
- 请求处理程序进行非阻塞操作时, 无法正确返回响应内容
(原因: 请求处理程序是以阻塞方式运行的, 非阻塞代码的回调函数还未执行获取到响应内容, 请求已经返回了, 故start()
请求处理函数始终返回empty
)
总结
该服务器分为三层:
- server - 用于启动服务器, 接收请求与返回响应
- router - 用于路由注册和路由处理
- requestHandlers - 用于编写业务逻辑, 实现各个请求处理函数
各层之间的通信顺序为: req -> server -> router -> requestHandlers -> router -> server -> resp, 其中 server 返回的 resp 依赖于 requestHandlers 请求处理程序的返回内容 (采用将内容传递给服务器的方式), 这就导致了如果请求处理程序中存在非阻塞代码获取返回结果时 (如上述代码中的 child_process.exec 异步非阻塞执行命令行命令) , 响应内容就会与我们期望不符 (解决方案见->Node.js 实现的简易服务器 (二))