减少重复开发,GraphQL在低代码平台如何落地?

2015年,Facebook推出了GraphQL(Graph-Query-Language)查询语言。到目前为止,IBM、Twitter、Walmart Labs、纽约时报、Coursera等很多公司已经在内部从 RESTful转向GraphQL API

作为一种查询语言,GraphQL具有以下特点:

(1)无需关心如何更新文档,所有的查询(query)和变更会 自动形成文档(cchema)

(2)无需获取整个数据集,通过 schema与resolver(处理器) 之间的映射关系,由对应的resolver去获取数据,将结果返回给前端,从而可以编写仅仅返回所请求数据的查询。

(3)对前端提供统一的访问点。从不同的API中获取数据并非易事,GraphQL 支持将所有API进行拼接

爱奇艺号技术团队在实施微服务化的过程中,受到Forrester Research提出的 低代码开发(Low-Code:即无需编码或通过少量代码就可以快速生成应用程序的开发理念) 的启发,基于GraphQL构建BFF(Backend for Frontends),帮助开发人员用拖拽式操作,直观地创建出一个 供前端调用的 API,本文将对实施过程中的经验总结进行叙述。

GraphQL介绍

与 RESTful API 一样,GraphQL API设计用于处理 HTTP 请求并为这些请求提供响应。REST API 构建在请求方法和端点之间的连接上,而 GraphQL API 被设计为只通过一个端点,始终使用 POST 请求进行查询,其 URL 通常是xxx.com/graphql。图1-1为GraphQL部署架构图,可以看到它处于 系统“中间层”

图1-1

GraphQL 全称叫 Graph Query Language,官方宣传语是 “为你的 API 量身定制的查询语言” ,用传统的方式来解释就是:将你所有后端 API 组成的集合看成一个数据库,用户终端发送一个查询语句,你的 GraphQL 服务解析这条语句并通过一系列规则从你的“API 数据库”里面将查询的数据结果返回给终端,而 GraphQL 就相当于这个系统的一个查询语言,像 SQL 之于 MySQL 一样。GraphQL执行过程如图1-2所示:

图1-2

图1-2是GraphQL 执行的大致流程, 第一步 去验证查询语句是否符合GraphQL的schema规范,确认查询内容的合法性, 第二步 生成执行的上下文,关键点在第三步和第四步, 第三步 是获取查询语句所需要查询的字段,这里叫 fields,所有需要查询的字段可以在查询语句里拿到,这就是 GraphQL 如何做到避免返回冗余数据的。拿到所有需要查询的字段后, 第四步 针对每一个字段去执行它的 resolver,可以从 resolver 返回的数据里面拿到字段对应的数据,最后是格式化结果并返回。

重点是第四步,展开说明一下,如图1-3、图1-4所示:

图1-3

图1-4

在GraphQL里面有一个概念叫类型 (type),每一个类型下面对应的是一个或多个字段(field),每个字段都会绑定一个处理器(resolver),这个 resolver 的作用就是 获取字段对应的数据

对应到图1-4所示,UserInfo这个类型,它有三个字段:nickName、contractNo、fansNumber。每个字段都对应一个resolver,resolver 需要被开发者重新定义,否则会报错。所以UserInfo下的三个字段nickName、contractNo、fansNumber需要通过实现各自resolver来分别从用户微服务、合同微服务、粉丝微服务去获取用户信息、合同信息和粉丝信息,然后再聚合返回,这样就达到了 使用 GraphQL 进行数据拼接的目的

BFF架构

爱奇艺号在实施微服务化的过程中,加入了BFF的前后端架构,如图2-1所示:

图2-1

从图中可以看出,BFF作为前后端的中间层服务。主要的业务逻辑都封装在BFF层,前端通过BFF进行访问,减少微服务之间的相互调用。BFF层通过REST API方式提供服务,随着服务的增多,提供的接口越来越多,这会导致REST API越来越冗余。对于前端而言,有的API粒度较粗不满足需求;有的API又粒度太细,不仅增加了响应时间,还会造成流量的浪费。对于后端而言,前端需要的数据往往在不同的地方具有相似性,但却又不尽相同,比如:针对用户信息,有些地方需要用户简要的基础信息和详细的视频信息,而有些地方却需要用户详细的基础信息和简要的视频信息。这往往需要开发不同的接口去满足各种定制需求,增加了开发人员的工作量,提升了开发工作的重复度。

  • GraphQL与Rest API对比:

    性能

    文档

    调试

    学习成本

    GraphQL

    性能好,申明式获取,非常直观和精准,减少不必要数据网络传输

    代码即文档

    前端能快速感知,强类型,避免出错

    Rest API

    缺乏弹性,大多数情况会获取额外数据

    使用 Swagger

    后端配合排查

表2-1

从表2-1中的对比可以看出,GraphQL相对于Rest API方式,性能更好,能有效减少前后端开发沟通成本。但是Facebook的官方只有JS版本实现,查询方式和Rest API也有所不同(如表2-2所示),对于老项目有一定的迁移、学习成本。

表2-2

接下来,本文将主要探讨如何基于graphql-java,做到减少迁移成本的同时,又能提升后端开发人员的效率,避免重复开发。

爱奇艺号API生成平台实践

爱奇艺号API生成平台,是一个低代码平台。 由于爱奇艺号的技术栈主要基于Java,所以使用的是GraphQL的 Java实现。 基于graphql-java,API生成平台主要做了以下功能优化及增强。

(1 支持Rest API:降低前端接入成本。

(2) 动态接入监控 动态生成的API,与其他普通接口一样支持Prometheus监控,保证监控的灵活性和服务的稳定性。

(3)灵活配置:可以动态生成GraphQL的schema,方便后端接入新服务。

(4) 可视化API管理平台: API接口提供可视化操作,方便查看、新增、修改和重用。

接下来将对以上功能进行详细介绍:

1.支持Rest API

graphql-java通过Spring的封装,位于整个架构的网关层或BFF层。项目user-info-graphQL依赖graphql-java-spring,支持Rest API请求。平台的整体架构图如图3-1所示:

图3-1

User-info-graphQL的服务流程图如图3-2所示。客户端通过graphql/前缀的Rest API方式请求,后端通过前缀与GraphQL Query绑定,从DB获取映射关系,最终转换成GraphQL支持的查询语法。

图3-2

2.动态接入监控

在user-info-graphQL项目中,原本是通过template url来匹配任意自定义url;导致监控平台只能显示template url的请求信息,如图3-3所示。这个问题可以通过重写 spring-boot-actuator 中获取tags的方法,将真实的url请求信息暴露到Spring boot的/actuator/prometheus端点这个方法来解决,如图3-4所示。

图3-3

图3-4

通过暴露的监控端点接入Prometheus,实现对新生成的API进行实时动态监控,示例效果如图3-5所示:

图3-5

3.灵活配置

为了方便后端快速接入新增微服务,达到支持API动态扩展目的。项目中通过Velocity定义schema模板,通过Java注解、反射机制动态生成graphqls模板文件,如图3-6所示:

图3-6

图3-6中的GraphQL schema模板,支持通过用户UID查询用户信息。用户信息是由多个微服务聚合而成,采用异步调用多个微服务并行获取数据。基于此模板,用户只需要实现SPI定义好的接口,就能实现对新增微服务的支持。

4.可视化API管理平台 

通过API生成管理平台,开发人员可以实现API接口的可视化配置、生成、动态监控等功能,达到开箱即用的效果,极大 提升开发和运维效率

总结

通过GraphQL动态构建BFF服务层API,聚合不同的微服务,相比于Rest API方式,能够减少后端重复开发,加快响应前端需求。后端开发人员只需要开发维护新增微服务,并通过SPI方式,增加BFF层对新增微服务的支持即可。

价 值:

通过在爱奇艺号后端微服务引入GraphQL 构建BFF服务层 ,可以达成以下效果:

开发方面的优势

  • 提升开发效率:后端开发人员职责分工明确,微服务与BFF层独立开发及维护。新增微服务接入方便,因BFF层对外的API支持动态生成,所以无需更改BFF层的代码,只需集中维护微服务。前端可以通过GraphQL的schema查看接口返回数据,减少前后端沟通成本。

  • 形成低代码平台:随着构建的微服务基础措施足够完善,BFF层支持动态生成API接口,极大减轻重复工作量。

运行维护时的优势

  • 便于监控:新增BFF层API接口,通过支持Prometheus端点监控,无开发成本。

  • 支持系统高吞吐量:BFF服务、微服务都是基于docker部署,支持QPS动态扩容,能够支持高并发。

  • 便于维护和扩展:基于GraphQL构建的BFF层,API接口动态生成,层次清晰,更易维护、扩展。

难 点:

  • 动态扩展查询支持,目前schema的定义都是基于明确字段的情况下,如果需要支持动态的查询支持,需要支持动态schema扩展和解析。

  • 中文文档少,Facebook官方只发布了JS实现,Java实现都是基于开源社区,文档和功能都不是很完善。

未来规划:

随着BFF端对API请求的多样化,需要动态支持新方法扩展及监控。 目前API与请求的映射关系持久化在MySQL中,需要支持集群和高性能,后续逐步迁移到ZK或Redis中,并缓存到本地。

随着云原生和K8s的兴起,基于K8s部署的Go服务,更易扩容和维护。基于Java实现的GraphQL,如果迁移到K8s上部署,很难实现快速扩缩容的效果。而graphql-go在github上的star 高达7k ,可见热度极高。如果基于Go实现BFF端,API与请求的映射关系可以存储于K8s的Pod配置文件中,并且通过一个API部署一类Pod,进行服务隔离,可以更高程度的保证服务稳定。

也许你还想看

低代码在爱奇艺鹊桥数据同步平台的实践

代码质量提升之道——代码覆盖率原理与移动端工程实践

 扫一扫下方二维码,更多精彩内容陪伴你!