91 # 实现 express 的优化处理

news2025/1/10 23:46:54

上一节实现 express 的请求处理,这一节来进行实现 express 的优化处理

  1. 让 layer 提供 match 方法去匹配 pathname,方便拓展
  2. 让 layer 提供 handle_request 方法,方便拓展
  3. 利用第三方库 methods 批量生成方法
  4. 性能优化问题
    • 进行路由懒加载,当进行 get/post... 请求时,或者 listen 时,才加载 route
    • layer 进行加速匹配,在 layer 层判断是否请求方法在 route 里有

methods 库地址:https://www.npmjs.com/package/methods

[
  'acl',        'bind',        'checkout',
  'connect',    'copy',        'delete',
  'get',        'head',        'link',
  'lock',       'm-search',    'merge',
  'mkactivity', 'mkcalendar',  'mkcol',
  'move',       'notify',      'options',
  'patch',      'post',        'pri',
  'propfind',   'proppatch',   'purge',
  'put',        'rebind',      'report',
  'search',     'source',      'subscribe',
  'trace',      'unbind',      'unlink',
  'unlock',     'unsubscribe'
]

批量生成方法测试 demo 如下

const express = require("./kaimo-express");
const app = express();

app.get("/", (req, res, next) => {
    res.end("get okk end");
});
app.post("/", (req, res, next) => {
    res.end("post okk end");
});

app.listen(3000, () => {
    console.log(`server start 3000`);
    console.log(`在线访问地址:http://localhost:3000/`);
});

控制台执行下面命令:

curl -v -X POST http://localhost:3000/

在这里插入图片描述

layer 进行加速匹配 demo

const express = require("./kaimo-express");
const app = express();

app.post("/", (req, res, next) => {
    res.end("post okk end");
});
app.put("/", (req, res, next) => {
    res.end("put okk end");
});
app.delete("/", (req, res, next) => {
    res.end("delete okk end");
});
app.options("/", (req, res, next) => {
    res.end("delete okk end");
});


app.listen(3000, () => {
    console.log(`server start 3000`);
    console.log(`在线访问地址:http://localhost:3000/`);
});

然后去访问:http://localhost:3000/,可以看到没有优化的时候会请求这些方法

在这里插入图片描述
优化之后可以看到就没有请求了

在这里插入图片描述

优化的代码如下:

application.js

const http = require("http");
const Router = require("./router");
const methods = require("methods");
console.log("methods----->", methods);

function Application() {}

// 调用此方法才开始创建,不是创建应用时直接装载路由
Application.prototype.lazy_route = function () {
    if (!this._router) {
        this._router = new Router();
    }
};

methods.forEach((method) => {
    Application.prototype[method] = function (path, ...handlers) {
        this.lazy_route();
        this._router[method](path, handlers);
    };
});

Application.prototype.listen = function () {
    const server = http.createServer((req, res) => {
        function done() {
            res.end(`kaimo-express Cannot ${req.method} ${req.url}`);
        }
        this.lazy_route();
        this._router.handle(req, res, done);
    });
    server.listen(...arguments);
};

module.exports = Application;

router/index.js

const url = require("url");
const Route = require("./route");
const Layer = require("./layer");
const methods = require("methods");

function Router() {
    // 维护所有的路由
    this.stack = [];
}

Router.prototype.route = function (path) {
    // 产生 route
    let route = new Route();
    // 产生 layer 让 layer 跟 route 进行关联
    let layer = new Layer(path, route.dispatch.bind(route));
    // 每个路由都具备一个 route 属性,稍后路径匹配到后会调用 route 中的每一层
    layer.route = route;
    // 把 layer 放到路由的栈中
    this.stack.push(layer);
    return route;
};

methods.forEach((method) => {
    Router.prototype[method] = function (path, handlers) {
        // 1.用户调用 method 时,需要保存成一个 layer 当道栈中
        // 2.产生一个 Route 实例和当前的 layer 创造关系
        // 3.要将 route 的 dispatch 方法存到 layer 上
        let route = this.route(path);
        // 让 route 记录用户传入的 handler 并且标记这个 handler 是什么方法
        route[method](handlers);
    };
});

Router.prototype.handle = function (req, res, out) {
    console.log("请求到了");
    // 需要取出路由系统中 Router 存放的 layer 依次执行
    const { pathname } = url.parse(req.url);
    let idx = 0;
    let next = () => {
        // 遍历完后没有找到就直接走出路由系统
        if (idx >= this.stack.length) return out();
        let layer = this.stack[idx++];
        // 需要判断 layer 上的 path 和当前请求路由是否一致,一致就执行 dispatch 方法
        if (layer.match(pathname)) {
            // 将遍历路由系统中下一层的方法传入
            // 加速匹配,如果用户注册过这个类型的方法在去执行
            if (layer.route.methods[req.method.toLowerCase()]) {
                layer.handle_request(req, res, next);
            } else {
                next();
            }
        } else {
            next();
        }
    };
    next();
};

module.exports = Router;

route.js

const Layer = require("./layer");
const methods = require("methods");

function Route() {
    this.stack = [];
    // 用来描述内部存过哪些方法
    this.methods = {};
}

Route.prototype.dispatch = function (req, res, out) {
    // 稍后调用此方法时,回去栈中拿出对应的 handler 依次执行
    let idx = 0;
    console.log("this.stack----->", this.stack);
    let next = () => {
        // 遍历完后没有找到就直接走出路由系统
        if (idx >= this.stack.length) return out();
        let layer = this.stack[idx++];
        console.log("dispatch----->", layer.method);
        if (layer.method === req.method.toLowerCase()) {
            layer.handle_request(req, res, next);
        } else {
            next();
        }
    };
    next();
};
methods.forEach((method) => {
    Route.prototype[method] = function (handlers) {
        console.log("handlers----->", handlers);
        handlers.forEach((handler) => {
            // 这里的路径没有意义
            let layer = new Layer("/", handler);
            layer.method = method;
            // 做个映射表
            this.methods[method] = true;
            this.stack.push(layer);
        });
    };
});

module.exports = Route;

layer.js

function Layer(path, handler) {
    this.path = path;
    this.handler = handler;
}

Layer.prototype.match = function (pathname) {
    return this.path === pathname;
};
Layer.prototype.handle_request = function (req, res, next) {
    this.handler(req, res, next);
};
module.exports = Layer;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1024028.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

亚马逊 CodeWhisperer 初体验

1、CodeWhisperer 介绍 CodeWhisperer 是亚马逊出品的一款基于机器学习的通用代码生成器,可实时提供代码建议。类似 Cursor 和 Github Copilot 编码工具。 官网:AI 代码生成器 - Amazon CodeWhisperer - AWS 在编写代码时,它会自动根据您现…

Flink sql 1.17笔记

环境准备 # 启动hadoop集群 # 启动Flink yarn session (base) [link999hadoop102 flink-1.17.0]$ bin/yarn-session.sh -d# 启动finksql客户端 (base) [link999hadoop102 flink-1.17.0]$ bin/sql-client.sh -s yarn-session# 如果有初始化文件 bin/sql-client.sh embedded -s …

数据结构 - 二叉树

文章目录 目录 文章目录 前言 一 . 树型结构 1.1 树的概念(了解) 1.2 数的常用术语 (掌握) 1.3 树的应用(了解) 1.4 树相较于数组和链表的优势(了解) 二 . 二叉树(重点) 2.1 二叉树的概念 2.2 两种特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的基本操作 2.4.1 二叉树的前序遍历 …

高云FPGA系列教程(7):ARM GPIO外部中断

文章目录 [toc]GPIO中断简介FPGA配置常用函数MCU程序设计工程下载 本文是高云FPGA系列教程的第7篇文章。 本篇文章介绍片上ARM Cortex-M3硬核处理器GPIO外部的使用,演示按键中断方式来控制LED亮灭,基于TangNano 4K开发板。 参考文档:Gowin_E…

go学习之函数知识

函数 文章目录 函数1.函数入门(1)为什么需要函数?(2)什么是函数:2.包3.函数的调用机制通俗理解调用过程:return语句递归调用 4.函数注意事项和细节讨论5.init函数6.匿名函数7.闭包8.defer9.函数参数的传递方式10.字符串中常用的函数11.时间和…

JVM中的java同步互斥工具应用演示及设计分析

1.火车站售票系统仿真 某火车站目前正在出售火车票,共有50张票,而它有3个售票窗口同时售票,下面设计了一个程序模拟该火车站售票,通过实现Runnable接口实现(模拟网络延迟)。 伪代码: Ticket类…

Java代码质量评估工具

概述 Java代码的质量评估主要包括代码的可维护性、健壮性、以及在运行时能达到既定的性能目标,可维护性主要包括代码的可读性、在关键的代码上提供详细注释、在设计类、方法以及代码逻辑时符合设定的编码规范,健壮性主要包括编写代码时应使用常用的设计…

【JAVA-Day26】数组解析:什么是数组?如何定义?

数组解析:什么是数组?如何定义? 数组解析:什么是数组?如何定义?摘要一、什么是数组数组的特性:不同类型的数组:数组的应用场景:数组的限制和挑战: 二、如何定…

【论文阅读 07】Anomaly region detection and localization in metal surface inspection

比较老的一篇论文,金属表面检测中的异常区域检测与定位 总结:提出了一个找模板图的方法,使用SIFT做特征提取,姿态估计看差异有哪些,Hough聚类做描述符筛选,仿射变换可视化匹配图之间的关系&#xf…

【算法基础】数学知识

质数 质数的判定 866. 试除法判定质数 - AcWing题库 时间复杂度是logN #include<bits/stdc.h> using namespace std; int n; bool isprime(int x) {if(x<2) return false;for(int i2;i<x/i;i){if(x%i0) return false;}return true; } signed main() {cin>&g…

Git学习笔记1

任务要求&#xff1a; 1、使用git提交代码到仓库&#xff1b; 2、实现自动代码发布系统&#xff1b; 1、了解DevOps的发展历程和思想&#xff1b; 2、学会git版本控制&#xff1b; 3、会使用github公有仓库和gitlab私有仓库&#xff1b; 4、了解CI/CD&#xff1b; 5、使用…

svn(乌龟svn)和SVN-VS2022插件(visualsvn) 下载

下载地址: https://www.visualsvn.com/visualsvn/download/

Go的error接口

从本书的开始&#xff0c;我们就已经创建和使用过神秘的预定义error类型&#xff0c;而且没有解释它究竟是什么。实际上它就是interface类型&#xff0c;这个类型有一个返回错误信息的单一方法&#xff1a; type error interface { Error() string } 创建一个error最简单的方…

cutree 算法

传播 ​ 由于块与块之间具有参考关系&#xff0c;提升被参考块的质量&#xff0c;可以改善后续参考块的质量 ​ Pn1帧中CU0,1完全参考Pn的CU1,1。且Pn1帧中CU0,1块帧内预测和帧间预测的代价分别为 c x , y n 1 ( 0 , 0 ) c_{x,y}^{n1}(0,0) cx,yn1​(0,0)和 c x , y n 1 ( d…

vue获取本地缓存并转为json格式

场景 要求获取当前登录用户id&#xff0c;传入后台去筛选属于该用户的数据&#xff1b; 当前登录用户信息一般会在本地存储中&#xff0c;有些则是在session中&#xff0c;此处只对本地存储做讨论&#xff1b; 本地缓存的用法 1 存储数据 localStorage.setltem(userId,"…

【版本控制】Github和Gitlab同时使用ssh

前言 最近在使用 WSL 时会同时用到 GitHub和 Gitlab &#xff0c;因此与传统配置 ssh 方式有些不一样的地方&#xff0c;这里特别记录一下 本地生成公私密钥 首先确保把之前的 ssh 信息清除&#xff0c;也可以将整个 ~/.ssh 目录删除 rm -rf ~/.ssh/*我们分别生成 Github 和…

Bigemap如何添加谷歌历史影像

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 很多粉丝私信都在问怎么才可以看到谷歌的历史影像&#xff0c;其实这个图源目前是没有对大陆网络ip进行开放&#xff0c;所以如果需要查看&#xff0c;也是需要看你当前…

阿里云产品试用系列-Serverless 应用引擎 SAE

Serverless 应用引擎 SAE&#xff08;Serverless App Engine&#xff09;是一个全托管、免运维、高弹性的通用 PaaS平台。SAE 支持 Spring Boot、Spring Cloud、Dubbo、HSF、Web 应用和 XXL-JOB、ElasticJob任务的全托管&#xff0c;零改造迁移、无门槛容器化、并提供了开源侧诸…

【JAVA-Day23】Java反射的五大神奇妙用,令人惊叹

Java反射的五大神奇妙用&#xff0c;令人惊叹 Java反射的五大神奇妙用&#xff0c;令人惊叹摘要引言一、什么是反射?一、什么是反射?1.1 为什么需要反射?1.1.1 动态加载类1.1.2 序列化和反序列化1.1.3 框架和库开发 1.2 反射基础 二、类2.1 类完整路径2.2 包路径2.3 类名2.4…

乐鑫科技全球首批支持蓝牙 Mesh Protocol 1.1 协议

乐鑫科技 (688018.SH) 非常高兴地宣布&#xff0c;其自研的蓝牙 Mesh 协议栈 ESP-BLE-MESH 现已支持最新蓝牙 Mesh Protocol 1.1 协议的全部功能&#xff0c;成为全球首批在蓝牙技术联盟 (Bluetooth SIG) 正式发布该协议之前支持该更新的公司之一。这意味着乐鑫在低功耗蓝牙无线…