每隔一段时间就能看到一篇 GraphQL 的文章,但是打开文章一看,基本上就是简单的介绍下 GraphQL 的特性。
很多文章其实就是 github 上找个 GraphQL 的项目,然后按照对应的 demo 跑起来而已。
有些文章明显是没有完整的项目实践经历,却在狂吹 GraphQL 的各种优点,让不熟悉 GraphQL 的同学以为这是神丹妙药,弄不好还要在项目中实践一番。
因为项目的背景(后面会讲到),我有幸参与过 GraphQL 在实际项目中的落地,本篇文章我会谈谈我对 GraphQL 的一些理解
不知道大家有没有遇到过这样的一些场景,某个服务有几十个接口,更有甚者上百个也是有可能的。APP 或者其他下游要封装一个功能,需要调用 10 个接口左右
可能这些接口还涉及到不同的团队。不管开发,联调,测试,还是对于调用方,整个链条功能太多了。随着这些功能经过多个版本的迭代升级后,新+旧版本的接口谁也不敢大规模改动了,只能在原来的基础上做代码拼接
这基本就是祖传代码的由来。大部分同学基本的代码素养是有的,但是也只能任由这些祖传代码慢慢腐烂,原因为很简单,谁也不敢保证改动之后功能是不是有遗漏的地方。
有没有这样一个功能,将这些接口做一下聚合,然后将结果的集合返回给前端呢?在目前比较流行微服务架构体系下,有一个专门的中间层专门来处理这个事情,这个中间层叫 BFF(Backend For Frontend)
我曾经工作过的某公司想要将某业务的售卖相关功能给公司其他业务使用,但是接入方一看那么多接口,瞬间就决定不接了,逼不得已该业务平台紧急开发了 BFF 相关功能让其他业务方接入。
有些同学在稍微大点规模的公司做的工作就是合并各种接口,然后返回给调用方,这基本上就是 BFF 的主要工作内容了。除了类似 BFF 组建接入平台的方式,是否还有其他的方式能够只发出一个请求就能获取到一系列的接口返回值呢?
我在京东 APP 上随便截图了一个商品
类似这个页面,当用户打开这个页面的时候,按照目前比较流行的 REST 接口,需要 APP 至少发起下面这些请求:
这些接口一般来说都比较重,里面有很多当前页面并不需要的字段,那有没有一种可能:APP 端发一次请求就能获取这个页面需要的所有字段,同时 APP 还能根据自己的需求只请求自己需要的字段呢?
答案是肯定的,那就是 GraphQL
query jdGoodsQuery {
goods {
detail {
id
pictures(first: 10) {
pic_id
thumb
}
spec {
name
size
weight
}
}
price {
price
origin_price
market_price
}
comment(first: 10) {
comment_id
topic_id
content
from_uid
}
self_show(first: 10) {
id
pic_id
}
}
}
对于上面京东商品详情的截图,类似这样的一个 Query 就可以把这个页面需要的所有的字段都获取到。
对于 REST 接口还有另外一个比较棘手的问题。当业务升级的时候,接口不可避免的需要升级,一个比较常见的问题,某个字段在新版本升级后不需要了,如何优雅的处理旧接口以及旧字段呢?
可能有些同学说,可以强行推动下游去升级,限定期限进行升级,如果下游不升级概不负责,这种方式基本只能自己欺骗自己了,因为当你接口下线导致业务方报错,这个责任只能你自己负责了。特别的,当某些接口是直接面对消费者时,这个问题会变得更加棘手。
2016 年那会就职过的一家做 AI 产品的公司,上市了一款智能读书的儿童产品,其基本功能就是绘本图书翻一页读一页,这也算是 HomeAI 落地比较成功的产品了,这个产品在天猫、京东的销量还可以。在 AI 风口的那会,最开始的定位的功能就是读书,随着市场的拓展,功能也逐渐变得复杂起来,当时最痛苦的事情莫过于短短 3 个月内,接口版本号从 v1 增加到 v12。
由于当时产品的理念认为强制升级是不优美的,不符合产品设计美学,导致这款产品是没有强制升级功能的,于是导致的结果是 v1 到 v12 的接口总是有用户在使用的。你可能会说发公告、短信通知用户在某个期限内升级,如果不升级,出现产品无法使用情况概不负责,这也是行不通的,特别那些用户付费的产品,如果万一不能使用的话,那 12315 是会找上门的。
就在我们被无法下线的 API 接口折磨的时候,经过调研之后发现 GraphQL 正好有一个功能,“API 演进无需划分版本”,这不是瞌睡来了就有枕头吗,于是在技术负责人带领下(GraphQL 改造项目还没上线,他去了 Microsoft),我们开始推进 GraphQL 的改造工作。
说到这里基本上就把 GraphQL 的最容易吸引人的优点介绍完了。我们要知道没有任何技术是“银弹”,当某些人给你狂吹某个技术的优点,而没有说这项技术的缺点限制,或者简略的一笔带过的时候,你就需要小心了,说不定你就是那个小白鼠。
GraphQL 是由 Facebook 开发,Facebook 也成立了 GraphQL 基金会,但是 Facebook 官方只提供了 JS 版本的开源实现,其他语言的实现都是 GraphQL 对应语言的非官方社区实现的,这就造成不同语言的理解和实现是有差异的。比如 Graphql schema 的合并工具,只有 JS 官方实现有对应的实现。
GraphQL 属于那种大家都觉得很不错,但是经过近 10 年的发展,依然是不温不火。
上面这个图是 GraphQL 官方的 landscape[1],你仔细看上面公司图标,一眼能看出来的大的公司只有 Github 了。Facebook 虽说是推出了 GraphQL 的规范以及 JS 的相关实现,但是他自己都没有放出有关 GraphQL 的实际接口,让人对这个技术的信任度都大打折扣。
当你遇到 GraphQL 问题的时候,要么搜出来全是 JS 的相关实现,要么干脆就没有人回答,总之 GraphQL 相关社区看着文档很丰富,其实遇到问题时能搜到解决问题的有用的资料并不多。
缓存[2]对于 REST 接口来说是很容解决的,但是在 GraphQL 中却变得非常复杂。由于 GraphQL 的特性,即便它操作的是同一个实体,每次查询可能都各不相同。
举个例子,由于客户端可以自定义其需要的字段,如某次请求只需要某个人的名字,但是在另外一次查询中你可能也想知道他的消费积分。名字可能要查询 user.userProfile 库,而且消费积分可能要查询第三方系统。本次查询输入的是单个 user_id,下次查询输入的可能就是 userId_list 了
为了解决这些查询的缓存问题,你可能会设置很多 key 或者每个用户设置 key-value 放入缓存,这些都不是很优美的解决方案,归根到底还是因为 GraphQL 太多灵活,服务器的缓存如何设计都跟不上客户端的灵活的查询方式。
对于这个问题 Facebook 也有 DataLoader 的解决方案,当然只是 JS 版本的方案,其他语言的社区可能根本都没有对应的实现。按照我当初的调研这个东西真的不好用,还不如自己做缓存来得快。
GraphQL 缓存不只对服务端不友好,同时对客户端也是一个挑战,需要用户自己做客户端的缓存,因为 GraphQL Query 只有一个路由,而且都是 POST 方式。
GraphQL 是强类型的,所以必须需要一个 schema 的存在。按照当初的实践经验,客户端有且只能存在一份 schema 文件,于是另外一个比较棘手的问题就出现了。
假如我们的服务是类似下图这个样子:
比如 server1 是商品服务,server2 是优惠服务,客户端如果要对接这两个服务的话,直接对接是不行的,因为客户端只能有一个 schema, 但是服务端却有两个 schema 文件。
这种情况该如何处理呢?当初经过调研发现 JS 提供了 schema merge 的工具,而且仅仅是个工具。其他语言压根就没有这个玩意。
这个的设计还会带来另外一个非常严重的问题,目前的 API 网关都是无法使用的。随着业务规模的扩大,走上微服务是迟早的事情,但是如果服务端全是基于 GraphQL 开发的,那么网关该如何处理呢?最近调研了下 APISIX 和 KONG 的最新版本,这两个业界有影响力的网关也仅仅是支持 GraphQL 协议的转发而已。
在我看来,在如今微服务比较流行市场下,GraphQL 唯一比较契合的场景的就是将 BFF 的 REST 换成 GraphQL,该 BFF 即做网关也做业务。其实这样做,也不是很完美,即限定了客户端只能有这一个 BFF,同时也让 BFF 变得不纯粹起来。看到美团有篇介绍 GraphQL 的实践的文章[3],就是让 GraphQL 做的 BFF 的相关工作,不过他们没有将 GraphQL 暴露给客户端,而是在 GraphQL 之上又包裹了一层 HTTP 接口。
GraphQL 最大的好处就是客户端能按需查询,是便利了客户端,但是把问题的复杂度都移交给了服务器。服务器也不是想查就能查的,毕竟服务器也是资源限制的,不可能无限制的让客户端去索取。
query deep3 {
viewer {
albums {
songs{
author {
company {
address {
...
}
}
}
}
}
}
}
由于 GraphQL 要追求从一个类型能查到 schema 的任意类型。比如这样的查询,能无限嵌套下去,每个 Type 对服务器来说都是对应的查询,服务器肯定承受不了的。
如何限制呢?GraphQL 提出的有复杂度和深度的相关概念,但是这两个值该如何去计算,只能靠服务器开发人员的估计。于是这样的场景就经常出现,开发初期约定复杂度 1000,过了两天客户端同学找了过来要提高到 3000,全是无尽的扯皮,不管设置多少,由于客户端查询的多样性总有不够用的情形出现。而且这个复杂度、深度是全局的,不是每一个 Query 能单独配置,这样就会造成这两个值最后变得可有可无。
限流也是 GraphQL 的最难解决的难题之一,服务端不可能没有限流的,不然服务器稳定性就保障不了。对于 REST 来说接口的路由都是固定不变的,针对于不同的 URI 做限流是很容做到的。但是 GraphQL 限流的难点在哪里呢?
query maliciousQuery {
album(id: "some-id") {
photos(first: 9999) {
album {
photos(first: 9999) {
album {
photos(first: 9999) {
album {
#... Repeat this 10000 times...
}
}
}
}
}
}
}
}
这个请求会导致什么问题呢?客户端会发起一个请求 maliciousQuery,这个请求会去查询 some-id 的 album,这个 album 获取这个相册里的最大 9999 张图片,每个图片又要查询到所属的相册,就这么无限制的嵌套下去。这样的查询,服务器根本就影响不来,上面说到的复杂度和深度其实有点用处,但是用处不大。
曾经遇到这样的真实场景,GraphQL 项目已经部署线上,复杂度和深度也配置了,客户端同学在获取商品分页列表的同时也将对应商品详情以及商品详情的级联内容给取了出来,导致的结果服务器直接 OOM。原因跟上面这个例子很相似,就是嵌套的查询过多导致的。这个问题其实跟复杂度和深度是相关的,但是复杂度和深度真的是很难评估。
所以 GraphQL 限流的难题就在于客户端只发起一次请求,但是在服务器端可能被放大无数倍。如何能够有效的评估某个值能让客户端的嵌套比较合理,根据实践经验来看,这个值不是官方提供的复杂度和深度能解决的。
不过好消息 GraphQL 提供一种评估 GraphQL 限流的方式[4],另外一个中文版本的理论解析[解决 GraphQL 的限流难题] 。理论是一回事,不过实施起来依然是困难重重。
本文主要介绍了我曾经经历过的 GraphQL 落地的一点感悟,距离如今也有一段时间了,GraphQL 留给我的印象就停留在这些无法解决的问题上。曾经有人咨询我想用 GraphQL 去重构某个服务,被我比较激动的给打消了这个念头。这篇文章可能也有写的不对的地方,欢迎同学们指出。
[1]landscape: https://landscape.graphql.org/
[2]缓存: https://graphql.cn/learn/caching/
[3]有篇介绍 GraphQL 的实践的文章: https://tech.meituan.com/2021/05/06/bff-graphql.html
[4]GraphQL 限流的方式: https://medium.com/swlh/please-rate-limit-your-graphql-api-9832b5c64418
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/C7SEFuE2OojmBfSHzNnzlg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。