黑夜无论怎样悠长,白昼总会到来。
爱奇艺作为中国最大的互联网视频综合门户,一直致力于给用户提供更好的使用体验及观影品质。PC主站作为爱奇艺的门户,日均覆盖用户达千万级别。随着公司业务的扩展及端上对项目更新迭代的频率越来越快,对接口的性能、响应时间、缓存策略、接口定制化等要求越来越高,需要对接的接口团队也越来越多,单纯的靠PC Web前端发送ajax请求去调用接口整合数据,会让前端的业务逻辑变得越来越复杂;同时对接团队越多也意味着会带来更多的沟通成本,不利于项目需求的快速开发迭代,而且前端调用接口属于外网调用,接口的响应时间相比内网调用会更长,导致页面渲染速度变慢,用户体验变差。 此时需要一个专门服务于PC Web的后端去把这些接口整合成一个接口给前端按需调用,以提升用户体验、优化开发效率。随着NodeJS体系的日益改进与趋于稳定,被越来越多的Web项目应用于线上,而且JavaScript也是前端同学熟悉的开发语言,因此我们决定使用NodeJS来开发主站后台接口系统也就是中间层去解决上述面临的问题。 本文将介绍Node在爱奇艺PC主站的应用实践,包括:中间层是什么;中间层的架构设计与实践;针对业务场景如何设计中间层代码架构;中间层服务如何通过监控保证稳定性、可用性;中间层带来的好处。 |
1.PC Web NodeJS中间层是什么?
NodeJS中间层是面向端的后台接口系统,是介于端与平台之间的薄数据层,提供内外网接口供端上或开发平台调用,主要用来实现前后端分离,对数据进行二次加工,包括拼接转换和过滤,它在业务中的角色如下图所示:
Node中间层专为PC Web提供定制化接口整合服务,各个后端业务方只需要提供原始抽象数据接口,由中间层去整合,这样前端只需要请求一次中间层接口就获取到适合展现的数据,无需浏览器端进行多次处理,具体功能如下图:
接口合并:承担从底层接口获取数据输出给页面的任务,主要包括调用、合并;
字段过滤:字段过滤、数据格式化等工作,处理展现相关逻辑与部分数据校验等;
定制化:为前端提供个性化服务,如内容压缩、RESTful风格、片段与JSON、JSONP输出、个性化数据等。
2.中间层的架构设计
中间层采取了nginx+varnish+node的架构模式,具体的架构设计如图:
由nginx做接口转发、负载,varnish做数据缓存层,node是业务处理层,在4核服务器上启动4个node进程(跟cpu核数对应),通过nginx的ip_hash做负载均衡,将请求均分给各个node进程,充分利用cpu的性能;node进程在收到请求后会通过内网调用发送http请求获取后端接口数据;varnish是一款高性能、开源的反向代理服务器和缓存服务器,我们在nginx跟node之间加了一层varnish缓存系统,通过配置在varnish缓存整条接口请求,减少相同请求的透传提高中间层服务性能。
可能有同学看到这里会有疑问:单纯的缓存接口无法缓存相同的jsonp请求,因为时间戳与callback值在变,这样即使相同参数的jsonp请求也命中不到varnish缓存。针对这种情况我们做了一层参数过滤,利用varnish的反向代理功能,重新将接口转发给varnish,达到缓存复用的目的,提高中间层服务的整体性能。
3.中间层代码架构
Node中间层采用的框架是我们基于Koa2封装的自己的qiyi-wings框架,框架本身约定了router、controller、service、model、formatter、dao等文件分层。
- app/base/ **.js: 定义 dao model service基类 提供一些通用的基类方法
- app/controller/ **: 用于解析用户的输入,处理后返回相应的结果。
- app/dao/ **.js: 接口请求模块,所有的接口请求都在这里实现。
- app/middleware/ **.js: 用于编写中间件,比如错误处理中间件,接口时长统计中间件等。
- app/model/ **.js: 数据模型模块,用于定义通用数据模型的处理方法,返回固定类型的数据,比如视频数据,资源位数据等
- app/formatter/ **.js: 数据模板,用于定义通用数据的解析模板,与model模块一一对应,在model中调用formatter,处理成符合需求的数据
- app/service/ **: 主要的业务逻辑层
- app/util/ **.js: 用于编写中间层业务共用方法
- app/router.js: 用于配置URL路由规则
- app/app.js: 用于引入qiyi-wings框架
- index.js: 用于编写中间层服务启动代码
正如前面所说中间层需要整合不同业务方的各种接口,所以我们抽象除了dao层,发送http请求获取接口数据;dao请求的各个业务方的数据格式定义多种多样,但是输出数据的主体大致是类似,例如视频数据、专辑数据、明星数据等等,我们针对这些实体抽象出model层,用来定义这些实体的字段,保证输出数据的一致性;同时定义formatter层定义数据适配schema,将不同业务方的数据转换成对应model定义的字段的数据;数据格式化完毕后由service层去做model数据的拼接组合(即主要的业务逻辑层),形成最终要输出的数据,最后由controller调用service获取数据返回给调用放。
整体的调用流程如下:
4.中间层业务接口实践示例
前面已经讲述了中间层的代码架构,那么在这个架构下如何快速的开发接口供PC Web前端调用呢?我们根据一个视频接口示例一起来看一下,在这个接口中我们将要输出基础的视频信息。框架的请求数据流程如下:
根据框架的请求数据流程图,要开发这个接口我们首先需要在Router中定义路由接受用户http请求:
其次我们需要定义视频的Controller接受Router转发过来的请求,并在Controller中定义参数处理、调用视频Service、数据返回等逻辑:
再其次我们需要定义视频的Service来做具体的业务逻辑,调用Dao发送http请求后端数据,调用Model、Formatter处理数据:
在前端调用视频接口的时候,不同的团队的视频信息有不同的字段定义,导致以前的调用方式中存在很多的数据处理工具函数去兼容,对视频数据进行二次处理,导致数据的处理多种多样,同样的组件可能由于字段名问题无法复用。所以我们提供了Model的概念,用来统一输出视频信息,方便前端调用与组件复用。视频Model的实现如下:
前面说到不同的团队会输出不同的视频信息,这些视频信息长相不同但是含义大致相同,那如何把这些不同的输入整合成同样的输出呢?我们提供了Formatter的概念,用来做数据格式化。视频Formatter的实现如下:
通过以上几个模块的配合,就开发完成了一个视频基础信息接口,前端再使用视频接口的时候,只需要定义一种渲染组件,调用一次接口就可以在多个页面复用,大大提高了PC Web的开发效率。
5.中间层服务监控实现
现在中间层服务有几十台机器,日均接口请求达亿次以上,日均QPS近万,中间层的接口服务了整个爱奇艺Web主站各个页面。此时如何保证中间层服务的稳定、可靠就变的尤为重要。对此我们从以下几个方面做了保障:
• 提测上线流程规范化:中间层接口从开发到上线要经过三步,本地开发,测试机测试,预上线环境准备安装包,生成环境部署。在预上线环境保留了前3次的上线包,一旦上线后发现有问题可于一分钟内立刻回滚。所有的代码打包是在预上线完成,然后拷贝到生产环境部署,每一次打包都会生成版本号进行校验,不会出现线上环境代码不一致的问题。
• 日志分析类监控:中间层Node代码中定义了详细的日志,用于记录参数错误、数据未找到、接口错误、代码错误、接口超时等各种错误情况以及Node进程性能指标数据,通过接入公司的venus日志上报系统将日志上报到ES集群。为了更好的利用这些日志数据我们开发了中间层自己的监控系统,通过对ES集群中日志的查询分析,获取中间层服务的QPS趋势、延迟时间、错误率、接口访问排名、各机房访问占比、Node进程的内存CPU使用状况等中间层状态,将这些状态按时间生成图文报表,只需要查看这些图文报表就可以获取中间层详细的运行数据。
• 硬件系统类监控:中间层服务接入了公司的hubble监控系统(一种类似zabbix监控的系统),用于监控各服务器上的内存、CPU使用率、nginx端口号连通性、varnish端口号连通性、各个node进程端口号连通性,一旦超过报警值或者端口号不通会收到电话、短信报警,需要立刻处理,能在服务crash或者被大流量阻塞服务时第一时间收到报警,通过这种监控方式保证了中间层服务的稳定性。
• 报警系统细分:我们在监控系统中添加了接口订阅系统、提测申请系统、上线系统,通过上线系统提交接口上线申请后会自动订阅该接口,中间层每隔一小时会跟去查询的ES集群里的日志分析各个接口的错误情况,对于错误率超过1%的接口会发邮件给对应的开发人员,开发人员收到报警后去排查错误;在监控系统中还启动了2分钟间隔的定时任务,每隔两分钟查询一次这两分钟内的日志,10分钟内连续五次接口错误率超过10%的接口大概率出了较大问题需要紧急处理,通过这种细粒度级别的监控保证了服务的可用性。
• 拨测类监控:中间层接口服务是给全国各地去调用访问的,此时保证全国各地各网络的连通性就很重要,对此我们接入了云波测系统,对全国各个地区的各主要网络运营商的网络进行定时拨测监控,一旦发现连通性问题会进行短信报警,立刻联系系统网络的同事去协助处理。
通过上述流程层、业务层、系统层、网络层的监控相结合从而保证了中间层服务99.99%的稳定性与可用性。
6.中间层带来的好处
中间层的上线对PC Web前端开发带来了革命性的改变,具体好处如下:
- 通过PC Web自己的中间层,可以按照业务定制化接口,扩大前端展现的能力和范围;
- 中间层接口由使用接口的前端工程师开发,对展现和接口的功能更加熟悉,避免了以前的工作模式中接口方跟各方的需求对接、沟通、联调时间,这样使得项目的推进更加顺利,项目迭代会更快;
- 中间层使用NodeJS,开发语言是JavaScript,跟现在前端工程师的工作语言一样,减少了学习成本;
- 中间层接口的开发由前端工程师同时负责开发,既节省了人力成本,同时又提高了前端开发人员的技术能力,使得前端工程师向全栈工程师迈进。
目前后台系统主要采用Java构建,为什么我们还要使用NodeJS去开发中间层?Java跟NodeJS相比,一个是静态强类型,一个是动态弱类型;一个是面向对象编程语言,一个既支持面向对象又适合函数式编程。虽然两者都适合业务逻辑表达和数据处理,但从代码编写角度来讲NodeJS更简洁灵活、易于维护,尤其对于JSON数据处理和接口请求合并方面。Java Web体系经过长时间的发展已比较成熟,适合做大型后台系统或中间件等构建等,在这里主要用来提供原始数据,而NodeJS相比从项目启动、开发构建、部署上线都比较轻量级,同时其事件驱动跟异步回调机制能更高效处理IO相关的业务,更适合高并发、高性能的场景,尤其是网关或前台、中台项目。
7.后记
终端的开发团队是需求链中的最上游、数据链的下游,很多产品功能都受限于业务接口,中间层提供了一种可能,让我们PC Web有了自己的接口开发能力可以对接最原始数据,既减少了前端开发中的局限性,也让前端团队在开发过程中有了更多的想象力,能更好的根据业务需要快速开展项目。