浅谈 Node.js 在携程的应用

阅读数:8545 2019 年 5 月 25 日 08:00

浅谈Node.js在携程的应用

携程在 2017 年 9 月份正式上线了 Node.js 应用,本文主要介绍近两年 Node.js 技术栈在携程的应用和体系情况。

一、技术栈

1.1 应用部署

浅谈Node.js在携程的应用

应用部署主要分为以上四个步骤:Develop-> Build ->Release -> Publish

  • Coding 阶段会使用脚手架和中间件开发应用,中间件后面会介绍到。
  • Build Docker 会负责源码的构建功能,包括一些 C++ 模块的编译和集成环境,同时会设置构建的缓存机制。
  • Release Docker 负责应用的启动和运行,相对轻量级,更需要关注的是 Docker 的数量、CPU、内存等基础信息。
  • Publish 负责应用启动之后的健康检查,健康检查完成之后会将 Docker 拉入集群并提供外部访问。

1.2 版本选择

在 Build 阶段,会选择 Node.js 的版本。提供了三个固定版本分别是 v6.10.2,v8.9.4 和 v10.15.1。目前 v6.10.2 版本已经基本进入非维护阶段,并逐步更新下线,版本的更迭与 Node.js 官方几乎保持同步,为保证性能最优,推荐优选最新的 LTS。

当时选择 Node.js 固定版本是考虑到编译环境的简单和稳定性。Node.js 中间件和第三方库都需要做预编译,为了保证编译环境的简单和应用稳定,会选择固定的某一个版本。

同时针对这 3 个固定的版本,中间件发布的时候,也会一并提供 window/linux/mac 这 3 个平台预编译的包。Linux 预编译包是为了 Build Docker 和 Release Docker 准备的,windows 和 mac 预编译的包是为了开发工程师本地开发的时候准备的。

浅谈Node.js在携程的应用

1.3 构建原则

“靠前构建原则”

如果能在线下编译的尽量线下编译,不要在运行构建。例如:

  • C++ 模块的预编译
  • 访问 SOA 或者数据库的环境配置
  • Babel 或者 TS

二、运维与监控

2.1 Docker 化

Node.js 应用部署在 Docker 上,采用 Nginx+PM2 的模式。

2.2 核心指标

浅谈Node.js在携程的应用

Nginx 会监控整个 Docker 上所有应用的情况:

1)CPU util:CPU 总的使用率

2)CPU throttle count&time:CPU 被限制的次数和 CPU 使用率被限制的总时间。这两个指标的上升一般表示应用有 CPU 密集型操作,需要检查一下是否有大量的计算等操作。

3)Mem RSS used:这个指标上升一般显示应用内存泄漏的问题。

4)HTTP imcoming&outgoging:http request 的数量变化趋势。如果有错误响应或者超过了告警的阈值,则会在趋势图中显示。

5)Connection reset:这个指标如果上升,表示应用出现了大量的拒绝请求,例如是服务器的并发数超过了原本的承载量等原因。

Nginx 中监控的是整个 Docker 的情况,但是我们更需要的是监控应用的指标。

应用一般采用 PM2 cluster – i max 模式启动,最大化利用 CPU。

1)Heartbeat(心跳信息)

每个 worker 一分钟发送一次 Heartbeat(心跳信息)给到 CAT 数据中心。一般来说,如果 Heartbeat 告警的话,需要立刻查看一下错误日志,是不是有异常错误导致进程已经退出了。

Heartbeat 主要包括 CPU、Memory、网络信息等。这些信息和上述提到的 Nginx 信息不是一个维度的。这个更细节的关注了应用的情况,而不是整个 Docker 的情况。如果需要分析应用细节的问题,是需要查看这里的 Heartbeat 信息。

2)性能情况

一般来说,中间件会处理应用常规的性能日志记录。包括:

  • 每一个响应的请求耗时(服务端逻辑处理耗时,不包括网络耗时)
  • 每一个 Transaction 的耗时。一个 Transaction 可以简单理解为一个有功能意义的代码片段。
  • 跨应用调用的请求耗时

3)错误 / 告警信息

错误告警信息是应用中需要重点关注的,包括:

  • 应用逻辑出错,例如处理 JSON 数据出错等。
  • HTTP 请求出错,会记录状态码、请求地址、返回内容
  • 应用中使用了不同版本的同一个包,会报一条告警信息通知开发工程师

4)详细数据日志

详细数据日志一般有开发工程师针对应用的逻辑埋点,而非中间件统一处理。这些日志会包括返回数据的记录,具体运行在哪一段 transaction 中。这些日志一般是故障发生时,用来复盘时的辅助手段。

2.3 监控模型

一开始的监控日志是扁平化的,只能看到一条一条简单的日志,但无法将他们的关系串联起来。为了方便排障,设计了调用树的模型,可以将应用中的多个 transaction 串联起来。

浅谈Node.js在携程的应用

2.4 日志排障

应用发布上线后,需要关注系统的保障通知。经常遇到的故障是发现随着时间的推移,Mem RSS Used 这根线会不停的飙升。

浅谈Node.js在携程的应用

遇到这种情况,基本猜测是发生了 Memory-Leak(内存泄漏)。我们需要分析 heapdump 来定位具体的问题点。

不建议在应用中定期发送 heapdump 的信息来监控,比较消耗内存。所以我们一般在发布到测试阶段,发现问题之后,采样几个不同时间点 heapdumpsnapshot 进行比对。使用的是开源的 heapdump

首先将两份 snapshot 文件加载到 chrome 中,查看 statistics,对比这里的内存变化和 Docker 中的内存变化。

浅谈Node.js在携程的应用

如果两者的变化一致,那么就说明内存泄漏的确发生在 Heap 区域,那么就可以进行两份 snapshot 的对比。

浅谈Node.js在携程的应用

如果两者变化不一致,Docker 变化量明显比 Heapdump 的多,那么就说明内存泄漏可能出现非 Heap 区域(堆外内存区域),需要查看一下 snapshot 中 Buffer 的数量是否有变化,是不是 buffer 导致的,或者查看一下 Node.js 的官方的 changelog 是否有提到 memory issue。

三、公共服务

3.1 服务调用

SOA client:SOA 客户端主要负责调用 JAVA/.NET/Node.js 等各技术栈的 SOA 服务。主要服务于数据聚合的场景。

3.2 存储服务

1)Ceph(资源存储客户端),主要存储静态资源,包含 JS/CSS/ 图片等;

2)Redis(Redis 客户端),为应用提供 Redis 缓存服务;

3)Kafka (消息系统)消息生产者和消费者;

3.3 缓存服务

缓存中间件 Cache 主要解决以下问题:

实现跨进程共享内存和跨进程锁。(参考开源模块 node-shared-cache )

3.4 公共业务

1)监控模块目前携程 Node.js 支持三种场景的日志

  • 场景一:CAT 日志埋点,树状结构展示日志, 监控服务运行快慢、监控异常、以及自定义事件监控告警。目前携程 CAT 已开源 CAT
  • 场景二:可通过特定事件、特定时间、特定 tag 值过滤查询日志
  • 场景三:可基于时间序列查看各种性能数据聚合结果,如统计某个中间件使用次数、某请求结果的平均值等。

2)foundation-framework 基础模块

  • 为不同环境下所有应用提供统一的获取 AppId、环境等基础配置的 API
  • 提供 IPv4、IPv6 的检查和 IPv6 的全地址转换

3)qconfig-client 该中间件支持从携程内部服务配置中心获取不同文件类型的配置,支持配置热更新。

4)携程 Node.js 还提供:获取 mysql 数据库连接信息、ABTest、pm2 跨进程通讯等功能模块。

3.5 DR (Disaster Recovery)

为支持 DR,nodeJS 中间件做以下处理:

1)服务连接失败重试机制。

2)通过 IP 地址访问服务时,需定时重新获取服务 IP 地址。

3)同一服务存在多个 IP 地址时,依次通过不同 IP 地址访问服务,直到成功或全部失败。

4)特定数据缓存。

在某个服务器宕机或某个 IDC 机房毁坏情况下,目前 nodeJS 中间件大部分无需任何操作,可自动恢复;部分中间件需重启应用,以保证应用可用性和数据的实时性。

四、应用场景

4.1 DA(DataAggregation)

浅谈Node.js在携程的应用

图 3 Introduction of BFF(Backend for Frontend)
(摘自 https://www.jianshu.com/p/eb1875c62ad3 )

DA 这一层主要的功能是能够提升加载速度,降低重复的数据逻辑处理。

在 DA 之前,前端展示一般需要请求多条服务做数据聚合。更复杂的情况是,如果需要适配多个平台 (Web/Android/IOS),那么就需要服务写多个接口,造成重复的开发和维护工作。

DA 主要负责将数据做逻辑处理,包括缓存、展示限制等,为前端提供更轻量级的服务。

本着“服务自治,服务于 UI”的原则,我们用前端工程师更熟悉的 JavaScript 开发是非常合适的选择,可以降低学习和开发成本,带来更大的灵活性和高效性。实践下来,引入了数据聚合层之后,性能提升在 20% 左右。

4.2 SSR(Server-SideRendering)

服务端在携程的引入主要考量有几点:

1)SEO 的.NET+V8 的老架构

2)SPA 模式首屏性能问题

3)JS 技术栈陈旧等诸多问题

4)不同平台重复编码,无法实现代码同构

所以设计一套 SSR 的框架来解决历史问题,最终带来了 30% 的开发效率提升和 20% 的性能优化。

目前携程的 SSR 框架是 NFES ,感兴趣的同学可以点击详细了解。

4.3 内部工具

Node.js 技术栈的内部工具,主要在几个方向:

1)构建工具,例如发布平台中的 Node.js 应用的构建工具

2)跨平台的 GUI 的工具,一般基于 electron 框架开发

3)静态资源的发布

五、小结

经过一年多的积累,携程已经上线 500+ 的应用。这一年多,我们比较关注的方向是中间件建设和应用性能的监控优化,后续将计划实践一些 Node.js 技术栈的框架建设和工程化方向,希望能通过更稳定的基础设施,探索新的应用场景,提升开发效率。

作者简介

潘斐斐,携程无线平台研发部高级研发工程师。2008 年加入携程,目前负责携程 Node.js 技术栈的基础平台研发工作。

本文转载自公众号携程技术中心(ID:ctriptech)

原文链接

https://mp.weixin.qq.com/s/uDwX0iq9RWs1sK1ct0tiCg

收藏

评论

微博

用户头像
发表评论

注册/登录 InfoQ 发表评论