背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效。
已读完书籍:《架构简洁之道》。
当前阅读周书籍:《深入浅出的Node.js》。
产品化
项目工程化
工程化,可以理解为项目的组织能力。体现在文件上,就是文件的组织能力。对于不同类型的项目,其组织方式也有所不同。除此之外,还应当有能够将整个项目串联起来的灵魂性文件。
在项目工程化过程中,最基本的几步是目录结构、构建工具、编码规范和代码审查等。
目录结构
目前,主要的两类项目为Web应用和模块应用。
普通的模块应用遵循CommonJS的模块和包规范即可。对于Web应用,组织方式有各种各样,但是只要遵循单一原则即可。
常见的Web应用都是以MVC为主要框架的,其余部分在这个基础上进行扩展。下面是某个Web应用项目:
$ tree -L 2
.
├── History.md // 项目改动历史
├── INSTALL.md // 安装说明
├── Makefile // Makefile文件
├── benchmark // 基准测试
├── controllers // 控制器
├── lib // 没有模块化的文件目录
├── middlewares // 中间件
├── package.json // 包描述文件,项目依赖项配置等
├── proxy // 数据代理目录,类似MVC中的M
├── test // 测试目录
├── tools // 工具目录
├── views // 视图目录
├── routes.js // 路由注册表
├── dispatch.js // 多进程管理
├── README.md // 项目说明文件
├── assets // 静态文件目录
├── assets.json // 静态文件与CDN路径的映射文件
├── bin // 可执行脚本
├── config // 配置目录
├── logs // 日志目录
└── app.js // 工作进程
在部署项目时,我们通过npm install命令安装package.json中配置的依赖文件时,会自动生成这个目录。
构建工具
要想真正能用上源代码,还需要一定的操作,这些操作主要有合并静态文件、压缩文件大小、打包应用、编译模块等。
为了节约资源,此类工作交给工具来完成比较合适,而构建工具就是完成此类需求的。
在Node的应用中,主流的构建工具还是老牌的make,但它的缺点是只在*nix操作系统下有效。为了实现跨平台,Grunt应运而生。Grunt通过Node写成,借助Node的跨平台能力,实现了很好的平台兼容性。
编码规范
为团队统一良好的编码风格,有助于帮助提升代码的可读性,进而提升可维护性。建议在项目一开始就制定基本的编码规范,让团队形成统一的风格。
编码规范的统一一般有几种实现方式,一种是文档式的约定,一种是代码提交时的强制检查。前者靠自觉,后者靠工具。
在JSLint和JSHint工具的帮助下,现在已经能够很好地配置规则了。一旦团队约定了编码规范的详细规则,则可以生成一份规则文件。一些扫描工具或者编辑器能够通过该规则文件对源码进行扫描,直接提示开发者问题所在。
代码审查
代码审查建立在具体的代码提交过程中。目前,开源社区大多通过GitHub实现代码托管。
git的分支开发模式非常灵活,非常利于分布式开发。开发者可以很容易地从主干迁出代码,然后进行功能的开发,待开发完毕后,提交回主干,发起合并请求即可。
部署流程
代码在完成开发、审查、合并之后,才会进入部署流程。在测试环境中测试之后才允许进入生产环境进行线上测试。
部署环境
我们将普通测试环境称为stage环境,预发布环境称为pre-release环境,实际的生产环境称为product环境。
通常会准备测试环境来供开发或者测试人员验证代码的改动是否正确。测试环境中的数据集在种类或者大小上不能够满足测试需求,进而需要在一个预发布环境中测试。预发布环境与普通的测试环境的差别在于它的数据较为接近线上真实的数据。
部署操作
为了能让进程持续执行,我们可能会用到nohup和&以不挂断进程的方式执行:
nohup node app.js &
性能
对于Web应用而言,提升性能最直接有效的莫过于动静分离、多进程架构、分布式,其中涉及的几个拆分原则如下所示。
- 做专一的事。
- 让擅长的工具做擅长的事情。
- 将模型简化。
- 将风险分离。
- 此外,缓存也能带来很大的性能提升。
动静分离
将图片、脚本、样式表和多媒体等静态文件都引导到专业的静态文件服务器上,让Node只处理动态请求即可。
将动态请求和静态请求分离后,服务器可以专注在动态服务方面,专业的CDN会将静态文件与用户尽可能靠近,同时能够有更精确和高效的缓存机制。
启用缓存
如果产品需要应对巨大的流量,启用缓存并应用好它,是系统性能瓶颈的关键。
多进程架构
通过多进程架构,不仅可以充分利用多核CPU,更是可以建立机制让Node进程更加健壮,以保障Web应用持续服务。
由于Node是通过自有模块构建HTTP服务器的,所以需要开发者自己处理多进程的管理。不过好在官方已经有cluster模块,在社区也有pm、forever、pm2这样的模块用于进程管理。
读写分离
就任意数据库而言,读取的速度远远高于写入的速度。
某些系统为了提升性能,通常会进行数据库的读写分离,将数据库进行主从设计,这样读数据操作不再受到写入的影响,降低了性能的影响。
日志
在健全的系统中,完善的日志记录最能够还原问题现场。通过记录日志来定位问题是一种成本较小的方式。这种非结构化、轻量的记录方式容易实现,也容易扩展。
访问日志
访问日志一般用来记录每个客户端对应用的访问。在Web应用中,主要记录HTTP请求中的关键数据。
在Node开发的Web应用中,可以自行实现访问日志的记录。中间件框架Connect在其众多中间件中提供了一个日志中间件,通过它可以将关键数据按一定格式输出到日志文件中。下面是Connect的一段示例代码:
var app = connect();
// 记录访问日志
connect.logger.format('home', ':remote-addr :response-time - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent" :res[content-length]');
app.use(
connect.logger({
format: 'home',
stream: fs.createWriteStream(__dirname + '/logs/access.log'),
}),
);
这里记录的数据有remote-addr和response-time等,这些数据已经足够用来帮助分析Web应用的用户分布情况、服务器端的响应时间、响应状态和客户端类型等。这些数据属于运营数据,能反过来帮助改进和提升网站。
异常日志
异常日志通常用来记录那些意外产生的异常错误。通过日志的记录,开发者可以根据异常信息去定位bug出现的具体位置,以快速修复问题。
异常日志通常有完善的分级,Node中提供的console对象就简单地实现了这几种划分,具体如下所示:
- console.log:普通日志。
- console.info:普通信息。
- console.warn:警告信息。
- console.error:错误信息。
日志与数据库
数据库比日志文件好的地方在于它是结构化数据,可以直接编写SQL语句进行分析,日志文件则需要再加工之后才能分析。
写日志是轻量的方法,将日志分析和日志记录这两个步骤分离开来是较好的选择。日志记录可以在线写,日志分析则可以借助一些工具同步到数据库中,通过离线分析的方式反馈出来。
分割日志
日志过多时也不便直接查看,为此,将产生的日志按日期分割是一个不错的主意。
在设计的过程中,我们可以按日期传递对应的日志文件可写流对象,为此可以设计一个定时器用于当日期发生更改时,更改日志对象的两个输入流对象即可。
监控报警
监控的主要目的是为了将一些重要指标采样记录下来,一旦这些指标发生较大变化,可以配合报警系统将问题反馈到负责人那。
应用的监控主要有两类,一种是业务逻辑型的监控,一种是硬件型的监控。
监控
1、日志监控
通过监控异常日志文件的变动,将新增的异常按异常类型和数量反映出来。
对于访问日志的监控也能体现出实际的业务QPS值。观察QPS的表现能够检查业务在时间上的分布。
从访问日志中也能实现PV和UV的监控。同QPS值一样,通过对PV/UV的监控,可以很好地知道应用的使用者们的习惯、预知访问高峰等。
2、响应时间
响应时间也是一个需要监控的点。一旦系统的某个子系统出现异常或者性能瓶颈,将会导致系统的响应时间变长。健康的系统响应时间应该是波动较小的、持续均衡的。
3、进程监控
监控进程一般是检查操作系统中运行的应用进程数,比如对于采用多进程架构的Web应用,就需要检查工作进程的数量,如果低于预估值,就应当发出报警声。
4、磁盘监控
磁盘监控主要是监控磁盘的用量。给磁盘的使用量设置一个上限,一旦磁盘用量超过警戒值,服务器的管理者就应该整理日志或清理磁盘了。
5、内存监控
监控内存并长时间观察是防止系统出现异常的好方法。如果突然出现内存异常,也能够追踪到是近期的哪些代码改动导致的问题。
6、CPU占用监控
监控CPU占用情况,可以帮助分析应用程序在实际业务中的状况。合理设置监控阈值能够很好地预警。
7、CPU load监控
CPU load又称CPU平均负载,它用来描述操作系统当前的繁忙程度,可以简单地理解为CPU在单位时间内正在使用和等待使用CPU的平均任务数。
CPU load过高说明进程数量过多,这在Node中可能体现在用子进程模块反复启动新的进程。监控该值可以防止意外产生。
8、I/O负载
I/O负载指的主要是磁盘I/O,反应的是磁盘上的读写情况。对于Node编写的应用,主要是面向网络服务,大多数的I/O压力来自于数据库。
不管Node进程是否与数据库或其他I/O密集的应用共处相同的服务器,我们都应监控该值以防万一。
9、网络监控
一旦流量超过警戒值,开发者就应当找出流量增长的原因。对于正常增长,应当评估是否该增加硬件设备来为更多用户提供服务。
网络流量监控的两个主要指标是流入流量和流出流量。
10、应用状态监控
应用还应当提供一种机制来反馈其自身的状态信息,外部监控将会持续性地调用应用的反馈接口来检查它的健康状态。
最简单的状态反馈就是给监控响应一个时间戳,监控方检查时间戳是否正常即可:
app.use('/status', function (req, res) {
res.writeHead(200);
res.end(new Date());
});
11、DNS监控
DNS是网络应用的基础,在实际的对外服务产品中,多数都对域名有依赖。DNS故障导致产品出现大面积影响的事件并不少见。
对于产品的稳定性,域名DNS状态也需要加入监控。
报警的实现
搭配监控系统的则是报警系统。如今的报警已经能够多样化,最普通的邮件报警、IM报警适合在线工作状态,短信或电话报警适合非在线状态。
- 邮件报警:如果报警系统由Node编写,可以调用nodemailer模块来实现邮件的发送。
- 短信或电话报警:一些短信服务平台提供短信接入服务,可以在监控系统中接入此类服务时,一旦线上出现到达阈值的异常时,就将信息发送给应用相关的责任人。
监控系统的稳定性
监控系统自身的稳定性对应用同样十分非常重要。
稳定性
为了更好的稳定性,典型的水平扩展方式就是多进程、多机器、多机房,这样的分布式设计在现在的互联网公司并不少见。
- 多机器:多机器部署应用带来的好处是能利用更多的硬件资源,为更多的请求服务。同时能够在有故障时,继续服务用户请求,保证整体系统的高可用性。但是一旦出现分布式,就需要考虑负载均衡、状态共享和数据一致性等问题。
- 多机房:多机房部署是比多机器部署更高层次的部署,目的是为了解决地理位置给用户访问带来的延迟等问题。
- 容灾备份:在多机房和多机器的部署结构下,十分容易通过备份的方式进行容灾,任何一台机器或者一个机房停止了服务,都能有其余的服务器来接替新的任务。
异构共存
在应用Node的过程中,不存在为了用它而推翻已有设计的情况。Node能够通过协议与已有的系统很好地异构共存。将Node用于系统改良的开发者需要考虑的是已有的系统是否具备良好的服务化,是否支持多种终端,是否支持多种语言调用。
总结
我们来总结一下本篇的主要内容:
- 在实际的产品中,需要很多非编码相关的工作以保证项目的进展和产品的正常运行等,这些细节包括工程化、架构、容灾备份、部署和运维等。只有这些任务在持续性进行,才表明项目是活着的。
- 捕捉日志相对而言是较为烦琐的事情,但是一旦构建好这个基础过程,有问题产生时则可以快速解决。良好的日志可以为系统的长期运行保驾护航。
- 在应用Node的过程中,不存在为了用它而推翻已有设计的情况。Node能够通过协议与已有的系统很好地异构共存。
- 将Node纳入到新的层面上进行考虑,使它更适应产品,在产品中发挥出更大的优势来。
作者介绍非职业「传道授业解惑」的开发者叶一一。《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。如果看完文章有所收获,欢迎点赞
标签:Node,文件,监控,产品化,js,应用,进程,日志 From: https://blog.51cto.com/u_15838863/12045094