作者| 阿里文娱高级开发工程师 天甘 慕理
责编 | 屠敏
头图 | CSDN 下载自东方 IC
背景
你一定也深有同感,在开发业务需求时,1/3是时间都花费在了前期准备工作上。比如,调研数据来自哪里,查看几条真实数据及其结构,然后开始写代码过滤出所需要的数据,并将数据组装成期望的结构……经过漫长的过程后,才能进入正题。今天我将介绍,如何通过元数据中心,帮你直接提升开发效率。
什么是元数据中心?它有两个主要作用:沉淀数据源广场、自定义接口。
1) 数据源广场:通过关键字搜索出基础平台服务相关接口,可手动快速调用查看接口;
2)自定义接口:根据业务自定义所需字段(字段名、类型),元数据中心SDK还将接口调用这一系列逻辑抽象出来,沉淀了“接口调用引擎”,业务开发只需要将平台下发的接口标示传入引擎中,即可完成接口接入,由hardcode转为配置化。
除以上两点,元数据中心还建立了统一监控、熔断能力,采用多线程池,保证接口调用的效率及稳定性。
元数据中心有两个核心模块:1)数据源,对基础平台服务(像需求中的ABCD系统)接口做标准化调用;2)自定义接口,为业务开发做接口编排,包括入参和出参的定义。
1、数据源
数据源模块是将基础平台服务接口做了标准化调用,与其他平台标准化不同的是,基础平台服务不需要实现JAVA接口,这样降低了基础服务接入。目前元数据中心暂时支持HTTP及RPC接口,后面我们会逐步支持分布式数据库数据源,分布式缓存数据源及mock数据源。数据源模块采用以下技术来保证调用的稳定:
数据源模块采用多线程池技术,不但可以并发调用,保证接口调用的稳定,还可以根据接口调用情况动态调整所使用的线程池,保证在某个接口性能较差情况下不影响其他接口的调用;
使用了泛化调用技术,泛化调用技术避免了引入二方包导致包冲突的问题;
数据源模块还支持分批调用,很多诸如queryByIdList这种查询,肯定需要对查询ID做count限制,但是有时候业务所需要的单次查询数多于限制数,那么就需要分批并发调用多次,然后再merge结果;
利用ThreadLocal技术元数据中心还支持对接口调用的超时时间做动态的调整,有些业务为了获取到数据可以容忍较长的RT,有些业务对RT比较敏感,所以可以根据业务不同对超时时间做个性化设置。
2、业务自定义接口
1)Schema的定义
通常在业务开发时,接口主要解决两件事:1)对接底层的数据源,2)用几个数据源的结果组合成一个业务接口暴露给上层的业务。因此,在设计Schema时考虑了以上两点,将Schema分为调用和返回,根据不同的需求配置调用哪些数据源,并配置这些数据源的依赖关系,并且配置接口需要以什么样的方式返回,可直接透出数据源的返回结果,对结果进行简单维护,也可以自己定义结果,并指定取值的来源,对结果的类型和结构进行控制和干预。
2)Schema执行引擎
第一:简述执行过程
首先解决依赖关系,然后进行数据源调用,调用后再根据返回配置来处理。这里如果返回值配置的是一个表达式,那么引擎直接执行这个表达式来作为中间结果,并根据特殊配置对中间就结果进行处理。如果没有指定配置表达式,而是指定了自己需要的哪些字段,引擎会根据这些字段值的表达式分别进行设置,如果这个字段是一个嵌套,引擎对嵌套的Schema进行递归处理。
第二:如何解决数据源依赖
有很多使用场景是一个业务接口需要两个数据源的支持。对于依赖,抽象出一个依赖解决器,它会遍历所有的数据源调用,分析调用关系并生成一颗依赖树。在调用时,对树进行层次遍历和先序遍历,保证先执行到无依赖的调用,再处理有依赖的调用。举例,总共需要调用8个数据源分别为A、B、C、D、E、F、G、H,其中C依赖A,D依赖A和B,E依赖B,G依赖C,F依赖C和D,H依赖E。下图表示生成的依赖树:
如图所示,其中先按层次遍历ROOT,但这是一个虚拟的节点不处理,之后调处理A和B,这里可以并发调用,也可以顺序调用,交给数据源调用层来处理。A和B处理完成后,处理A的子节点和B的子节点C、D、F。直到所有节点都处理完毕。
第三:如何映射结果
引擎调用数据源后,需要根据表达式来进行结果映射。如果是需要自定义返回的每一个key,比如调用视频查询,默认返回的key为id,而需要向业务方返回的是vid,可配置key的值表达式。设计时希望表达式尽量简单,于是设计出了这样一种表达式,比如vid,可配置为datasource.video.response.id。
3)动态脚本的执行与优化
第一:为什么选择Groovy
因为整体技术栈是Java,因此首选的脚本语言就是基于JVM,而选择Groovy一是因为成熟,同时语法比Java简洁,二是因为接入简单(只需一个jar包),三是因为本身兼容Java的大部分语法,学习成本较低。
第二:Groovy遇到的问题和解决办法
主要是安全问题和性能问题。安全的问题非常严重,因为脚本是交给用户输入的,所以很容易出现风险代码。系统对Groovy的编译器进行了一系列自定义,同时使用沙箱,将代码进行拦截,过滤掉敏感操作,最大化避免风险代码的执行。
性能上,Groovy的性能远比Java慢,于是进行了一番优化,比如将Groovy的代码进行预编译和缓存,保证执行时不会发生编译动作,保证执行一个Groovy单条脚本不会有太大的性能问题。
第三:最终解决方案
经过测试,发现Groovy本身的执行效率还是要低,因为一次请求可能需要执行成百上千次的脚本,一次执行的性能问题不明显,但是多次执行问题很容易就暴露出来,比原生的Java执行仍然有百倍的差距。
这个既然是Groovy的问题,并且无解,就想到了是否可以用其它的方式替换Groovy,于是研究了各种表达式引擎包括Aviator、FEL、MVEL、JSONPath、Java动态编译。并且对这些代码进行测试,使用两条语义相同的代码,分别编写了不同脚本的版本,与Java原生的代码进行比对。长时间运行后结果如下:
上图为两个脚本执行1000次的耗时,发现使用Java动态编译和JSON-Path,性能比Groovy提升了约150倍。但是缺点也非常明显:JSON-Path是能写单行的取值语句,而Java语法复杂、不方便写内部类和方法等。考虑到使用场景,Java和JSON-Path满足95%以上的场景,可再用Groovy写更复杂的脚本。
稳定性
稳定大于一切,接口调用稳定性是重要一环,如何保证稳定性呢?1)有问题时要能及时的报警,2)为了整个应用的稳定性,需要自动熔断。
1、监控报警
因为元数据中心SDK是去中心化的,接口的调用逻辑发生在业务应用本地,SDK利用logback的Appender扩展将日志异步发送到日志服务中,再用监控平台采集日志服务的日志,在监控平台中完成监控报警的搭建。元数据中心SDK还集成了调用链监控平台,不但可以在业务应用对应的调用链监控平台上看到接口调用情况,还利用了logback的ClassicConverter扩展,在日志中统一加入调用链唯一ID,方便通过该ID查看全链路日志,以达到快速定位问题的目的。
2、熔断降级
如果你的应用有外部依赖,那么保证系统稳定性,熔断是必不可缺的手段。想象一下,某个依赖的RT突然变高,如果没有及时熔断,那么你应用的线程池资源很快会被该依赖消耗完,且整个应用将无法响应,造成雪崩,所以熔断的重要性是不言而喻的。元数据中心目前支持三种熔断策略:RT、秒级异常比例、分钟异常数。
实践提效
说了这么多,究竟能否为业务开发提效呢。实践是检验真理的唯一标准,接下来让我们一起来看下元数据中心如何与业务结合,为业务开发提效。
1、与内容分发开发框架集成
目前优酷主APP业务基本上是统一的内容分发开发框架开发的,那么与内容分发开发框架集成,是为业务开发提效最好的切入点。内容分发开发框架虽然具有强大的配置能力,但是它的数据源开发还是面临到处找接口,学习怎么调用,然后引入二方包,hard code,提测,aone发布一系列漫长而重复的操作。另外一旦有接口需要切换或者业务需要增加字段等变更操作,还需要通过hard code方式改动,这样不但开发效率比较低,频繁的发布还增加了系统不稳定性。结合元数据中心的目标,内容分发开发框架集成元数据中心就可以完美解决它的痛点:
接口接入不再需要hardcode,通过配置即可完成;
接口需要切换或者业务需要增加字段时,不需要hard code,在元数据中心做切换,做字段的增加,然后通过脚本语言动态的将字段读取出来;
无需重复的打印监控日志,也不需要单独做熔断,由元数据中心统一处理。
再来看看开发者的反馈情况,最近与业务开发同学了解到,目前开发新组件的需求中有涉及需要读取某些数据,通过元数据中心配置了相关接口和所需字段,且结合开发框架的配置能力,从开发到上线基本没有hard code,通过配置快速的上线验证。
2.与Faas集成
众所周知,互联网产品讲究的是快速迭代和试错,业务需要具备快速上线能力,与竞争对手PK。这就意味着背后的技术研发流程需要更加高效,元数据中心与Faas结合就可以实现快速上线的效果。因为业务开发通过元数据中心可以快速接入接口,再结合Faas的开箱即用特点,直接编写业务逻辑,保存之后自动完成构建、部署,整个流程简单、高效。足以达到快速上线验证的目的。前优酷小程序业务已经在元数据中心+Faas下上线了很多业务。
总结与展望
元数据中心平台已经初步上线,并通过与内容分发开发框架集成已服务于优酷APP部分业务场景及与Faas集成服务于小程序部分业务场景。从业务开发反馈来看,目前已实现了业务在接入数据源这一层由hard code方式转变为配置方式,极大提高了开发效率。
虽然平台基础能力已经建设完成,并且开发提效这一目标也已初步实现,但是平台建设还有很长一段路要走,还需要做很多工作,比如提高配置效率,支持更多协议数据源,将平台的价值发挥到最大化。