zoukankan      html  css  js  c++  java
  • 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 实现的简易服务器 (二))

  • 相关阅读:
    从获取QQ验证码谈如何改进用户体验,提高程序的响应效果
    如何利用C#批量注册QQ邮箱
    利用DotRAS组件,实现ADSL的自动拨号断网自动化操作
    探讨如何利用C#登录QQ邮箱进行群邮件的发送
    利用C#开发基于snmpsharpnet基础的SNMP开发应用
    QQ窗口抓取及如何进行自动化操作
    对比三种GoogleMap图标操作处理,谈如何构造快速响应的GoogleMap图标叠加操作
    Winform下的地图开发控件(GMap.NET)使用心得之三批量解析地址经纬度坐标
    基于Lumisoft.NET实现的邮件发送功能
    谈谈数据加密的处理提供各种算法处理
  • 原文地址:https://www.cnblogs.com/ayuuuuuu/p/13826315.html
Copyright © 2011-2022 走看看