zoukankan      html  css  js  c++  java
  • express源码剖析1

    什么是中间件?

         中间件是模拟现实中的一个策略,可以把一个http看作是污水处理流,中间件就像是一层层的过滤网。每个中间件在http处理过程中通过改写request或(和)response的数据、状态,实现了特定的功能。

    1.EventEmitter.prototype

    mixin(app, EventEmitter.prototype, false);

         app为一个函数,也是对象,mixin是一个类库(merge-descriptors)。它就是一种mixin设计模式,作用是让app这个对象具有EventEmitter.prototype的方法。第三个参数表示“是否重新定义app中与EventEmitter.prototype中存在重名的方法。

    EventEmitter类是nodejs中event的一个类,也是唯一类。它的核心是对事件触发与事件监听功能的封装。

    EventEmitter实例的产生?

         大多数 Node.js 核心 API 都是采用惯用的异步事件驱动架构,其中某些类型的对象(称为触发器)会周期性地触发命名事件来调用函数对象(监听器)。

    Node.js里面的许多对象都会分发事件:一个net.Server对象(创建TCP或本地服务器)会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。   

    EventEmitter类的使用

    1.1 newListener事件

       当注册一个事件时,触发newListener事件,例如:

    var MyEmitter = require("events");
    const myEmitter = new MyEmitter();
    // Only do this once so we don't loop forever
    //当新的监听器被添加时,所有的 EventEmitter 会触发 'newListener' 事件;
    //当移除已存在的监听器时,则触发 'removeListener'。
    myEmitter.once('newListener', (event, listener) => {
      if (event === 'event') {
        // Insert a new listener in front
        myEmitter.on('event', () => {
          console.log('B');
        });
      }
    });
    myEmitter.on('event', () => {
      console.log('A');
    });
    myEmitter.emit('event');
    //B
    //A

    1.2 EventEmitter.defaultMaxListeners

      唯一的静态成员,默认值是10,表示对应监听同一的事件个数是10个,不建议修改这个参数,否则会影响到所有EventEmitter实例的。

    1.3 EventEmitter的prototype对象

    var MyEmitter = require('events');
    console.log(MyEmitter.prototype); 
    //结果是:
    EventEmitter {
      domain: undefined,
      _events: undefined,          
      _maxListeners: undefined,
      setMaxListeners: [Function: setMaxListeners],
      getMaxListeners: [Function: getMaxListeners],
      emit: [Function: emit],
      addListener: [Function: addListener],
      on: [Function: addListener],
      once: [Function: once],
      removeListener: [Function: removeListener],
      removeAllListeners: [Function: removeAllListeners],
      listeners: [Function: listeners],
      listenerCount: [Function: listenerCount] }
    [Finished in 0.3s] 

    app对象(函数)它具有以上方法,即app继承了EventEmitter的原型对象,值得注意的是:大部分模块继承自Event模块。

    在express中的应用中,app对象通过on绑定了mount事件,如下:

    //这个函数的重点是parent参数
    this.on('mount', function onmount(parent) {
        // inherit trust proxy
        if (this.settings[trustProxyDefaultSymbol] === true
          //parent.settings['trust proxy fn'] = compileTrust(val)
          && typeof parent.settings['trust proxy fn'] === 'function') {
          delete this.settings['trust proxy'];
          delete this.settings['trust proxy fn'];
        }
        // inherit protos
        this.request.__proto__ = parent.request;
        this.response.__proto__ = parent.response;
        this.engines.__proto__ = parent.engines;
        this.settings.__proto__ = parent.settings;
      });

    下面是利用到的中间件

    array-flatten

    有一个函数flattern,它的作用是把数组从多维变成一维,例如:

    var flatten = require('array-flatten');
    var arr = [1,2,5,[1,3]];
    console.log(flatten(arr));
    //[1,2,5,1,3]

    finalhandler

    finalhandler的作用就是一个http请求的最后一步的处理方式,

    var finalhandler = require('finalhandler');
    /*从handler传递过来的callback为undefined,
       finalhandler返回一个函数,这个函数可以出发done(err),
       如果err为false,它将在res写入404,否则,会在res中写入错误信息。
    */
      var done = callback || finalhandler(req, res, {
        env: this.get('env'),
        onerror: logerror.bind(this)
      });

    parseUrl

    
    

    var req1 = {
    url: "http://localhost:4000/m/xx/login.html?kf=33",
    };

    
    

    var str = parseUrl(req1);
    console.log("str=",str);

    //str= Url {

    protocol: 'http:',
    slashes: true,
    auth: null,
    host: 'localhost:4000',
    port: '4000',
    hostname: 'localhost',
    hash: null,
    search: '?kf=33',
    query: 'kf=33',
    pathname: '/m/xx/login.html',
    path: '/m/xx/login.html?kf=33',
    href: 'http://localhost:4000/m/xx/login.html?kf=33',
    _raw: 'http://localhost:4000/m/xx/login.html?kf=33' }

     

    类似url包中parse方法

    var url = require('url');
    var testStr = "http://www.sina.com//xx/y/z.html?ss=1&s=2";
    //第二个参数为false,返回对象中query为字符串。第三个参数看文档测试没什么鸟用
    console.log(url.parse(testStr,false,true));
    console.log(url.parse(testStr,true,true));
    console.log(url.parse(testStr,true,false));
    /*
    Url {
      protocol: 'http:',
      slashes: true,
      auth: null,
      host: 'www.sina.com',
      port: null,
      hostname: 'www.sina.com',
      hash: null,
      search: '?ss=1&s=2',
      query: 'ss=1&s=2',
      pathname: '//xx/y/z.html',
      path: '//xx/y/z.html?ss=1&s=2',
      href: 'http://www.sina.com//xx/y/z.html?ss=1&s=2' }
    Url {
      protocol: 'http:',
      slashes: true,
      auth: null,
      host: 'www.sina.com',
      port: null,
      hostname: 'www.sina.com',
      hash: null,
      search: '?ss=1&s=2',
      query: { ss: '1', s: '2' },
      pathname: '//xx/y/z.html',
      path: '//xx/y/z.html?ss=1&s=2',
      href: 'http://www.sina.com//xx/y/z.html?ss=1&s=2' }
    Url {
      protocol: 'http:',
      slashes: true,
      auth: null,
      host: 'www.sina.com',
      port: null,
      hostname: 'www.sina.com',
      hash: null,
      search: '?ss=1&s=2',
      query: { ss: '1', s: '2' },
      pathname: '//xx/y/z.html',
      path: '//xx/y/z.html?ss=1&s=2',
      href: 'http://www.sina.com//xx/y/z.html?ss=1&s=2' }
    */

    pathRegexp

    在Layer.js中利用到。path-to-regexp是express的路由规则,初始化pathRegexp有三个参数:path(可以是正则表达式),key(对path中关键字填充的数组)和options,options包括三个参数,

    分别是end,caseSensitive和strict,具体可以参考http://blog.csdn.net/chszs/article/details/51055229

    var pathRegexp = require('path-to-regexp');
    /*
      var layer = Layer('/', {}, handle);
      this.keys是对path中参数的一种解释。如path="/user/:foo",那么keys=  [name:"foo",delimiter:"false"]
    */
    this.regexp = pathRegexp(path, this.keys = [], opts);
    // end为false时,正则从path开始进行匹配,相当于^
      if (path === '/' && opts.end === false) {
        this.regexp.fast_slash = true;
      }

     看下面一个例子:

    function test(){
      var keys = [],
          opts= {
            "sensitive":false,
            "strict":false,
            "end":false
          },
          regexp = pathRegexp("/user/:id/:page/", keys = [], opts);
      var m = regexp.exec("/user/anthonyliu/your");
      console.log("m="+JSON.stringify(m));
    } 
    test(); 
    //第一个元素是输入的路径,后面都是匹配的分组!
    //m=["/user/anthonyliu/your","anthonyliu","your"]

    compileQueryParser

    var compileQueryParser = require('./utils').compileQueryParser;
    compileQueryParser("extended");

    qs是express依赖的包
    他只提供两个方法:parse,和stringify,qs.parse作为回调函数传递给middleware/query。query返回一个函数,给rounter.use调用。

    /*
      middlemare/query,返回该函数,给rounter.user
    */
    return function query(req, res, next){
        if (!req.query) {
          var val = parseUrl(req).query;
          req.query = queryparse(val, opts);   
        }
        next();
      };
    };
  • 相关阅读:
    QQ视频直播架构及原理 流畅与低延迟之间做平衡 音画如何做同步?
    边缘推流与中心推流对比
    推流协议 支持RTMP协议推流
    改变原型
    window.onbeforeunload 埋点 页面停留时间
    修改/etc/hosts 云服务器 没有做外网转内网的优化
    :nohlsearch
    z waiting to receive.**B0100000023be50
    Powered by Flink
    负载均衡
  • 原文地址:https://www.cnblogs.com/liuyinlei/p/6257572.html
Copyright © 2011-2022 走看看