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.`);
route(pathname, resp);
}
/**
* 创建一个基础的 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.index,
'/index': requestHandlers.index,
'/start': requestHandlers.start,
'/upload': requestHandlers.upload,
};
// 路由处理
function route(pathname, resp) {
console.log(`About to route a request for ${pathname}`);
let handle = handler[pathname];
if (typeof handle === 'function') {
handle(resp);
console.log(`${handle.name} was called`);
} else {
console.log(`No request handle found for ${pathname}`);
requestHandlers.NotFound(resp);
}
}
module.exports = {
route
}
requestHandlers.js
const fs = require('fs');
function index(resp) {
resp.writeHead(200, { 'Content-Type': 'text/html' });
fs.readFile('./index.html', function (err, data) {
resp.write(data);
resp.end();
});
}
function start(resp) {
const exec = require('child_process').exec;
let result = 'empty';
setTimeout(function() {
exec(`echo 'hello'`, function (error, stdout, stderr) {
result = stdout;
console.log(result);
resp.writeHead(200,);
resp.write(result);
resp.end(); // 等待事件循环调用回调函数拿到结果后才完成响应
});
}, 10000);
}
function upload(resp) {
resp.writeHead(200, { 'Content-Type': 'text/plain' });
resp.write('Hello Upload');
resp.end();
}
function NotFound(resp) {
resp.writeHead(200, { 'Content-Type': 'text/html' });
resp.write(`<h1>NOT FOUND</h1>`);
resp.end();
}
module.exports = {
index,
start,
upload,
NotFound
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demo</title>
</head>
<body>
<form action="/upload" method="post">
<h1>Hello World</h1>
</form>
</body>
</html>
特点
- 请求处理程序进行阻塞操作时, 会阻塞其他请求的处理
(原因: 主(执行)线程被阻塞代码阻塞后, 其余所有请求必须等待该阻塞代码处理完毕之后才能执行) - 请求处理程序进行非阻塞操作时, 可以正确返回响应内容
(原因: 请求处理程序是以阻塞方式运行的, 非阻塞代码的回调函数执行时通过 resp 对象返回正确的响应内容)
总结
该服务器分为三层:
- server - 用于启动服务器, 接收请求与返回响应
- router - 用于路由注册和路由处理
- requestHandlers - 用于编写业务逻辑, 实现各个请求处理函数
各层之间的通信顺序为: req -> server -> router -> requestHandlers -> router -> server -> resp, 采用将服务器响应对象传递给请求处理程序, 在请求处理程序中返回响应的方式