GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)_第1頁
GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)_第2頁
GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)_第3頁
GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)_第4頁
GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)_第5頁
已閱讀5頁,還剩35頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

GraphQL在微服務(wù)架構(gòu)中的實踐架構(gòu)

目錄

GraphQL是什么?1

GraphQL在微服務(wù)架構(gòu)中的使用2

GraphQL在實踐過程中遇到的棘手問題3

合理的GraphQL微服務(wù)架構(gòu)的設(shè)計4

GraphQL是什么?

簡樸對象訪問合同(SOAP)從今天來看已經(jīng)是一門非常古老的Web服務(wù)

技術(shù)了,雖然諸多服務(wù)仍然在使用遵循SOAP的接口,但是到今天REST風格

的面向資源的API接口已經(jīng)非常進一步人心,也非常的成熟;但是這篇文章要

簡介的主角其實是另一門更加復(fù)雜、完備的查詢語言GraphQL.

作為Facebook在年推出的查詢語言,GraphQL可以對API中的數(shù)據(jù)提

供一套易于理解的完整描述,使得客戶端可以更加精確的獲得它需要的數(shù)據(jù),目

前涉及FacebookTwitter.GitHub在內(nèi)的諸多公司都已經(jīng)在生產(chǎn)環(huán)境使用

GraphQL提供API;其實無論我們與否決定生產(chǎn)環(huán)境中使用GraphQL,它的

確是一門值得學習的技術(shù)。

二、GraphQL在微服務(wù)架構(gòu)中的使用

類型系統(tǒng)

GraphQL的強大體現(xiàn)能力重要還是來自于它完備的類型系統(tǒng),與REST

不同,它將整個Web服務(wù)中的所有資源當作一種有連接的圖,而不是一種個

資源孤島,在訪問任何資源時都可以通過資源之間的連接訪問其他的資源???/p>

如上圖所示,當我們訪問User資源時,就可以通過GraphQL中的連接訪問

目前User的Repo和Issue等資源,我們不再需要通過多種REST的接口

分別獲取這些資源,只需要通過如下所示的查詢就能一次性拿到所有的成果:

user{

id

email

username

repos(first:10){

id

url

name

issues(first:20){

id

author

title}

)

)

)

GraphQL這種方式可以將原有RESTful風格時的多次祈求聚合成一次祈

求,不僅可以減少多次祈求帶來的延遲,還可以減少服務(wù)器壓力,加快前端的渲

染速度。它的類型系統(tǒng)也非常豐富,除了標量、枚舉、列表和對象等類型之外,還

支持接口和聯(lián)合類型等高檔特性。口

為了可以更好的表達非空和空字段,GraphQL也引入了Non-Null等標記代

表非空的類型,例如String!表達非空的字符串。

schema{

queiy:Query

mutation:Mutation

)

Schema中絕大多數(shù)的類型都是一般的對象類型,但是每一種Schema

中均有兩個特殊類型:query和mutation,它們是GraphQL中所有查詢的入

口,在使用時所有查詢接口都是query的子字段所有變化服務(wù)器資源的祈求

都應(yīng)當屬于mutation類型。

集中式vs分散式

GraphQL以圖的形式將整個Web服務(wù)中的資源展示出來,其實我們可以理

解為它將整個Web服務(wù)以〃SQL〃的方式展示給前端和客戶端,服務(wù)端的資

源最后都被聚合到一張完整的圖上,這樣客戶端可以按照其需求自行調(diào)用,類似

添加字段的需求其實就不再需要后端多次修改了。

與RESTful不同,每一種的GraphQL服務(wù)其實對外只提供了一種用于調(diào)用內(nèi)

部接口的端點,所有的祈求都訪問這個暴露出來的端點。

GraphQLvsRESTful

(endpoint)

dr/posts

drAveMsa.M/ftpi/po?t?/2/c(NMi?nts

dravencss.?e/ap1/posts/2/m>thor

GraphQL事實上將多種HTTP祈求聚合成了一種祈求,它只是將多種

RESTful祈求的資源變成了一種從根資源Post訪問其她資源的Comment

和Author的圖,多和祈求變成了一種祈求的不同字段,從原有的分散式祈求

變成了集中式的祈求,這種方式非常適合單體服務(wù)直接對外提供GraphQL服

務(wù),可以在數(shù)據(jù)源和展示層建立一種非常清晰的分離,同步也可以通過某些強大

的工具,例如GraphiQL直接提供可視化的文檔;但是在業(yè)務(wù)復(fù)雜性指數(shù)提高

的今天,微服務(wù)架構(gòu)成為理解決某些問題時必不可少的解決方案,因此如何在微

服務(wù)架構(gòu)中使用GraphQL提高前后端之間的溝通效率并減少開發(fā)成本成為了

一種值得考慮的問題。

Relay原則

如果說RESTful其實是客戶端與服務(wù)端在HTTP合同通信時定義的固定原則,

那么Relay其實也是我們在使用GraphQL可以遵循的一套規(guī)范。

GRAPHQLANDRELAY

這種原則的浮現(xiàn)可以讓不同的工程師開發(fā)出較為相似的通信接口,在某些

場景下,例如標記對象和分頁這種常用的需求,引入設(shè)計良好的原則可以減少開

發(fā)人員之間的溝通成本。

Relay原則其實為三個與API有關(guān)的最常用的問題制定了某些規(guī)范:

提供可以重新獲取對象的機制;

提供對如何對連接進行分頁的描述;

原則化mutation祈求使它們變得更加可預(yù)測;

通過將上述的三個問題規(guī)范化,可以極大地增長前后端對于接口制定和對

接時的工作效率。

對象標記符

Node是Relay原則中定義的一種接口,所有遵循Node接口的類型都應(yīng)當

涉及一種id字段:

interfaceNode{

id:ID!

)

typeFaction:Node(

id:ID!

name:String

ships:ShipConnection

)

typeShip:Node{

id:ID!

name:String

)

Faction和Ship兩個類型都擁有標記符id字段,我們可以通過該標記

符重新從服務(wù)端取回相應(yīng)的對象,Node接口和字段在默認狀況下會假定整個

服務(wù)中的所有資源的id都是不同的,但是諸多時候我們都會將類型和id綁定

到一起,組合后才干一種類型特定的ID;為了保證id的不透明性,返回的id

往往都是Base64編碼的字符串,GraphQL服務(wù)器接受到相應(yīng)id時進行解碼

就可以得到有關(guān)的信息。

連接與分頁

在一種常用的數(shù)據(jù)庫中,一對多關(guān)系是非常常用的,一種User可以同步擁有

多種Post以及多種Comment,這些資源的數(shù)量在理論上不是有窮的,沒有

措施在同一種祈求所有返回,因此要對這部分資源進行分頁。

query{

viewer{

name

email

posts(first:1){

edge{

cursor

node{

title}

)

)

)

)

Relay通過抽象出的『連接模型』為一對多的關(guān)系提供了分片和分頁的支持,在

Relay看來當我們獲取某一種User相應(yīng)的多種Post時,其實是得到了一

種PostConnection,也就是一種連接:

{

"viewer":{

"name":"Draveness",

"email":

"posts":{

"edges":[

"cursor":"YXJyYXIjb25uZWNOaW9uOjI="z

"node":{

"title":"Posttitle;

)

]

)

)

)

在一種PostConnection中會存在多種PostEdge對象其中的cursor就

是我們用來做分頁的字段,所有的cursor其實都是Base64編碼的字符串,

這可以提示調(diào)用方cursor是一種不透明的指針,拿到目前cursor后就可以

將它作為after參數(shù)傳到下一種查詢中:

query{

viewer{

name

email

posts(first:1,after:MYXJyYXIjb25uZWNOaW9uOjI="){

edge{

cursor

node{

title}

)

)

)

)

當我們想要懂得目前頁與否是最后一頁時,其實只需要使用每一種連接中的

Pageinfo對象,其中涉及了諸多與分頁有關(guān)的信息,一種連接對象中一般均有

如下的構(gòu)造和字段,例如:Edge、Pageinfo以及游標和節(jié)點等。

PostConnection

|PostEdge

||cursor

|1-Post

1Pageinfo

|hasNextPage

|hasPreviousPage

IstartCursor

endCursor

Relay使用了非常多的功能在連接周邊構(gòu)建油象,讓我們可以更加以便地

管理客戶端中的游標,整個連接有關(guān)的規(guī)范其實特別復(fù)雜,可以閱讀Relay

CursorConnectionsSpecification理解更多與連接和游標有關(guān)的設(shè)計。

可變祈求

每一種Web服務(wù)都可以看做一種大型的復(fù)雜狀態(tài)機,這個狀態(tài)機對外提供兩

種不同的接口,一種接口是查詢接口,它可以查詢狀態(tài)機的目前狀態(tài),而另一種

接口是可以變化服務(wù)器狀態(tài)的可變操作,例如POST、DELETE等祈求。

WEBSERVERASASTATEMACHINE

Webserver

Mutation??J

按照商定,所有的可變祈求都應(yīng)當以動詞開頭并且它們的輸入都以Input結(jié)尾,

與之相相應(yīng)的,所有的輸出都以Payload結(jié)尾:

inputIntroduceShiplnput{

factionld:ID!

shipName:String!

clientMutationld:String!

)

typeIntroduceShipPayload{

faction:Faction

ship:Ship

clientMutationld:String!

)

除此之外,可變祈求還可以通過傳入clientMutationld保證祈求的幕等

性。

小結(jié)

Facebook的Relay原則其實是一種在GraphQL上對于常用領(lǐng)域問題的商

定,通過這種商定我們可以減少工程師的溝通成本和項目的維護成本并在多人

協(xié)作時保證服務(wù)對外提供接口的統(tǒng)一。

三、GraphQL在實踐過程中遇到的棘手問題

N+1問題

在老式的后端服務(wù)中,N+1杳詢的問題就常明顯由于數(shù)據(jù)庫中一對多的關(guān)

系非常常用,再加上目前大多服務(wù)都使用ORM取代了數(shù)據(jù)層,因此在諸多時

候有關(guān)問題都不會暴露出來,只有真正浮現(xiàn)性能問題或者慢查詢時才會發(fā)現(xiàn)。

SELECT*FROMusersLIMIT3;

SELECT*FROMpostsWHEREuserjd=1;

SELECT*FROMpostsWHEREuserjd=2;

SELECT*FROMpostsWHEREuserjd=3;

SELECT*FROMusersLIMIT3;

SELECT*FROMpostsWHEREuserjdIN(1,2,3);

GraphQL作為一種更靈活的API服務(wù)提供方式,相比于老式的Web服

務(wù)更容易浮現(xiàn)上述問題,類似的問題在浮現(xiàn)口寸也也許更加嚴重,因此我們更需要

避免N+1問題的發(fā)生。

數(shù)據(jù)庫層面的N+1查詢我們可以通過減少SQL查詢的次數(shù)來解決,-

般我們會將多種=查詢轉(zhuǎn)換成IN查詢;但是GraphQL中的N+1問題就

有些復(fù)雜了,特別是當資源需要通過RPC祈求從其她微服務(wù)中獲取時,更不能

通過簡樸的變化SQL查詢來解決。

N+1PROBLEM

ParentRequest

ChildRequest??

ChildRequest

ChaildRequest

在解決N+1問題之前,我們要真正理解如何解決這一類問題的核心邏輯,

也就是將多次查詢變成一次查詢,將多次操作變成一次操作,這樣可以減少由于

多次祈求增長的額外開銷——網(wǎng)絡(luò)延遲、祈求解析等;GraphQL使用了

DataLoader從業(yè)務(wù)層面解決了N+1問題,其核心邏輯就是整個多種祈求,

通過批量祈求的方式解決問題。

微服務(wù)架構(gòu)

微服務(wù)架構(gòu)在當下已經(jīng)成為了遇到業(yè)務(wù)異常復(fù)雜、團隊人數(shù)增長以及高并發(fā)

等需求或者問題時會使用的常用解決方案,當微服務(wù)架構(gòu)遇到GraphQL時就

會浮現(xiàn)諸多理論上的碰撞,會浮現(xiàn)非常多的使用措施和解決方案。

MONOLITHIC

MICROSERVICESARCHITECTURE

ARCHITECTURE

UserInterface

在這一節(jié)中,我們將簡介在微服務(wù)架構(gòu)中使月GraphQL會遇到哪些常用

的問題,對于這些問題有哪些解決方案需要權(quán)衡,同步也會分析GraphQL的

設(shè)計理念在融入微服務(wù)架構(gòu)中應(yīng)當注意什么。

當我們在微服務(wù)架構(gòu)中融入GraphQL的原則時,會遇到三個核心問題,

這些問題其實重要是從單體服務(wù)遷移到微服務(wù)架構(gòu)這種分布式系統(tǒng)時引入的一

系列技術(shù)難點,這些技術(shù)難點以及選擇之間的折衷是在微服務(wù)中實踐GraphQL

的核心。

Schema設(shè)計

GraphQL獨特的Schema設(shè)計其實為整個服務(wù)的架構(gòu)帶來了非常多的變

數(shù),如何設(shè)計以及暴露對外的接口決定了我們內(nèi)部應(yīng)當如何實現(xiàn)顧客的認證與

鑒權(quán)以及路由層的設(shè)計。

從總體來看,微服務(wù)架構(gòu)暴露的GraphQL接口應(yīng)當只有兩種;一種接口是分散

式的,每一種微服務(wù)對外暴露不同的端點,分別對外界提供服務(wù)。

GRAPHQLSCHEMADESIGN

在這種狀況下,流量的路由是根據(jù)顧客祈求的不同服務(wù)進行分發(fā)的,也就是我們

會有如下的某些GraphQLAPI服務(wù):

我們可以看到目前博客服務(wù)總共由內(nèi)容、評論以及訂閱三個不同的服務(wù)來提

供,在這時其實并沒有充足運用GraphQL服務(wù)的好處,當客戶端或前端同步

需要多種服務(wù)的資源時,需要分別祈求不同服務(wù)上的資源,并不能通過一次

HTTP祈求滿足所有的需求。

另一種方式其實提供了一種集中式的接口,所有的微服務(wù)對外共同暴露一種端

點,在這時流量的路由就不是根據(jù)祈求的URL了,而是根據(jù)祈求中不同的字段

進行路由。

這種路由的方式并不可以通過老式的nginx來做,由于在nginx看來整

個祈求其實只有一種URL以及某些參數(shù),我們只有解析祈求參數(shù)中的查詢才

干懂得客戶端究竟訪問了哪些資源。

祈求的解析其實是對一顆樹的解析,這部分解析其實是涉及業(yè)務(wù)邏輯的,在這里

我們需要懂得的是,這種Schema設(shè)計下的祈求是按照field進行路由的,

GraphQL其實協(xié)助我們完畢理解析查詢樹的過程,我們只需要對相應(yīng)字段實現(xiàn)

特定的Resolver解決返回的邏輯就可以了。

然而在多種微服務(wù)提供Schema時,我們需要通過一種機制將多種服務(wù)的

Schema整合起來,這種整合Schema的思路最重要的就是需要解決服務(wù)之

間的反復(fù)資源和沖突字段問題,如果多種服務(wù)需要同步提供同一種類型的基本

資源,例如:User可以從多種資源間接訪問到。

(

post(id:1){

user{

id

email}

id

title

content}

作為微服務(wù)的開發(fā)者或者提供方來講,不同的微服務(wù)之間的關(guān)系是平等的,我們

需要一種更高檔別或者更面向業(yè)務(wù)的服務(wù)對提供整合Schema的功能,保證服

務(wù)之間的字段與資源類型不會發(fā)生沖突。

GRAPHQLSERVICES

前綴

如何解決沖突資源從目前來看有兩種不同的方式,一種是為多種服務(wù)提供的資

源添加命名空間,一般來說就是前綴,在合并Schema時,通過添加前綴可以

避免不同服務(wù)浮現(xiàn)反復(fù)字段導致沖突的也許。

GRAPHQLSCHEMAPREFIXING

SourceGraph在實踐GraphQL時其實就使用了這種增長前綴的方式,

這種方式的實現(xiàn)成本比較低,可以迅速解決微服務(wù)中Schema沖突的問題,讀

者可以閱讀GraphQLatmassivescale:GraphQLastheglueina

microservicearchitecture一文理解這種做法的實現(xiàn)細節(jié);這種增長前綴解決

沖突的方式長處就是開發(fā)成本非常低,但是它將多種服務(wù)的資源看做孤島,沒有

措施將多種不同服務(wù)中的資源關(guān)系串聯(lián)起來,這對于中心化設(shè)計的GraphQL

來說其實會導致一定體驗上的丟失。

粘合

除了增長前綴這種在工程上開發(fā)成本非常低的措施之外,GraphQL官方提供了

一種名為SchemaStitching的方案,可以將不同服務(wù)的GraphQLSchema

粘合起來并對外暴露統(tǒng)一的接口,這種方式可以將多種服務(wù)中的不同資源粘合

起來,可以充足運用GraphQL的優(yōu)勢。

GRAPHQLSCHEMASTITCHING

schema

為了打通不同服務(wù)之間資源的壁壘、建立合理并且完善的GraphQLAPI,我們

其實需要付出某些額外的工作,也就是在上層完畢對公共資源的解決;當對整個

Schema進行合并時,如果遇到公共資源,就會選用特定的Resolver進行解

析,這些解析器的邏輯是在SchemaStitching時指定的。

constlinkTypeDefs='

extendtypeUser{

chirps:[Chirp]

)

我們需要在服務(wù)層上的業(yè)務(wù)層對服務(wù)之間的公共資源進行定義,并為這些公共

資源建立新的Resolver,當GraphQL解析當公共資源時,就會調(diào)用我們在合

并Schema時傳入的Resolver進行解析和解決。

constmergedSchema=mergeSchemas({

schemas:[

chirpSchema,

authorSchema,

linkTypeDefs,

],

resolvers:{

User:{

chirps:{

fragment:'...onUser{id}',

resolve(userzargs,context,info){

returninfo.mergeInfo.delegateToSchema({

schema:chirpSchema,

operation:'query,,

fieldName:'chirpsByAuthorld',

args:{

authorld:user.id,

},

context,

info,

});

},

},

},

},

});

在整個SchemaStitching的過程中,最重要的措施其實就是

mergeSchemas,它總共接受三個參數(shù),需要粘合的Schema數(shù)組、多種

Resolver以及類型浮或沖突時的回調(diào):

mergeSchemas({

schemas:Array<string|GraphQLSchema|

Array<GraphQLNamedType>>;

resolvers?:Array<IResolvers>|IResolvers;

onTypeConflict?:(

left:GraphQLNamedType,

right:GraphQLNamedType,

info?:{

left:{

schema?:GraphQLSchema;

);

right:{

schema?:GraphQLSchema;

);

},

)=>GraphQLNamedType;

})

SchemaStitching其實是解決多服務(wù)共同對外暴露Schema時比較好的

措施,這種粘合Schema的措施其實是GraphQL官方推薦的做法,同步它們

也為使用者提供了JavaScript的工具,但是它需要我們在合并Schema的地

方手動對不同Schema之間的公共資源以及沖突類型進行解決,還要定義某些

用于解析公共類型的Resolver;除此之外,目前GraphQL的Schema

Stitching功能對于除JavaScript之外的語言并沒有官方的支持,作為一種承

載了服務(wù)發(fā)現(xiàn)以及流量路由等功能的重要組件,穩(wěn)定是非常重要的,因此應(yīng)當謹

慎考慮與否應(yīng)當自研用于SchemaStitching組件。

組合

除了上述的兩種方式可以解決對外暴露單一GraphQL的問題之外,我們也可

以使用非常老式的RPC方式組合多種微服務(wù)的功能,對外提供統(tǒng)一的

GraphQL接口:

GRAPHQLSCHEMAWITHRPC

當我們使用RPC的方式解決微服務(wù)架構(gòu)下GraphQLSchema的問題時,

內(nèi)部的所有服務(wù)組件其實與其她微服務(wù)架構(gòu)中的服務(wù)沒有太多區(qū)別,它們者噲

對外提供RPC接口,只是我們通過另一種方式GraphQL整合了多種微服務(wù)

中的資源。

使用RPC解決微服務(wù)中的問題其實是一種比較通用同步也是比較穩(wěn)定的

解決方案,GraphQL作為一種中心化的接口提供方式,通過RPC調(diào)用其她服

務(wù)的接口并進行合并和整合其實也是一種比較合理的事情;在這種架構(gòu)下,我們

其實可以在提供GraphQL接口的狀況下,也讓各個微服務(wù)直接或者通過其她

業(yè)務(wù)組件對外暴露RESTful接口,提供更多的接入方式。

雖然RPC的使用能為我們的服務(wù)提供更多的靈活性,同步也可以將

GraphQL有關(guān)的功育然分到單獨的服務(wù)中,但是這樣給我們帶來了某些額外的

工作量,它需要工程師手動拼接各個服務(wù)的接口并對外提供GraphQL服務(wù)

在遇到業(yè)務(wù)需求變更時也也許會導致多種服務(wù)的修改和更新。

小結(jié)

從使用前綴、粘合到使用RPC組合各個微服務(wù)提供的接口,對外暴露的

Schema其實是一種曰點到面逐漸聚合的過程,同步實現(xiàn)的復(fù)雜度也會逐漸上

GRAPHQLSCHEMADESIGNTECHNICS

在這三種方式中,作者并不推薦使用前綴的方式隔離多種微服務(wù)提供的接

口,這種做法并沒有充足運用GraphQL的好處,不如使用RESTful將多種服

務(wù)的接口直接解耦,使用GraphQL反而是有某些濫用的感覺。

除了使用前綴的做法之外,無論是粘合還是組合都可以提供一種完整的

GraphQL接口,它們兩者都需要在直接對接顧客的GraphQL服務(wù)中對各個

微服務(wù)提供的接口進行整合,當我們使用SchemaStitching時,其實對背面

的服務(wù)提出了更多的規(guī)定—開發(fā)微服務(wù)的工程師需要掌握GraphQL

Schema的設(shè)計與開發(fā)措施,與此同步,各個微服務(wù)之間的類型也也許浮現(xiàn)中

突,需要在上層進行解決,但是這也減少了某些最前面的GraphQL服務(wù)的工

作量。

在最后,使用組合方式就意味著整個架構(gòu)中的GraphQL服務(wù)需要通過組

合RPC的方式解決與GraphQL有關(guān)的所有邏輯,相稱于把GraphQL有關(guān)

的所有邏輯都抽離到了最前面。

通過幾次架構(gòu)的重構(gòu)之后,在微服務(wù)架構(gòu)中,作者更傾向于使用RPC組合

各個微服務(wù)功能的方式提供GraphQL接口,雖然這樣帶來了更多的工作量,

但是卻能擁有更好的靈活性,也不需要其她微服務(wù)的開發(fā)者理解GraphQL有

關(guān)的設(shè)計規(guī)范以及商定讓各個服務(wù)的職責更加清晰與可控。

認證與授權(quán)

在一種常用的Web服務(wù)中,如何解決顧客的認證以及鑒權(quán)是一種比較核心的

問題,由于我們需要理解在使用GraphQL的服務(wù)中我們是如何進行顧客的認

證與授權(quán)的。

GRAPHQLAUTHS

Authentication

如果我們決定Web服務(wù)作為一種整體對外暴露的是Gr叩hQL的接口,

那么在很大限度上,Schema設(shè)計的方式?jīng)Q定了認證與授權(quán)應(yīng)當如何組織;作為

一篇簡介GraphQL在微服務(wù)架構(gòu)中實踐的文章,我們也自然會簡介在不同

Schema設(shè)計下,顧客的認證與授權(quán)方式應(yīng)當如何去做。

上一節(jié)中總共提到了三種不同的Schema設(shè)計方式,分別是:前綴、粘合和組

合,這些設(shè)計方式在最后都會給出一種如下所示的架構(gòu)圖:

GRAPHQLMICROSERVICES

使用GraphQL的所有構(gòu)造最后都會由一種中心化的服務(wù)對外接受來自客

戶端的GraphQL祈求哪怕它僅僅是一種代理,當我們有了這張GraphQL

服務(wù)的架構(gòu)圖,如何對顧客的認證與授權(quán)進行設(shè)計就變得非常清晰了。

認證

一方面,顧客的認證在多種服務(wù)中分別實現(xiàn)是大不合理的,如果需要在多種服務(wù)

中解決顧客認證有關(guān)的邏輯,相稱于將一種服務(wù)的職責同步分給了多種服務(wù)這

些服務(wù)需要共享顧客認證有關(guān)的表,users,sessions等等,因此在整個Web

服務(wù)中,由一種服務(wù)來解決顧客認證有關(guān)的邏輯是比較合適的。

GRAPHQLAUTHORIZATION

GraphQL

(authorization)

這個服務(wù)既可以是作為網(wǎng)關(guān)代理的GraphQL服務(wù)自身,也可以是一種獨

立的顧客認證服務(wù),在每次顧客祈求時都會通過RPC或者其她方式調(diào)用該服

務(wù)提供的接口對顧客進行認證,顧客的授權(quán)功能與認證就有某些不同了。

授權(quán)

我們可以選擇在GraphQL服務(wù)中增長授權(quán)的功能,也可以選擇在各個微服務(wù)

中判斷目前顧客與否對某一資源有權(quán)限進行操作,這其實是集中式跟分布式之

間的權(quán)衡,兩種方式均有各自的好處,前者將鑒權(quán)的權(quán)利留給了各個微服務(wù),讓

它們進行自治,根據(jù)其業(yè)務(wù)需要判斷祈求者與否可以訪問后者修改資源,而后者

其實把整個鑒權(quán)的過程解耦了,內(nèi)部的微服務(wù)無條件的信任來自GraphQL服

務(wù)的祈求并提供所有的服務(wù)。

AUTHORIZATION

GraphQL

Server

??.一

serviceservic*

(MIlittle^)

serviceservice

上面的設(shè)計其實都是在我們只需要對外提供一種GraphQL端點時進行的,當

業(yè)務(wù)需要同步提供B端、C端或者管理后臺的接口時,設(shè)計也許就完全不同了

在這時,如果我們將鑒權(quán)的_L作分給多種內(nèi)部的微服務(wù),每個服務(wù)都需要對

不同的GraphQL服務(wù)(或者Web服務(wù))提供不同的接口,然后分別進行鑒

權(quán);但是將鑒權(quán)的工作交給GraphQL服務(wù)就是一種比較好的方式了,內(nèi)部的微

服務(wù)不需要關(guān)懷調(diào)用者與否有權(quán)限訪問該資源,鑒權(quán)都由最外層的業(yè)務(wù)服務(wù)來

解決,實現(xiàn)了比較好的解耦。

固然,完全的信任其她0艮務(wù)的調(diào)用其實是一種比較危險的事情,對于某些重

要的業(yè)務(wù)或者祈求調(diào)用可以通過外部的風控系統(tǒng)進行二次檢查判斷目前祈求方

調(diào)用的合法性

GRAPHQLAUTHORIZATION

Business■Customer■Admin

C

noR

rtE

lo

如何實現(xiàn)一種完備并且有效的風控系統(tǒng)并不是這篇文章想要重要簡介的內(nèi)

容,讀者可以尋找有關(guān)的資料理解風控系統(tǒng)的原理以及模型。

小結(jié)

認證與授權(quán)的設(shè)計本來是系統(tǒng)中一件比較靈活的事情,無論我們與否在微服務(wù)

架構(gòu)中使用GraphQL作為對外的接口,將這部分邏輯交由直接對外暴露的服

務(wù)是一種比較好的選擇,由于直接對外暴露的服務(wù)中掌握了更多與目前祈求有

關(guān)的上下文,可以更容易地對來源顧客以及其權(quán)限進行認證,而重要或者高危的

業(yè)務(wù)操作可以通過額外增長風控服務(wù)管理風險,或者在路由層對RPC的調(diào)用

方通過白名單進行限制,這樣可以將不同的功能解耦,減少多種服務(wù)之間的反復(fù)

工作。

四、合理的GraphQL微服務(wù)架構(gòu)的設(shè)計

路由設(shè)計

作為微服務(wù)中非常重要的一部分,如何解決路由層的設(shè)計也是一種比較核

心的問題;但是與認證與鑒權(quán)相似的是,Schema的設(shè)計最后其實就決定了祈求

的路由如何去做。

GraphQLSchemaStitching其實已經(jīng)是一套涉及路由系統(tǒng)的GraphQL在微

服務(wù)架構(gòu)的解決方案了,它可以在網(wǎng)關(guān)服務(wù)器Resolve祈求時,通過HTTP

合同將相應(yīng)祈求的片段交由其她微服務(wù)進行解決,整個過程不需要手動介入只

有在類型浮現(xiàn)沖突時會執(zhí)行相應(yīng)的回調(diào)

GRAPHQLAUTHENTICATION

而組合的方式其實就相稱于要手動實現(xiàn)SchemaStitching中轉(zhuǎn)發(fā)祈求的

工作了,我們需要在對外暴露的GraphQL服務(wù)中實現(xiàn)相應(yīng)字段的解析器調(diào)用

其她服務(wù)提供的HTTP或者RPC接口取到相應(yīng)的數(shù)據(jù)。

在GraphQL中的路由設(shè)計其實與老式微服務(wù)架構(gòu)中的路由設(shè)計差不多,

只是GraphQL提供了Stitching的有關(guān)工具用來粘合不同服務(wù)中的

Schema并提供轉(zhuǎn)發(fā)服務(wù),我們可以選擇使用這種粘合的方式,也可以選擇在

Resolver中通過HTTP或者RPC的方式來自獲取顧客祈求的資源。

架構(gòu)的演進

從今年年初選擇使用GraphQL作為服務(wù)對外暴露的API到目前大概有半年

的事件,服務(wù)的架構(gòu)也在不斷演進和變化,在這個過程中的確經(jīng)歷了非常多的問

題,也一次一次地對既有的服務(wù)架構(gòu)進行調(diào)節(jié),整個演進的過程其實可以分為三

個階段從使用RPC組合的方式到SchemaStitching最后再回到使用

RPC。

GRAPHQLARCHITECTUREDEVELOPMENAT

雖然在整個架構(gòu)演進的過程中,最開始和最后選擇的技術(shù)方案雖然都是使

用RPC進行通信,但是在實現(xiàn)的細節(jié)上卻有著諸多的不同以及差別,這也是我

們在業(yè)務(wù)變得逐漸復(fù)雜的過程發(fā)現(xiàn)的。

中心化Schema與RPC

當整個項目剛剛開始啟動時,其實就已經(jīng)決定了使用微服務(wù)架構(gòu)進行開發(fā),但是

由于當時選擇使用的技術(shù)棧是Elixir+Phoenx,因此諸多基本設(shè)施并不完善,

例如gRPC以及Protobuf就沒有官方版本的日ixir實現(xiàn),雖然有某些開源

項目作者完畢的項目,但是都并不穩(wěn)定,因此最后決定了在RabbitMQ上簡樸

實現(xiàn)了一種基于消息隊列的RPC框架,并通過組合的方式對外提供GraphQL

的接口。

GRAPHQLMICROSERVICE

(RABBITMQRPC)

graphqX

service

RabbitMQ

RabbitMQ在微服務(wù)架構(gòu)中承當了消息總線的功能,所有的RPC祈求其

實都被轉(zhuǎn)換成了消息隊列中的消息”服務(wù)在調(diào)用RPC時會向RabbitMQ相應(yīng)

的隊列投遞一條消息并持續(xù)監(jiān)聽消息的回調(diào),等待其她服務(wù)的響應(yīng)。

這種做法的好處就是RabbitMQ中的隊列承當了『服務(wù)發(fā)現(xiàn)』的職能通

過隊列的方式將祈求方與服務(wù)方解耦,對RPC祈求進行路由,所如下游的消費

者(服務(wù)方)可以水平擴展,但是這種方式其實也可以由負載均衡來實現(xiàn),雖然

負載均衡由于并不清晰服務(wù)方的負載,因此在轉(zhuǎn)發(fā)祈求時的效果也許沒有月所

方作為消費者積極拉的效率高。

最核心的問題是,手搓的RPC框架作為基本服務(wù)如果沒有通過充足的測試以

及生產(chǎn)環(huán)境的考驗是不成熟的,并且作為語言無關(guān)的一種調(diào)用方式,我們也許需

要為諸多語言同步實現(xiàn)RPC框架,這其實就帶來了非常高的人力、測試和維護

成本,目前來看不是一種非??扇〉拇胧?/p>

如果我們拋開語言不談,在一種比較成熟的語言中使用RPC的方式進行通信,

的確能減少諸多開發(fā)和維護的成本,但是也有此外一種比較大的代價,當業(yè)務(wù)并

不穩(wěn)定需要常常變更時,內(nèi)部服務(wù)會常常為對外暴露的RPC接口添加額外的

字段,而這也會規(guī)定最前面的GraphQL服務(wù)做額外的工作:

GRAPHQLSERVCIEUPGRADE

GraphQL

每一次服務(wù)的修改都會導致三個有關(guān)服務(wù)或倉庫進行更新,這雖然是在微

服務(wù)架構(gòu)中是一件比較正常合理的事情,但是在項目的初期階段這會導致非常

多額外的工作量,這也是我們進行第一次架構(gòu)遷移的重要因素。

去中心化管理的Schema

這里的去中心化其實并不是指GraphQL對外暴露多種端點,而是指

GraphQL不同field的開發(fā)過程去中心化,為理解決中心化的Schema加上

RPC帶來的開發(fā)效率問題并且實踐GraphQL官方提供的SchemaStitching

解決方案,我們決定將Schema的管理去中心化,由各個微服務(wù)對外直接暴露

GraphQL祈求,同步將多種服務(wù)的Schema進行合并,以此來解決開發(fā)的效

率問題。

GRAPHQLMICROSERVICE

(SCHEMASTITCHING)

graphql

service

Stitch

使用SchemaStitching的方式可以將多種服務(wù)中不同的GraphQL

Schema粘合成一種更大的Schema,這種架構(gòu)下最核心的組件就是用于

Schema粘合的工具,在上面已經(jīng)說到,除了Javascript之外的其她語言并沒

有官方的工具支持,也沒有在生產(chǎn)環(huán)境中大規(guī)模使用,同步由于我們使用的也是

一種比較小眾的語言Elixir,因此更不存在一種可以拆箱即用的工具了。

通過評估之后,我們決定在GraphQLElixir實現(xiàn)Absinthe上進行一層包裝,

并對客戶端的祈求進行語法與語義的解析,將字段相應(yīng)的樹包裝成子查詢發(fā)送

給下游的服務(wù),最后再由最前面的GraphQL服務(wù)組合起來

GRAPHQLSCHEMASTITCHING

graphql

subquery

service

graphql

subquery

service

graphql

subquery

service

graphql

subquery

service

GraphQL前端服務(wù)總共涉及兩個核心組件,分別是GraphQLStitcher

和Dispatcher,其中前者負責向各個GraphQL服務(wù)祈求

IntrospectionQuery并將獲得的所有Schema粘合成一顆巨大的樹;當客戶

端進行祈求時,GraphqlDispatcher會通過語法解析目前的祈求,并將其中不

同的字段以及子字段轉(zhuǎn)換成樹后轉(zhuǎn)發(fā)給相應(yīng)的服務(wù)。

在實現(xiàn)GraphQLStitcher的過程中,需要格外注意不同服務(wù)之間類型沖

突的狀況,我們在實現(xiàn)的過程中并沒有支持類型沖突以及跨服務(wù)資源的問題而

是采用了覆蓋的方式,這其實有很大的問題,內(nèi)部的GraphQL服務(wù)其實并不

懂得整個Schema中有哪些類型是已經(jīng)被使用的,因此常常會導致服務(wù)之間的

類型沖突,我們只有在發(fā)現(xiàn)時手動增長前綴來解決沖突。

增長前綴是一種比較容易的解決沖突的措施,但是卻并不是特別的優(yōu)雅使

用這種方式的重要因素是,我們發(fā)現(xiàn)了由于權(quán)限系統(tǒng)的設(shè)計缺陷——在弓入

B端顧客時無法優(yōu)雅的實現(xiàn)鑒權(quán),因此選擇使用一種比較簡樸的措施臨時解決

類型沖突的問題。

在開發(fā)多種內(nèi)部服務(wù)時,我們通過scope的方式對顧客與否有權(quán)限讀寫資源做

了限制,內(nèi)部服務(wù)在執(zhí)行操作前會先檢查祈求的顧客與否可以讀寫該資源,然后

開始解決真正的業(yè)務(wù)邏輯,也就是說顧客鑒權(quán)是發(fā)生在所有的內(nèi)部服務(wù)中的。

當我們對外暴露的GraphQL服務(wù)僅僅是面向C端顧客的時候,使用scope

并且讓內(nèi)部服務(wù)進行鑒權(quán)其實可以滿足C端對于接口的需求,但是當我們需要

同步為B端顧客提供GraphQL或者RESTful接口時,這種鑒權(quán)方式其實就

非常尷尬了。

AUTHORIZATIONWITHSCOPE

Columns

在微服務(wù)架構(gòu)中,由于各個服務(wù)之間的數(shù)據(jù)庫是隔離的,對于一條數(shù)據(jù)庫記

錄來說,諸多內(nèi)部服務(wù)都只能懂得目前記錄屬于哪個顧客或者那些顧客,因此對

于scope來說傳遞資源、讀寫祈求加上來源顧客就可以讓解決祈求的服務(wù)判斷

目前的來源顧客與否有權(quán)限訪問該條記錄。

這種結(jié)論基于我們做的一條假設(shè)——微服務(wù)收到的所有祈求其實都規(guī)定

讀寫來源顧客擁有的資源,因此在引入B端顧客時就遇到了比較大的困難,我

們采用的臨時解決方案就是在目前顧客的scope中添加某些額外的信息并在

內(nèi)部服務(wù)中添加新的接口滿足B端查詢的需要,但是由于B端對于資源的查

詢規(guī)定也許非常多樣,當我們需要為不同的查詢接口進行不同的權(quán)限限制,并且

在B端顧客也不能訪問所有顧客的資源時,scope的方式就很難體現(xiàn)這種復(fù)雜

的鑒權(quán)需求。

在這種Schema管理去中心化的架構(gòu)中,我們遇到了兩個比較重要的問題:

用于SchemaStitching的組件對于Elixir語言并沒有官方或者大型開源項目

的支持,手搓的組件在承載較大的服務(wù)負載時會有很大的壓力,同步功能也有非

常多不完善的地方;

在內(nèi)部服務(wù)對于整個祈求沒有太多上下文的狀況下,一旦遇到復(fù)雜的鑒權(quán)需求

時,將鑒權(quán)交給內(nèi)部服務(wù)的的設(shè)計方式會導致服務(wù)之間的耦合增長——微服

務(wù)之間需要不斷傳遞祈求的上下文用于鑒權(quán),同歲也增長了開發(fā)的成本;

服務(wù)網(wǎng)格與RPC

使用去中心化管理的Schema雖然在一定限度上減少了開發(fā)的工作,但是在這

種架構(gòu)下我們也遇到了兩個不能接受的問題,為理解決這些問題,我們準備對當

前的技術(shù)架構(gòu)做出如下的修改,讓各個服務(wù)可以更加靈活的通信:

GRAPHQLMICROSERVICE

(SERVIC

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論