zoukankan      html  css  js  c++  java
  • node.js 框架基本功能

    这个框架从2018年1月左右开始开发,主要参考资料是《Nodejs:摆脱黑工坊发展出一款基础企业级框架》和阿里开源的 egg.js ,核心库是 koa@2。一开始用一两周的时间开发了原型,后来在业务的不断开发中,框架也不断的进行着更新。

    现在主要有下面的特性:

    负载均衡

    负载均衡使用的是 pm2,每台机器都跑着和 cpu 数量相同的进程数。沿用了之前的项目,同时也沿用了之前的项目不用 package.json 中的 script 来启动和停止项目,而是另外写了 shell.cfg 配置文件和 deploy.sh 部署脚本来启停项目的做法。之前这种做法是因为最开始的项目用这种方式做了很多配置,现在的项目不需要这种配置,所以后面其实可以放弃这种复杂的做法,直接使用 script 唤起 pm2 来进行进程管理了。

    统一响应格式

     每个响应的格式都是{ code: 0 | 1, data: data, msg: '' },用 code 来确定请求成功还是失败,成功为0,失败为1或者其它值,data 中设置响应的数据体, msg 为提示信息。

    日志

    日志使用的是 log4js。因为使用了 pm2 ,内网机器又不能安装 pm2 的插件,因此不能使用最新版本的 log4js。用的是古老的 1.1.1 版本,配置方式没有最新版本的结构明晰,但是能正常输出日志了。日志分为请求期间的详细日志 app.log 和请求结束时的总结日志 httpLog.log,此外还有按月归档的错误级别日志 errors_2019.01.log。
     
    请求期间的日志:在每个请求进来的时候设置一个 ctx.sessionId(用的是 uuid/v1),再把 logger.info 改造成 logger.info(sessionId, msg, [msg2]) 这样的形式,使每一个 log 都带上了 sessionId,这样就实现了每个请求中的所有日志输出都会带有一个统一的 id,方便搜索日志,排查问题了。另外,通过 console.log = ctx.logger.info 这样的赋值,使代码里面的所有 console 都能直接使用 log4js 的格式输出日志了。这样就既能方便的使用 log,又不用到处引入 log4js 了。
     
    请求结束的日志:得益于 koa@2 优秀的中间件执行模型,可以在最开始的中间件的 await next() 之后设置一个专门的日志 httpLog 来单独记录本次 http 请求的结果情况。这个日志有固定的格式,一开始是通过空格分割开的各个字段组成的字符串,类似 sessionId remoteAddr method originalUrl 这样。后来发现因为 remoteAddr 和 userAgent 中间也有空格,非常影响对日志进行数据分析,就改成了数组的形式,如下 [sessionId, remoteAddr, httpVersion, method, originalUrl, referer, status, responseTime, response.length, user-agent],再用 JSON.stringify 格式化一下,就可以得到可读性和可处理性良好的日志了。甚至如果冒险使用 eval 这样的黑科技的话,能直接当做数组使用。
     
    动态导入 controller 和 service

    使用 loader.js 自动化的导入所有的 controller 和 service 模块,统一挂载到 app.controller 和 requestHandle(ctx, svs, next) 的 svs 上。自动导入的文件必须写在 controller 或者 service 文件夹中,有固定的导出格式,这样统一了工程的文件结构。 controller 和 service 文件夹支持两层目录结构,可以把同一个业务的相关文件放在同一个子文件夹中,方便查看。 controller 模块的导出既支持直接导出函数对象,也支持导出类,推荐导出类的方式,因为这样可以更方便的使用框架提前为类挂载的各种有用功能,比如使用类的话可以在 service 函数中直接用 this.app 获取到 app 实例,而不用从 controller 中再传递过来。
     
    统一 router 配置入口

    所有的 router 都在同一个 router.js 文件中,包括通用接口和业务接口。这样的好处是不用再到处翻文件去找一个接口的 router 到底定义在哪里,也不会发生几个人做了同样功能通用接口的情况。
     
     

    应用生命周期


    给应用添加了 beforeStart 和 afterStart 的生命周期钩子,可以在应用的启动前后执行指定的任务。在实践中,beforeStart 任务列表中添加了获取 mongodb 账号密码的任务,连接 mongodb 的任务,afterStart 中添加了定时任务和获取平安开放平台 access_token 的任务。
    添加生命周期不是非常必要,完全可以在项目启动文件中添加各个任务,但是这样一来既会导致启动文件中掺入太多业务代码,又难以清晰的观察到项目启动前后做的各种任务,因此添加了应用生命周期概念,在这个范畴中处理项目启动前后的任务。
     
     

    自动设置对应环境中的 config


    config 的文件一共包括三类: 1是 config.default.js,定义了所有环境的通用配置,2是 config.dev.js、config.stg.js、config.prd.js 这一系列文件,定义了各个环境中的独特配置,3是 config.js 文件,功能是根据应用所在环境,自动 merge 对应环境的配置和 default 的配置。
    这样的文件安排既可以快速的添加通用配置,又可以为不同的环境添加独特的配置,同时还提供了清晰的配置文件结构。唯一的确定可能就是文件有点多了,不过所有配置都只有一个唯一出口 config.js ,因此用起来还是挺方便的。
     
     

    数据压缩


    使用 koa-compress,对于返回数据大于 2kb 的启用压缩。在生产实践中,通过优化接口返回数据和压缩,把一个两百多 kb 的数据优化到了十几 kb,最终接口返回速度由几秒钟优化到了平均几毫秒。
     
     

    安全 header 设置


    使用koa-helmet来为 response 添加安全设置,提供一定的安全防护。
     
     

    跨域设置


    使用 koa-cors 提供跨域支持。在开发和测试环境启用全部接口跨域支持,方便调试和测试。生产环境不开启全部跨域,根据 config 的配置,对特定路由开启跨域。
     
     

    定时同步数据


    我们的应用分布在不同的主机上,相互之间除了使用了同一个数据库之外毫无关联,我不想把同一份数据使用所有的进程都同步一遍,于是多个独立进程架构下的单进程数据库同步方案这篇文章所言,我设计了一个借助数据库来实现只会有一个进程执行同步任务的方案。这个方案完全自治并且支持无缝扩展,完美解决了困扰我很长时间的单进程同步问题。
     
     

    快速热更新系统设置


    有时一些系统设置需要更新,但是又不到重新部署系统的地步,这里就可以通过预留的接口来更新数据库中的配置,之后每个进程中的短时间定时任务就会很快的获取到数据库中的配置,系统配置就更新了。
     
     

    功能开关设置


    一些特定的任务可能有时需要关掉,这时就可以通过上面的快速热更新系统设置来快速的更新系统配置,根据业务流程中的开关设置,就可以控制任务的启停了。这个设置需要侵入到业务代码中,在每个需要设置的功能模块中手动添加开关。
     
     

    缓存功能


    自研了一个轻量、完备的缓存模块,为数据不常变动的接口数据提供缓存功能。使用缓存能极大的提高系统的并发能力。
     
     

    数据库自动挂载


    把数据库和各个表的实例挂载到了 app 上,不用再每个文件都再引入表实例。
     
     

    应用错误捕获和请求错误拦截、记录


    使用了 process.on('uncaughtError', err) 和 process.on('unhandledRejection', err) 来分别处理未捕获的错误和未处理的 rejected promise。在请求生命周期的最后使用中间件拦截系统错误,在开发和测试环境返回完整错误记录,生产环境返回错误提示。使用分级日志记录错误信息。
     
     

    未来要实现的功能:

     

    接口安全访问


    通过对接平安开放平台,了解到了一个加强接口安全访问的方案。前端请求参数中统一添加时间戳,然后获取所有接口入参的 md5 数据,把 md5 数据也发送到后台,这样后台就能通过对 md5 的校验来检查数据是否有被篡改。这个方案能够加强接口数据的安全性,但是获取接口入参的 md5 数据需要消耗 cpu 资源。
    另外一个方案是在一个业务流程中,首先要获取一个 requestId,后续流程中所有的请求都要在入参中加上这个 id。通过这个方案能够快速的识别出没有 requestId 的请求为无效请求,但是对于精心伪造的类似请求就需要查询数据库了,这里就需要再加入一个 redis 作为一个公共的第三方缓存,会增加数据读取和网络请求时间。
     
     
     
     
     
     
     
     
  • 相关阅读:
    Linq查询
    Lambda表达式与标准运算符查询
    第四章 面向对象与IO操作
    第三章 C#循环与方法
    第二章 C#基本数据类型
    FPGA与嵌入式一点见解
    FPGA中RAM使用探索
    可控硅的工作原理和主要作用
    异步电路中时钟同步的方法
    FPGA中计数器设计探索
  • 原文地址:https://www.cnblogs.com/lswit/p/10239947.html
Copyright © 2011-2022 走看看