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, 采用将服务器响应对象传递给请求处理程序, 在请求处理程序中返回响应的方式

姊妹篇见->Node.js 实现的简易服务器 (一) (非阻塞处理存在问题)

posted @ 2020-10-16 15:42  ayuuuuuu  阅读(103)  评论(0编辑  收藏  举报