在携程 Node.js 应用根据用户群,主要分两个方向:

DA(数据聚合服务)和 SSR(服务端渲染)是服务于外部用户的,目标是提升用户体验。当然,DA 和 SSR 同时也提升了开发效率,例如前端开发人员可以更加灵活地整合数据,同构给开发人员省去了大量重复的开发工作量;

公司桌面工具(例如内部 IM 等)是服务于内部员工的,一般使用 Electron,开发维护成本低,产品迭代快。

一、Node.js 工程化

基于上述三个场景, 目前携程有一套 Node.js 的工程化方案。工程化的方案并不是一成不变的, 在任何阶段遇到了实际问题, 都会更新甚至推翻一些步骤,为的就是更好地服务于整个应用开发的生命周期。

工程化涵盖五大部分:开发、构建、测试、发布和运维。

1.1 开发

脚手架

有三个类型的脚手架:Web Application、DA Service 和 Desktop Tools。这三种类型的脚手架会服务于上述提到的三种场景。

这三种脚手架有共同点:标准化的 Docker 日志、预置统一的中间件。但同时他们也是有差异的,例如 Desktop Tools 和 Web Application 的应用模型不一样, Desktop 有 UI 层,那么 UI 层和应用层上的应用日志和用户行为如何关联,方便后续的排障;DA Service 需要将应用的健康状况周期性上报给治理中心、熔断机制等等,这些框架层面的差异,脚手架会集成进去,做业务开发同学可以不用关心这些基础设施的接入。

核心中间件

核心中间件主要是做基础设施的建设。目前有 20 多个中间件,主要的中间件如下:

图1. 核心中间件介绍

  • 存储服务 主要应用于长期的固化存储,例如静态资源。主要提供的是 Ceph 客户端。

  • 业务服务 主要应用于 DA 场景,提供 SOA Client 和 SOA Service。SOA Client 用来获取数据,需要重点关注的是读取性能和容错处理;SOA Service 用来提供对外的聚合服务,需要重点关注的是稳定性和响应性能。

  • 监控服务 涵盖所有的应用,提供三个维度的监控:Tracing、Metrics 和 Logging。具体介绍请参看下方”运维”部分。

  • 公共服务 主要包括配置中心、ABTest 的客户端、数据访问层等。

  • 缓存服务 主要用于配置信息的缓存、应用数据的缓存。提供 Redis 客户端和共享内存两个中间件。

1.2 构建

Docker 镜像

Node.js 的版本更新频率很快,每 6 个月会发布一个大版本的升级,期间会陆续出很多小版本。如果为每个版本都做一个镜像,会带来极高的开发和运维成本。基于更新频率,我们目前选取 2 个固定版本,在 Node.js 版本更替的时候,可以保证一个稳定的镜像。

安装依赖包

为了提升开发效率,在构建时安装依赖包需要保证速度快。如果中间件中用到一部分 C++ 模块,那么在安装时会做实时编译,这样会导致耗时长,甚至会因为环境问题编译失败。所以我们会将用到 C++ 模块的中间件做一下预编译,为 windows、linux 和 mac 这三个平台分别编译出 2 个固定版本的预编译包。

依赖包扫描

扫描的目的主要解决几个问题:

  • 应用中不同的包如果引用了同一个子包,但是子包的版本不一致,就会导致应用中装了多个版本同一个包,会引发 bug;

  • 中间件缺乏治理能力。通过扫描依赖包,能够做到中间件统一收口。一旦要升级,可以很快通知到开发做快速升级。例如第三方依赖包有安全问题,可以在构建环节就提醒开发人员升级版本。

1.3 测试

目前测试环节包括单元测试、集成测试、压力测试和自动化测试。自动化测试主要针对 Service 和 UI 两方面测试。UI 自动化测试使用的是 Puppteer。每次代码更新,会走一遍自动化测试流程,保证代码质量。

1.4 发布

携程云和公有云

每个云的部署环境、网络、位置等差异,会带来应用访问差异,例如访问异常,网络延迟等。这些差异需要在基础设施层面抹平,避免放在应用逻辑层面处理。

应用一体化发布

一体化发布也可以理解为一键发布。一条发布指令包含了应用核心框架、静态资源、配置的同时发布,而不需要开发人员思考什么步骤需要发什么资源。这样不仅可以提升效率,还能有效地控制发布回滚。

私有 npm 包发布

私有包的发布和 GIT 做高度集成。原因是:第一,可以通过 git 做快速的发布;第二,有历史可查,方便查看到每个版本发布的时间、人员;第三,有权限控制,避免发生生产级别故障。

1.5 运维

运维是整个环节中最重要也是最容易被忽略的环节。一个应用上线只是开始,真正要关注的一定是运维指标。

日志监控

三种维度的监控:tracing、logging 和 metric。

图2. 三种维度的监控 图片来源于网络:https://zhuanlan.zhihu.com/p/28075841

Tracing 提供的是整个请求过程中的数据,例如请求信息(头部、地址)、响应信息(状态码,响应体)、请求耗时、调用链等信息。

Logging 提供的是在请求处理过程中,每一个具体的事件埋点,这些埋点相对是分散的。可以是记录普通的日志,也可以是记录抛出的错误。

Metric 提供的是聚合数据。最大的特征是可聚合的,它展现的是一个时间跨度中的某个维度的指标。一般用来记录量化的指标,例如访问量、性能等数据。

应用排障

一般我们排查问题的时候,会先通过 Metric 的聚合指标发掘出异常,然后追踪到某一批有异常的 Tracing,可以查看到调用链、耗时等具体情况,也可以跟踪到某一个请求,查看里面的事件埋点。也有其他方式的排障,例如下图中展示,可以在线直接通过一个特殊的地址访问到的一张火焰图,非常迅速地排障。当有用户说这个页面出现问题,打开这个页面排障,可以定位到底哪个对应的地方出现问题。

图3. 火焰图

二、Node.js 最佳实践

2.1 部署模型

图4. 部署模型

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

2.2 问题一:多进程通信

多进程通信主要用于数据交换,最常见的有 2 种场景:

  1. 提供 SequenceId:在单台机器需要提供唯一且按时间序列排列的 ID。

  2. 提供远端配置信息:当获取远端配置信息时,需要考虑多进程的共享分发。

图5. 多进程通信 V1.0

在第一版本设计中,我们采用的是 IPC 机制进行多进程的通信。Master 作为一个中转站,当 Slave 有消息分发时,通知给 Master,再由 Master 分发给各 Slave,从而达到进程之间通信的效果。但是上线之后发现,这样的机制会遇到几个问题:数据量必须控制好体积;数据的同步会有延迟;Master 必须时刻在线,一旦 Master 进程挂掉,就需要等待重启再重连。

基于这些问题,我们重新设计了第二个版本。

图6. 多进程通信 V2.0

在第二个版本的设计中,我们使用了共享内存(shared memory)。举一个场景为例,当需要获取某个配置的时候,先将这块内存锁定,尝试从内存中获取数据。如果判断数据存在且在有效期内,那么解锁并从内存中读取数据返回,否则从服务端获取数据,当服务端有数据返回时,将数据和有效期更新到内存中,解锁并从内存中读取数据返回。通过共享内存的机制,可以非常轻量级且高效地实现多进程之间的数据共享。

2.3 问题二:监控什么内容

图7. 监控指标

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

  • CPU util:CPU 总的使用率。

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

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

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

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

Nginx 中监控的是整个 Docker 的情况,但是我们更需要的是监控应用的指标。应用一般采用 PM2 cluster –i max 模式启动,最大化利用 CPU。

Heartbeat(心跳信息)

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

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

性能情况

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

  1. 每一个响应的请求耗时(服务端逻辑处理耗时,不包括网络耗时)。

  2. 每一个 Transaction 的耗时。一个 Transaction 可以简单理解为一个有功能意义的代码片段。

  3. 跨应用调用的请求耗时。

错误 / 告警信息

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

  1. 应用逻辑出错,例如处理 JSON 数据出错等。

  2. HTTP 请求出错,会记录状态码、请求地址、返回内容。

  3. 应用中使用了不同版本的同一个包,会报一条告警信息通知开发工程师。

详细数据日志

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

2.4 问题三:全链路监控

全链路监控指的是端到端的监控,监控的是一系列的调用链情况。

图8. Tracing 模型

在介绍全链路模型之前,首先介绍 Tracing 模型(图 8)。Tracing 模型是一个树状结构的模型。以一个场景为例,当用户发起一个请求,这个请求的处理中有三段逻辑(authentication、soa request 和 data aggregation)。在整个请求体外层会有一个 Transaction#1,记录请求响应等信息。每一个逻辑段会对应一个 Transaction#2,Transaction#2 的父节点是 Transaction#1。Transaction#2 中可以有多个 Logging 信息,根据类型可以分为 Event/Error/Log,也可以包含 Metric 信息。这些 Logging 和 Metric 都有父节点,是 Transacation#2。按照这样的结构可以将一整个 request 的过程的监控信息记录下来。

要做全链路监控,就是需要将每个 request 和调用链做关联。

在过程中遇到的最核心的问题是,如何将上下文进行关联。第一个版本使用的是 domain 的模块,使用 domain 的 add api 将上下文信息记录下来,使用 run api 运行逻辑代码块。第二个正在测试中的版本是使用 async_hook 的模块,引入了生命周期的概念,通过 executionAsyncId 和 ttriggerAsyncId 可以追踪每个函数体。

图9. 页面请求模型

通过上图的页面请求模型可以将每个请求做关联,从而达到全链路监控的效果。

三、总结

  • Node.js 工程化需要结合业务,反复磨合;

  • 设计好运维指标,做好 Tracing/Logging/Metric 的结合;

  • 密切关注上线之后的监控指标,防止内存泄漏;

  • 发掘出 Node.js 技术栈的差异,有针对性地解决问题;

  • 不要盲目相信同一个技术栈,合适才是最好的。

Node.js在携程的落地和最佳实践相关推荐

  1. 携程Hadoop跨机房架构实践

    陈昱康,携程架构师,对分布式计算和存储.调度.查询引擎.在线离线混部.高并发等方面有浓厚兴趣. 本文将分享携程Hadoop跨机房架构实践,包含Hadoop在携程的发展情况,整个跨机房项目的背景,我们跨 ...

  2. 干货 | 携程RN渲染性能优化实践

    作者简介 佳璐,前端开发专家,关注前端框架.性能.质量.效率和新技术. 一.背景 随着 React Native 在前端业界规模性的应用越来越多,各大厂也对其渲染性能越来越看重. 渲染性能的主要评判指 ...

  3. ceph怎么搭建文件存储_SUSE专家谈Ceph落地之最佳实践

    点击上方"蓝字",欢迎关注! 软件定义存储业内的重要开源项目Ceph可以说是目前业内最流行,应用最广泛的开源软件定义存储.近日,在第三届软件定义存储线上峰会上,SUSE中国区技术总 ...

  4. 云原生之路:趣谈容器技术从落地到最佳实践

    随着科技的发展,云原生技术正成为数字化转型的重要一环.今天,让我们一起踏上云原生之路,探索容器技术以及实现云原生的最佳实践.容器技术的出现,使得应用的开发部署变得更加快捷.灵活,极大地提高了企业级应用 ...

  5. 携程DBA负责人俞榕刚:OceanBase在携程的落地和实践

    编者按 作为在互联网耕耘多年的一线互联网厂商,携程在数据库实例规模和数据规模都是行业标杆,那么在引入分布式数据库时有哪些考量和在业务实践后效果如何?带着这些问题我们专访了高级数据库总监,携程DBA负责 ...

  6. 干货 | 支持10X增长,携程机票订单库Sharding实践

    作者简介 初八,携程资深研发经理,专注于订单后台系统架构优化工作:JefferyXin,携程高级后端开发专家,专注系统性能.业务架构等领域. 一.背景 随着机票订单业务的不断增长,当前订单处理系统的架 ...

  7. 携程RN渲染性能优化实践

    一.背景 随着 React Native 在前端业界规模性的应用越来越多,各大厂也对其渲染性能越来越看重. 渲染性能的主要评判指标是FMP与TTI,在 React Native 以跨平台前端框架身份逐 ...

  8. 干货 | 实现一个属于你的“语言”-携程Kotlin DSL开发与实践

    作者简介 刘媛,携程金融高级开发工程师,主要负责中文版.国际版支付Android端的开发及维护工作. 每一个DSL,都是一定意义上专有的语言,这篇文章希望能够用浅显易懂的方式,将Kotlin DSL的 ...

  9. 携程数据库高可用架构实践

    作者:携程技术团队,来自:DataFunTalk 导读: 我们推荐使用数据库三副本,一主一从一异地容灾.如果想要节省成本,也可以只保留两副本,但是一旦其中一台服务器发生故障,服务器维修时间会比较长,那 ...

最新文章

  1. 5G和AI机器人平台为工业4.0和无人机提供服务
  2. python第三节函数,文件操作
  3. 被迫重构代码,这次我干掉了 if-else
  4. 有36个人,36块砖,每人搬了一次,正好搬完。 其中男每人每次搬4块,女每人每次搬3块,小孩两人每次搬一块。问 男、女、小孩各多少人?...
  5. C++编码中减少内存缺陷的方法和工具
  6. C语言以递归求斐波那契数列(附完整源码)
  7. 为什么要将服务或者数据部署多份?
  8. 关于mysql修改密码 set password for root@localhost = password(‘xxx‘);报错解决方法
  9. 如何在Office 2007中查看关于对话框和版本信息
  10. CSharp设计模式读书笔记(22):策略模式(学习难度:★☆☆☆☆,使用频率:★★★★☆)...
  11. 【HDU - 5961】传递(图,思维,暴力,枚举点)
  12. html盒子嵌套居中,css在盒子中垂直居中和固定居中
  13. Linux Socket学习--为套接口绑定地址
  14. Android 应用开发(38)TableLayout(表格布局)
  15. 《linux内核完全注释》读书笔记 2
  16. synchronized的实现原理用法详解
  17. jieba java_【NLP】【一】中文分词之jieba
  18. java 前后端分离
  19. python编写一个函数把华氏温度转换成摄氏温度_编写一个函数把华氏温度转换成摄氏温度,温度转换公式为:c=(f-32)*5/9。在主函数中输入华氏温度值......
  20. 匿名留言板表白墙 小程序校园树洞带后台

热门文章

  1. 现代数据栈中的消费层
  2. 弗洛伊德的理论体系简介
  3. 如何通过linux下载github代码
  4. 最优java三角剖分算法代码,动态规划之凸多边形的最优三角剖分
  5. iOS——ARC规则
  6. 旅游网站做朋友圈广告有哪些要点?
  7. 【macbookpro】重装ventura系统
  8. Android layout adaptive to mutiply density screen
  9. 【系统篇 / 虚拟】❀ 02. Hyper-V 新建虚拟机 ❀ Windows Server 2016
  10. IOS JSBox 安装 VSCode 通过插件JSBox 同步编辑代码到iPhone