減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

2015年,F(xiàn)acebook推出了GraphQL(Graph-Query-Language)查詢語(yǔ)言。到目前為止,IBM、Twitter、Walmart Labs、紐約時(shí)報(bào)、Coursera等很多公司已經(jīng)在內(nèi)部從RESTful轉(zhuǎn)向GraphQL API。

作為一種查詢語(yǔ)言,GraphQL具有以下特點(diǎn):

(1)無(wú)需關(guān)心如何更新文檔,所有的查詢(query)和變更會(huì)自動(dòng)形成文檔(cchema)

(2)無(wú)需獲取整個(gè)數(shù)據(jù)集,通過(guò)schema與resolver(處理器)之間的映射關(guān)系,由對(duì)應(yīng)的resolver去獲取數(shù)據(jù),將結(jié)果返回給前端,從而可以編寫(xiě)僅僅返回所請(qǐng)求數(shù)據(jù)的查詢。

(3)對(duì)前端提供統(tǒng)一的訪問(wèn)點(diǎn)。從不同的API中獲取數(shù)據(jù)并非易事,GraphQL支持將所有API進(jìn)行拼接

愛(ài)奇藝號(hào)技術(shù)團(tuán)隊(duì)在實(shí)施微服務(wù)化的過(guò)程中,受到Forrester Research提出的低代碼開(kāi)發(fā)(Low-Code:即無(wú)需編碼或通過(guò)少量代碼就可以快速生成應(yīng)用程序的開(kāi)發(fā)理念)的啟發(fā),基于GraphQL構(gòu)建BFF(Backend for Frontends),幫助開(kāi)發(fā)人員用拖拽式操作,直觀地創(chuàng)建出一個(gè)供前端調(diào)用的API,本文將對(duì)實(shí)施過(guò)程中的經(jīng)驗(yàn)總結(jié)進(jìn)行敘述。

GraphQL介紹

與 RESTful API 一樣,GraphQL API設(shè)計(jì)用于處理 HTTP 請(qǐng)求并為這些請(qǐng)求提供響應(yīng)。REST API 構(gòu)建在請(qǐng)求方法和端點(diǎn)之間的連接上,而 GraphQL API 被設(shè)計(jì)為只通過(guò)一個(gè)端點(diǎn),始終使用 POST 請(qǐng)求進(jìn)行查詢,其 URL 通常是xxx.com/graphql。圖1-1為GraphQL部署架構(gòu)圖,可以看到它處于系統(tǒng)“中間層”

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖1-1

GraphQL 全稱叫 Graph Query Language,官方宣傳語(yǔ)是“為你的 API 量身定制的查詢語(yǔ)言”,用傳統(tǒng)的方式來(lái)解釋就是:將你所有后端 API 組成的集合看成一個(gè)數(shù)據(jù)庫(kù),用戶終端發(fā)送一個(gè)查詢語(yǔ)句,你的 GraphQL 服務(wù)解析這條語(yǔ)句并通過(guò)一系列規(guī)則從你的“API 數(shù)據(jù)庫(kù)”里面將查詢的數(shù)據(jù)結(jié)果返回給終端,而 GraphQL 就相當(dāng)于這個(gè)系統(tǒng)的一個(gè)查詢語(yǔ)言,像 SQL 之于 MySQL 一樣。GraphQL執(zhí)行過(guò)程如圖1-2所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖1-2

圖1-2是GraphQL 執(zhí)行的大致流程,第一步去驗(yàn)證查詢語(yǔ)句是否符合GraphQL的schema規(guī)范,確認(rèn)查詢內(nèi)容的合法性,第二步生成執(zhí)行的上下文,關(guān)鍵點(diǎn)在第三步和第四步,第三步是獲取查詢語(yǔ)句所需要查詢的字段,這里叫 fields,所有需要查詢的字段可以在查詢語(yǔ)句里拿到,這就是 GraphQL 如何做到避免返回冗余數(shù)據(jù)的。拿到所有需要查詢的字段后,第四步針對(duì)每一個(gè)字段去執(zhí)行它的 resolver,可以從 resolver 返回的數(shù)據(jù)里面拿到字段對(duì)應(yīng)的數(shù)據(jù),最后是格式化結(jié)果并返回。

重點(diǎn)是第四步,展開(kāi)說(shuō)明一下,如圖1-3、圖1-4所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖1-3

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖1-4

在GraphQL里面有一個(gè)概念叫類(lèi)型 (type),每一個(gè)類(lèi)型下面對(duì)應(yīng)的是一個(gè)或多個(gè)字段(field),每個(gè)字段都會(huì)綁定一個(gè)處理器(resolver),這個(gè) resolver 的作用就是獲取字段對(duì)應(yīng)的數(shù)據(jù)。

對(duì)應(yīng)到圖1-4所示,UserInfo這個(gè)類(lèi)型,它有三個(gè)字段:nickName、contractNo、fansNumber。每個(gè)字段都對(duì)應(yīng)一個(gè)resolver,resolver 需要被開(kāi)發(fā)者重新定義,否則會(huì)報(bào)錯(cuò)。所以UserInfo下的三個(gè)字段nickName、contractNo、fansNumber需要通過(guò)實(shí)現(xiàn)各自resolver來(lái)分別從用戶微服務(wù)、合同微服務(wù)、粉絲微服務(wù)去獲取用戶信息、合同信息和粉絲信息,然后再聚合返回,這樣就達(dá)到了使用 GraphQL 進(jìn)行數(shù)據(jù)拼接的目的。

BFF架構(gòu)

愛(ài)奇藝號(hào)在實(shí)施微服務(wù)化的過(guò)程中,加入了BFF的前后端架構(gòu),如圖2-1所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖2-1

從圖中可以看出,BFF作為前后端的中間層服務(wù)。主要的業(yè)務(wù)邏輯都封裝在BFF層,前端通過(guò)BFF進(jìn)行訪問(wèn),減少微服務(wù)之間的相互調(diào)用。BFF層通過(guò)REST API方式提供服務(wù),隨著服務(wù)的增多,提供的接口越來(lái)越多,這會(huì)導(dǎo)致REST API越來(lái)越冗余。對(duì)于前端而言,有的API粒度較粗不滿足需求;有的API又粒度太細(xì),不僅增加了響應(yīng)時(shí)間,還會(huì)造成流量的浪費(fèi)。對(duì)于后端而言,前端需要的數(shù)據(jù)往往在不同的地方具有相似性,但卻又不盡相同,比如:針對(duì)用戶信息,有些地方需要用戶簡(jiǎn)要的基礎(chǔ)信息和詳細(xì)的視頻信息,而有些地方卻需要用戶詳細(xì)的基礎(chǔ)信息和簡(jiǎn)要的視頻信息。這往往需要開(kāi)發(fā)不同的接口去滿足各種定制需求,增加了開(kāi)發(fā)人員的工作量,提升了開(kāi)發(fā)工作的重復(fù)度。

  • GraphQL與Rest API對(duì)比:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

表2-1

從表2-1中的對(duì)比可以看出,GraphQL相對(duì)于Rest API方式,性能更好,能有效減少前后端開(kāi)發(fā)溝通成本。但是Facebook的官方只有JS版本實(shí)現(xiàn),查詢方式和Rest API也有所不同(如表2-2所示),對(duì)于老項(xiàng)目有一定的遷移、學(xué)習(xí)成本。

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

表2-2

接下來(lái),本文將主要探討如何基于graphql-Java,做到減少遷移成本的同時(shí),又能提升后端開(kāi)發(fā)人員的效率,避免重復(fù)開(kāi)發(fā)。

愛(ài)奇藝號(hào)API生成平臺(tái)實(shí)踐

愛(ài)奇藝號(hào)API生成平臺(tái),是一個(gè)低代碼平臺(tái)。由于愛(ài)奇藝號(hào)的技術(shù)棧主要基于Java,所以使用的是GraphQL的 Java實(shí)現(xiàn)。基于graphql-java,API生成平臺(tái)主要做了以下功能優(yōu)化及增強(qiáng)。

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

(2)動(dòng)態(tài)接入監(jiān)控:動(dòng)態(tài)生成的API,與其他普通接口一樣支持Prometheus監(jiān)控,保證監(jiān)控的靈活性和服務(wù)的穩(wěn)定性。

(3)靈活配置:可以動(dòng)態(tài)生成GraphQL的schema,方便后端接入新服務(wù)。

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

接下來(lái)將對(duì)以上功能進(jìn)行詳細(xì)介紹:

1.支持Rest API

graphql-java通過(guò)Spring的封裝,位于整個(gè)架構(gòu)的網(wǎng)關(guān)層或BFF層。項(xiàng)目user-info-graphQL依賴graphql-java-spring,支持Rest API請(qǐng)求。平臺(tái)的整體架構(gòu)圖如圖3-1所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-1

User-info-graphQL的服務(wù)流程圖如圖3-2所示??蛻舳送ㄟ^(guò)graphql/前綴的Rest API方式請(qǐng)求,后端通過(guò)前綴與GraphQL Query綁定,從DB獲取映射關(guān)系,最終轉(zhuǎn)換成GraphQL支持的查詢語(yǔ)法。

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-2

2.動(dòng)態(tài)接入監(jiān)控

在user-info-graphQL項(xiàng)目中,原本是通過(guò)template url來(lái)匹配任意自定義url;導(dǎo)致監(jiān)控平臺(tái)只能顯示template url的請(qǐng)求信息,如圖3-3所示。這個(gè)問(wèn)題可以通過(guò)重寫(xiě)spring-boot-actuator中獲取tags的方法,將真實(shí)的url請(qǐng)求信息暴露到Spring boot的/actuator/prometheus端點(diǎn)這個(gè)方法來(lái)解決,如圖3-4所示。

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-3

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-4

通過(guò)暴露的監(jiān)控端點(diǎn)接入Prometheus,實(shí)現(xiàn)對(duì)新生成的API進(jìn)行實(shí)時(shí)動(dòng)態(tài)監(jiān)控,示例效果如圖3-5所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-5

3.靈活配置

為了方便后端快速接入新增微服務(wù),達(dá)到支持API動(dòng)態(tài)擴(kuò)展目的。項(xiàng)目中通過(guò)Velocity定義schema模板,通過(guò)Java注解、反射機(jī)制動(dòng)態(tài)生成graphqls模板文件,如圖3-6所示:

減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)減少重復(fù)開(kāi)發(fā),GraphQL在低代碼平臺(tái)如何落地?(graphql-go)

圖3-6

圖3-6中的GraphQL schema模板,支持通過(guò)用戶UID查詢用戶信息。用戶信息是由多個(gè)微服務(wù)聚合而成,采用異步調(diào)用多個(gè)微服務(wù)并行獲取數(shù)據(jù)?;诖四0?,用戶只需要實(shí)現(xiàn)SPI定義好的接口,就能實(shí)現(xiàn)對(duì)新增微服務(wù)的支持。
4.可視化API管理平臺(tái) 通過(guò)API生成管理平臺(tái),開(kāi)發(fā)人員可以實(shí)現(xiàn)API接口的可視化配置、生成、動(dòng)態(tài)監(jiān)控等功能,達(dá)到開(kāi)箱即用的效果,極大提升開(kāi)發(fā)和運(yùn)維效率。

總結(jié)

通過(guò)GraphQL動(dòng)態(tài)構(gòu)建BFF服務(wù)層API,聚合不同的微服務(wù),相比于Rest API方式,能夠減少后端重復(fù)開(kāi)發(fā),加快響應(yīng)前端需求。后端開(kāi)發(fā)人員只需要開(kāi)發(fā)維護(hù)新增微服務(wù),并通過(guò)SPI方式,增加BFF層對(duì)新增微服務(wù)的支持即可。
價(jià) 值:通過(guò)在愛(ài)奇藝號(hào)后端微服務(wù)引入GraphQL構(gòu)建BFF服務(wù)層,可以達(dá)成以下效果:

開(kāi)發(fā)方面的優(yōu)勢(shì)

  • 提升開(kāi)發(fā)效率:后端開(kāi)發(fā)人員職責(zé)分工明確,微服務(wù)與BFF層獨(dú)立開(kāi)發(fā)及維護(hù)。新增微服務(wù)接入方便,因BFF層對(duì)外的API支持動(dòng)態(tài)生成,所以無(wú)需更改BFF層的代碼,只需集中維護(hù)微服務(wù)。前端可以通過(guò)GraphQL的schema查看接口返回?cái)?shù)據(jù),減少前后端溝通成本。
  • 形成低代碼平臺(tái):隨著構(gòu)建的微服務(wù)基礎(chǔ)措施足夠完善,BFF層支持動(dòng)態(tài)生成API接口,極大減輕重復(fù)工作量。

運(yùn)行維護(hù)時(shí)的優(yōu)勢(shì)

  • 便于監(jiān)控:新增BFF層API接口,通過(guò)支持Prometheus端點(diǎn)監(jiān)控,無(wú)開(kāi)發(fā)成本。
  • 支持系統(tǒng)高吞吐量:BFF服務(wù)、微服務(wù)都是基于docker部署,支持QPS動(dòng)態(tài)擴(kuò)容,能夠支持高并發(fā)。
  • 便于維護(hù)和擴(kuò)展:基于GraphQL構(gòu)建的BFF層,API接口動(dòng)態(tài)生成,層次清晰,更易維護(hù)、擴(kuò)展。

難 點(diǎn):

  • 動(dòng)態(tài)擴(kuò)展查詢支持,目前schema的定義都是基于明確字段的情況下,如果需要支持動(dòng)態(tài)的查詢支持,需要支持動(dòng)態(tài)schema擴(kuò)展和解析。
  • 中文文檔少,F(xiàn)acebook官方只發(fā)布了JS實(shí)現(xiàn),Java實(shí)現(xiàn)都是基于開(kāi)源社區(qū),文檔和功能都不是很完善。

未來(lái)規(guī)劃:

隨著B(niǎo)FF端對(duì)API請(qǐng)求的多樣化,需要?jiǎng)討B(tài)支持新方法擴(kuò)展及監(jiān)控。目前API與請(qǐng)求的映射關(guān)系持久化在MySQL中,需要支持集群和高性能,后續(xù)逐步遷移到ZK或Redis中,并緩存到本地。


隨著云原生和K8s的興起,基于K8s部署的Go服務(wù),更易擴(kuò)容和維護(hù)?;贘ava實(shí)現(xiàn)的GraphQL,如果遷移到K8s上部署,很難實(shí)現(xiàn)快速擴(kuò)縮容的效果。而graphql-go在github上的star高達(dá)7k,可見(jiàn)熱度極高。如果基于Go實(shí)現(xiàn)BFF端,API與請(qǐng)求的映射關(guān)系可以存儲(chǔ)于K8s的Pod配置文件中,并且通過(guò)一個(gè)API部署一類(lèi)Pod,進(jìn)行服務(wù)隔離,可以更高程度的保證服務(wù)穩(wěn)定。

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號(hào)
公眾號(hào)
在線咨詢
分享本頁(yè)
返回頂部