译者 | 崔皓
10年积累的成都网站设计、成都网站建设经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有怀柔免费网站建设让你可以放心的选择与我们合作。
审校 | 云昭
gRPC是由Google开发的一个高性能、通用的开源RPC框架,主要面向移动应用开发且基于HTTP/2协议标准而设计,同时支持大多数流行的编程语言。
GraphQL既是一种用于API的查询语言,且GraphQL对API中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让API更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
两者看起来并用途不相同,但其实在通信场景中很多开发者面临如何选择的问题。本文笔者带领大家从实用的角度去一一剖析gRPC与GraphQL的取舍之道!
本文主要介绍使用gRPC和GraphQL的时机,先说结论:推荐大家在客户端与服务器之间的通信场景中使用GraphQL,在服务器与服务器之间的通信场景使用gRPC。此外,文末会介绍关于例外情况的处理方式。
gRPC是由谷歌在2016年发布的,它是一种高效且对开发者友好的服务器间通信协议。GraphQL是由Meta公司在2015年发布的,是一种高效的、对开发者友好的客户端-服务器通信协议。两者都比REST有明显的优势,并且有很多共同点。
我们将在文章中花大量的篇幅比较它们的特点,然后总结每个协议的优点和缺点。最后,我们会描述每种协议都适合哪些特定的领域,以及什么时候会出现跨领域使用不同协议。
gRPC和GraphQL都是界面描述语言(IDL),描述了两台计算机如何进行通信。它们适用于不同的编程语言,我们可以使用codegen工具来生成多种语言的类型化接口。IDL抽象出了传输层;GraphQL与传输无关,但通常工作在HTTP之上,而gRPC则工作在HTTP/2之上。我们不需要知道传输层的细节,比如REST中的方法、路径、查询参数和正文格式。我们只需要知道针对一个独立的节点,如何使用高级客户端库与之通信。
gRPC使用协议缓冲区(又称protobufs),这是一种二进制格式,只包括数值(传递的内容),而GraphQL使用JSON,它是基于文本的,除了数值外还包括字段名(数值的含义)。二进制格式与较少的信息发送相结合,通常导致gRPC消息比GraphQL消息小。(虽然高效的二进制格式在GraphQL中是可行的,但它很少被使用,也不被大多数的库和工具所支持)。
影响消息大小的另一个方面是overfetching(过度获取):我们决定是否只请求特定的字段或总是接收所有的字段(通过避免"overfetching"过滤掉我们不需要的字段,只接受我们需要的字段)。因此,对于GraphQL而言利用了overfetching技术可以在请求中指定需要哪些字段,而在gRPC中,我们可以使用FieldMasks作为请求的可重用的过滤器,该过滤器和overfetching有异曲同工之妙。
gRPC使用二进制格式的另一个好处是,比GraphQL信息的序列化和解析更快。当然,缺点也很明显,它比JSON而言更难查看和调试,毕竟JSON的信息结构更加适合人类阅读。在Temporal项目中(开源微服务平台),我们默认使用protobuf的JSON格式,以利于开发者体验的可见性。(这失去了二进制格式带来的效率,但更看重效率的用户可以切换到二进制)。
gRPC不在消息中包括默认值,而GraphQL可以对参数进行默认值的设置,但不能对请求字段或响应类型进行默认值设置。这导致gRPC消息尺寸较小的另一个因素。它还影响了消费gRPCAPI的DX,在不设置输入字段和将该字段设置为默认值没有什么区别,默认值也是基于字段类型的值。我们不能把`behavior`枚举输入字段默认为`BEHAVIOR_FOO=2`--我们必须把默认值放在第一位(`BEHAVIOR_FOO=0`),这意味着它在总是默认值,或者我们遵循推荐的做法,定义一个`BEHAVIOR_UNSPECIFIED=0`枚举值。
enumBehavior{
behavior_unspecified=0。
behavior_foo=1;
behavior_bar=2;
}
API提供者需要传达UNSPECIFIED的意思(通过记录"unspecified将使用默认行为,目前是FOO"),消费者需要考虑服务器的默认行为在未来是否会改变(如果服务器在消费者正在创建的业务实体中保存了UNSPECIFIED/0值,而服务器后来改变了默认行为,那么该实体将会有所不同),以及这种改变是否是所期望的。如果并不希望如此,客户需要将该值设置为当前的默认值。
这种方式比gRPC版本更简单,这种方式可以知道如果不提供字段会发生什么,而且我们不需要考虑是否要自己传递默认值。
其他类型的默认值也有一些特殊情况。对于数字而言,有时默认的0是一个有效的值,而有时它将意味着一个不同的默认值。对于布尔型,默认的false会导致字段被负数命名。当我们在编码时给布尔变量命名时,我们使用正向命名。例如,我们通常会声明letretryable=true而不是letnnotallow=false。人们通常认为前者更易读,因为后者需要额外的步骤来理解双重否定("notRetryable是假的,所以它是可重试的")。但是如果我们有一个gRPCAPI,我们希望默认状态是可重试,那么我们就必须把这个字段命名为nonRetryable,因为可重试字段的默认值是false,就像gRPC中的所有布尔值一样。
在gRPC中,我们一次调用一个方法。如果我们需要的数据比一个方法所返回的要多,就需要调用多个方法。如果我们需要第一个方法的响应数据,以便知道下一步调用哪个方法,那么我们就会连续进行多次往返。除非我们和服务器在同一个数据中心,否则会造成很大的延迟。这个问题被称为underfetching,指请求的返回的数据信息不够,这样导致我们需要发多个请求去获取数据。
这也是GraphQL设计能够解决的问题之一。在高延迟的移动连接中,能够在一次请求中获得需要的所有数据,这一点特别重要。在GraphQL中,我们在请求中发送一个字符串,其中包括我们想要调用的所有方法(称为queries和mutations)以及基于第一层结果的嵌套数据。嵌套的数据可能需要从服务器到数据库的后续请求,但它们通常位于同一个数据中心,应该有亚毫秒级别的网络延迟。
GraphQL的请求灵活性让前端和后端团队的耦合度降低。前端开发者不需要等待后端开发者在方法的响应中添加更多的数据(这样客户端就可以在单个请求中接收数据),而是可以在请求中添加更多的查询或嵌套结果字段。当有一个涵盖整个数据的GraphQLAPI时,后端发生变化而导致前端团队时受阻的情况就会明显减少。
GraphQL请求指定了所有需要的数据字段,这意味着客户端可以使用声明式数据获取(declarativedatafetching):生命视图组件接下来所需要的数据,而不是强制性地获取数据(比如调用`grpcClient.callMethod()'),GraphQL客户端库将这些合并成一个请求并提供数据,在数据变化时也可以提供数据。在Web开发中使用React而不是jQuery:声明组件应该是什么样子,并在数据变化时让组件自动更新,而不是必须用jQuery操作DOM。
GraphQL请求格式的另一个效果是增加了可见性:服务器可以看到每个被请求的字段。可以跟踪字段的使用情况,看到客户何时停止使用已废弃的字段,这样就知道何时可以删除它们,而不用支持哪些应该被摒弃的东西。像ApolloGraphOS和Stellate这样的常用工具中都有跟踪功能。
gRPC和GraphQL都有很好的向前兼容性;也就是说,很容易以不破坏现有客户端的方式更新服务器。这对已经过时的移动应用程序来说特别重要,但对于加载在用户浏览器标签中的SPA在服务器更新后继续工作来说,也是必要的。
在gRPC中,你可以通过对字段进行数字排序、用新的数字添加字段,以及不改变现有字段的类型/数字来保持向前兼容。在GraphQL中,你可以添加字段,用`@deprecated"`指令废除旧的字段(并让它们发挥作用),并避免将可选参数改为必填。
gRPC和GraphQL都支持服务器向客户端传输数据:gRPC有serverstreaming,GraphQL有Subscriptions和指令@defer、@stream和@live。gRPC的HTTP/2也支持客户端和双向流(尽管当一方是浏览器时无法做到双向)。HTTP/2还通过多路复用提高了性能。
gRPC有内置的网络故障重试,该功能在GraphQL中没有直接体现,而是在与之对应的客户端库中支持网络故障重试,比如ApolloClient的RetryLink。
gRPC无法使用大多数API代理,比如ApigeeEdge,它是以HTTP头为操作对象的,而当客户端是浏览器时,我们需要使用gRPC-Web代理或Connect(虽然现代浏览器确实支持HTTP/2,但没有浏览器API允许对请求进行足够的控制)。默认情况下,GraphQL不使用GET缓存:许多HTTP缓存都是在GET请求上工作的,而大多数GraphQL库默认使用POST。GraphQL有许多使用GET的选项,包括将操作放在查询参数中(当操作字符串不太长时是可行的),建立时间持久化查询(通常只用于私有API),以及自动持久化查询。缓存指令可以在字段级提供(整个响应中最短的值被用于Cache-Control头的`max-age')。
GraphQL有一个模式,服务器为客户端开发人员发布并用于处理请求。它定义了所有可能的queries和mutations,以及所有的数据类型以及它们之间的关系。这种模式使来自多个服务的数据易于结合。GraphQL有schemastitching(将多个GraphQLAPI组合成一个代理schema的API)和federation(每个下游API声明如何关联共享类型,网关通过向下游API发出请求并结合结果自动解决请求)的概念,用于创建一个超图(所有数据的图,整合了较小的子图/部分模式)。也有一些库将其他协议代理给GraphQL,包括gRPC。
伴随着GraphQL的模式而来的是自检系统:以标准方式查询服务器的能力,以确定其能力是什么。所有的GraphQL服务器库都有自检功能,还有一些基于自省的高级工具,如GraphiQL、请求提示与graphql-eslint和ApolloStudio,其中包括一个带有字段自动完成、提示、自动生成文档和搜索查询的IDE。gRPC有自检功能,但它没有那么广泛,使用它的工具也比较少。
GraphQL模式实现了反应式的规范化客户端缓存:因为每个(嵌套的)对象都有一个类型字段,类型在不同的查询之间是共享的,我们可以告诉客户端哪个字段是类型的ID,客户端可以将数据对象进行规范化存储。这就实现客户端的功能,比如查询结果或更新会触发对视图组件的更新,而这些视图组件依赖于包含相同对象的不同查询。
gRPC和GraphQL类型之间的区别:
GraphQL虽然具有模式和灵活查询的特点,但是对于公共API来说,速率限制更加复杂(对于私有API,允许列出持久查询)。因为可以在一个请求中包含任意多的查询,而且查询可以任意嵌套的数据,所以不能仅仅限制客户端的请求数量。还需要对整个操作实施成本分析率限制,例如通过使用graphql-cost-analysis-cost-analysis库将各个领域的成本相加,并将其传递给漏斗算法。
以下将gRPc和GraphQL的异同以及优缺点通过摘要的方式呈现给大家,方便比较分析。
(1)gRPC优势
更快的网络传输
更快的序列化、解析和验证
然而,比JSON更难查看和调试
多重化
客户端和双向流媒体
(2)gRPC劣势
(3)GraphQL优势
(4)GraphQL劣势
(1)服务器到服务器(Server-to-Server)
在服务器到服务器的通信中,低延迟往往很重要,而且有时需要更多类型的流,gRPC是明确的标准。然而,在有些情况下,我们可能会发现GraphQL的一些好处更为重要。
还有一个问题是,我们是否应该自己做服务器到服务器的通信。对于数据获取(GraphQL的查询),这是获得响应的最快方式,但对于修改数据(变更),像MartinFowler的"同步调用被认为是有害的",也就导致我们使用异步、事件驱动的架构,在服务之间进行编排或协调。微服务模式建议在大多数情况下使用后者,为了保持DX和开发速度,我们需要一个基于代码的协调器而不是基于DSL的协调器。一旦在像Temporal这样基于代码的协调器中工作时,就不再提出网络请求--平台会可靠处理这一切。在我看来,这就是未来。
(2)客户端-服务器(Client-to-Server)
在客户-服务器通信中,延迟很高。我们希望能够在一次往返中获得所有数据,能够灵活地为不同的视图获取对应的数据,并拥有强大的缓存能力,所以GraphQL是明显的赢家。然而,在有些情况下,我们可以选择使用gRPC来代替。
原文链接:https://stackoverflow.blog/2022/11/28/when-to-use-grpc-vs-graphql/
崔皓,社区编辑,资深架构师,拥有18年的软件开发和架构经验,10年分布式架构经验。曾任惠普技术专家。乐于分享,撰写了很多热门技术文章,阅读量超过60万。《分布式架构原理与实践》作者。
当前名称:API开发,gRPC还是GraphQL?
分享网址:http://www.gawzjz.com/qtweb/news7/162557.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联