您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
GraphQL实战(1)-GraphQL介绍
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
GraphQL实战(1)-GraphQL介绍
自猿其说Tech
2021-06-30
IP归属:未知
37800浏览
计算机编程
### 1 GraphQL的前世今生 Facebook的业务线有移动端,PC端和其它端,不同的场景下对一个资源所需要的信息是不同的。如移动端需要User的a、b、c三个字段,PC端需要b、c、d三个字段;对于此场景,要么开多个定制化API接口,会造成代码冗余,要么一个全信息API接口,有接口信息冗余。 造成了不止以下三个痛点 - 移动端需要高效的数据加载,被接口冗余字段拖累 - 多端产品下,API维护困难 - 前端新产品快速开发困难,需要大量的后端配合写业务定制化API 解决以上问题,2012年Facebook在开发中形成了一套规范,就诞生了GraphQL,并于2016年将此规范开源。 API开发方式的发展,从SOAP到REST,经历了很多年,GraphQL也许是下一代的API方式,发展的共同特点是,API越来越自由,越来越灵活。 #### 1.1 GraphQL-API查询语言 ##### 1.1.1 GraphQL是什么? 官方的解释是 GraphQL是一种用于API的查询语言。 怎么理解API查询语言,我们知道SQL是结构化查询语言,是查数据的,当然也能操作数据。同为Query Language 当然是有相同点的。 ##### 1.1.2 GraphQL和SQL - SQL 是对数据库(服务端)的操作语言 - GraphQL 是对API接口(服务端)的操作语言 数据库分为服务端和客户端,我们最为客户端写SQL来操作数据库; GraphQL也分服务端和客户端,这里讲的是服务端为主。客户端就是接口调用方,暂且理解为前端;服务端就是提供接口的后端服务。 GraphQL可以简单理解为,客户端 操作 API, 后端业务系统作为数据库为客户端提供服务。 ![](//img1.jcloudcs.com/developer.jdcloud.com/6f2f0ce6-4ffa-4f64-9f78-16d52b636a1920210630134748.jpeg) #### 1.2 GraphQL的优点 现在来看看GraphQL做到了什么。 ##### 1.2.1 接收的数据不多不少 向你的API发送一个GraphQL请求 就能准确获得你想要的数据,不 多不少。没有任何数据冗余。这是graphql最大的优点,客户端在GraphQL的请求脚本中指定查询的字段,API就不会返回任何多余的字段。 对应于SQL就相当于: ``` SELECT age,name FROM USER WHERE id = 1532; ``` 对应的GraphQL语句就是 ``` { queryUser(id:1532) { age name } } ``` 下图是使用效果: ![](//img1.jcloudcs.com/developer.jdcloud.com/7303368c-7bf8-43fb-a37f-ab73a26fc36020210630134957.png) 这个特点,避免了网络传输的数据冗余,对前端的性能有改善,对于后端的优化也提供了新的方向,如根据GraphQL的需求,动态生成对应的sql,避免每次查全数据库字段带来的性能损耗。 ##### 1.2.2 获取多个资源,只用一个请求 简单来说就是,多个接口的调用,可以放到一个http请求中来做,并且自定义数据返回的格式,包括指定变量名。 传统的REST请求,是一次http请求,只操作一个API的内容,如一个场景下需要查询用户信息,商品信息和订单信息。要么前端调三次接口,要么后端封装一个定制的接口。 **示例:** graphql多次调用服务获取资源: 获取用户信息 ``` query queryUserInfo { getUserInfoByID(userId:1890) { userName userAge mailbox } } ``` 获取商品信息 ``` query queryProducts { getProducts(device:web) { productName productid sppu price } } ``` 获取订单信息 ``` query queryOrders { getOrdersByUserId(userId:1890) { orderId orderNo } } ``` 以上内容,一次请求接口与搞定 ``` query { user: getUserInfoByID(userId:1890) { userName userAge mailbox } products: getProducts(device:web) { productName productid sppu price } orders: getOrdersByUserId(userId:1890) { orderId orderNo } } ``` 这个特点在移动端弱网环境下,也能高效的加载数据;而对于后端,也会相应减少Servlet线程是使用次数,可以有更高的系统吞吐量;另一个方向,一次请求多个接口,也可以采用异步执行,提升服务端的响应效率… ##### 1.2.3 描述所有可能的类型系统 graphql的schema真正做到了代码及文档,服务端定义好graph后,客户端可以方便的查看到结构模型,客户端写脚本的时候graph也会起到很好的辅助作用。 ![](//img1.jcloudcs.com/developer.jdcloud.com/b9bddac5-7bf8-4184-ba50-c0505b75677b20210630135243.png) 这一特点对于接口文档有了重新定义,后端定义好graph,前后端就可以同时开发业务,之后前端在需要组合类的API业务,只需要在这些原子性的API中组合数据即可。 以上三点就是GraphQL的明显优势 ##### 1.2.4 方便的调试工具GraphiQL GraphqiQL是一个用于调试graphql的开发工具,包括调试接口和文档查询,他有代码提示和校验的功能。在代码集成插件后,可以在浏览器直接使用。 ![](//img1.jcloudcs.com/developer.jdcloud.com/3597546d-ecc6-45b3-8ae7-b7ac95ecd87e20210630135312.png) ##### 1.2.5 较低的迁移成本 很多人由于考虑到庞大的老系统,从REST迁移到GraphQL过程中有很高的迁移成本,导致放弃使用GrqphQL。 这一点是多虑了,第一,graphql和rest是可以两种形式并存的,并不是说使用graphql后必须放弃之前的REST方式,其实有一部分的场景还是REST实现更方便;第二,只要选择或者设计好graphql的实现方式,完全可以使用现有的业务代码,可以很快开发出一套graphql的版本。 ##### 1.2.6 系统可以无感升级 对于API的升级,只要不是删字段的情况,服务端的接口增加字段,增加API方法对于客户端的使用是没有任何影响的,既不会改变URI,也不会造成API升级带来字段冗余的情况。 ### 2 GraphQL与REST GraphQL可以认为是REST的改良版。 graphql需要借助REST来暴露一个url地址,通过REST的形式更方便的操作定义的API。 一套系统中GraphQL与REST可以共存,并且很多场景下都是这样的。通过设计也可以共用一套权限验证的逻辑,业务代码就更是能够共用了。 一般情况下,REST是一个API对应一个URI.而GraphQL是可以所有的API共用一个URL,甚至客户端可以根据业务自己组合一个业务上的API。 ### 3 GraphQL的缺憾 GraphQL有很多优点,但GraphQL也不是完美的。也有一些缺憾的地方。 - GraphQL是Facebook发明的,并将GraphQL的规范开源。但Facebook的后端 设计并未开放,并且很难找到成功的示例。就连GraphQL的封装实现是第三方做的。 - 并不是所有的API都适合GraphQL来做 - 前期没有经过认真设计的GraphQL实现,容易造成一些性能问题 - 首次接触GraphQL的开发者,一定会遇到一些不好解决的问题,如N+1的问题:查询主信息附带子信息的场景,处理不好的话容易造成先根据id查询主表后获取到子表ids,然后循环根据子表id查询子表信息,这就造成了查询n+1次数据库,当然这个问题在REST中也是存在的,只不过GraphQL场景下解决起来似乎更加麻烦一点。权限验证:通常的权限验证是在网关层根据接口路径做的校验,如果是GraphQL的话只有一个URI资源,所以权限验证的方案就得重新设计了… ### 4 对GraphQL的期望 选择一个新的技术,一般是为了解决现有的痛点或者是使技术或方法体系能够得到进一步的升级。 这里谈一下笔者对GraphQL的一些期望 - 清晰的描述系统的功能:通过Graph的定义,使上游业务方可以清楚的了解我们开发的API结构,从而能够达到自助调用接口的目的。 - 构建强大的开发者工具:GraphQL是一套规范,基于这套规范开发出一款自动化的工具,如根据GraphQL自动生成SQL调用数据库,自动调用第三方系统,自动生成一些业务计算的逻辑,从而减少后端业务的开发量 ### 5 GraphQL的执行 GrsaphQL并不是一套神秘的黑盒技术,这是它的优点,让每一个真正使用的它的人先了解它的原理;这也是它的缺点,很多需要实现的功能都需要自己开发。 前边说了GraphQL是一套规范,是一个查询语言,自然就有它的使用规范和操作语法需要学习了。这边简单提一下,Schema中需要定义操作类型,对应的graphql需要定义一系列业务相关的Type,Input… ``` type Query { human(id: ID!): Human } type Human { name: String appearsIn: [Episode] starships: [Starship] } enum Episode { NEWHOPE EMPIRE JEDI } type Starship { name: String } ``` 有关GraphQL的规范建议到官网上去学习了解:https://graphql.cn/learn/execution/ ### 6 java实现及分析 到这里必须具备的知识储备:对GraphQL有简单的了解,了解Schema的常用类型。 这里用一些demo示例来体验GraphQL的执行过程,这只是借助graphql-java实现的java版本。 首先需要引入graphql-java的依赖 ```xml <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>15.0</version> </dependency> ``` demo ```java import graphql.ExecutionResult; import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.schema.StaticDataFetcher; import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; public class HelloWorld { public static void main(String[] args) { //定义schema文件,直接写在了代码中,包含一个hello的查询方法 String schema = "type Query{hello: String} schema{query: Query}"; SchemaParser schemaParser = new SchemaParser(); //直接加载schema,初始化GraphQL TypeDefinitionRegistry typeDefinitionRegistry = schemaParser.parse(schema); //加载一份服务端数据 RuntimeWiring runtimeWiring = new RuntimeWiring() .type("Query", builder -> builder.dataFetcher("hello", new StaticDataFetcher("world"))) .build(); SchemaGenerator schemaGenerator = new SchemaGenerator(); GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); // 构建一个GraphQL实例,执行graphql脚本 GraphQL build = GraphQL.newGraphQL(graphQLSchema).build(); ExecutionResult executionResult = build.execute("{hello}"); System.out.println(executionResult.getData().toString()); // Prints: {hello=world} } } ``` 这个demo直接加载了schema的内容,构建Graphql执行脚本。这种方式最接近于实际开发的操作。 从demo升级成实战项目需要做的内容: - schema与java代码分离,可以通过读取文件的形式将schema加载到系统中; - 动态处理GraphQL的请求参数,并随之生成对应的响应 可能大家已经发现,入参和出参都需要服务端编码实现解析。 其实graphql-java做的事情是很有限的,主要作用如下: - 维护schema的结构 - 根据GraphQL的脚本需要,过滤响应结果 其他很大一部分的工作都需要自己实现的。 ### 7 写在最后的话 GraphQL是2012年产生,与2015年开源的一套规范,相应的各个语言都有第三方实现的一套简单的框架。https://graphql.cn/code/ 在国外的一些开发者者手中,陆续有一些自研的插件被开源,使用也很方便,在或许的分享中会有体现。当然,GraphQL在国内还没有大面积使用,包括相关的技术分享也极其保守,很少有人分享自己对GraphQL的设计及问题处理。 很多人觉得,GraphQL出现到现在已经好多年了,现在还是没有火起来,认为GraphQL已经没戏了,如果去了解一下SOAP到REST的演化,或许就不这么想了。也有人认为,GraphQL只适合一些个新业务的小项目去练手,或者老系统中在REST的基础上再维护一套GraphQL,认为GraphQL是上不了台面或扛不起任务的一个技术,笔者认为这个观念有点悲观,一个新技术的流行,是需要全方位的配合,一整套系统中,需要前端,后端,还需要各个产品线的配合,如果只是一个点的试验,确实推行的阻力会很大。还有一些人是主推GraphQL的缺点,以至于一些人了解缺点之后立即放弃并大肆传播,如一些文章标题就是《GraphQL从入门到放弃》《GraphQL使用它的五个理由和不使用的五个理由》…笔者认为,没有完美的技术,我们需要做的是理性的看待新事物,并积极的接受新事物,对应新技术我们应该放大其优点,解决其短板。就像我们接受微服务的时候,并没有因为微服务带来的分布式事物等难题而放弃使用它。 ------------ 自猿其说Tech-JDL京东物流技术发展部 作者:中台技术部-基础平台组 程路超
原创文章,需联系作者,授权转载
上一篇:京东零售云企业直播平台,实力解决企业数字化变革 “ 刚需 ”
下一篇:spring七种事务传播行为
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号