構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)

原文鏈接:Building Microservices: Using an API Gateway

  1. 微服務(wù)介紹
  2. 構(gòu)建微服務(wù)之使用API網(wǎng)關(guān)(本文)
  3. 構(gòu)建微服務(wù)之:微服務(wù)架構(gòu)中的進(jìn)程間通信
  4. 微服務(wù)中的服務(wù)發(fā)現(xiàn)
  5. 微服務(wù)之事件驅(qū)動(dòng)的數(shù)據(jù)管理
  6. 選擇一種微服務(wù)部署策略
  7. 重構(gòu)單體應(yīng)用到微服務(wù)

對(duì)于設(shè)計(jì)、構(gòu)建和部署微服務(wù)系列七篇文章的第一篇,我們介紹了微服務(wù)架構(gòu)風(fēng)格,討論了微服務(wù)的優(yōu)勢(shì)和劣勢(shì),盡管微服務(wù)有些復(fù)雜,但仍然是構(gòu)建復(fù)雜應(yīng)用的一個(gè)明智選擇,第二篇文章將討論使用API網(wǎng)關(guān)構(gòu)建微服務(wù)。

當(dāng)我們選擇把應(yīng)用構(gòu)建成一組微服務(wù)的時(shí)候,我們需要決定應(yīng)用的客戶端如何與這些微服務(wù)進(jìn)行交互。傳統(tǒng)單體應(yīng)用中,往往只是一組(一般是replicated,負(fù)載均衡)的節(jié)點(diǎn),而在微服務(wù)架構(gòu)中,每個(gè)微服務(wù)都會(huì)暴露一組細(xì)粒度的節(jié)點(diǎn)。這篇文章中,我們將檢驗(yàn)這種方式如何影響客戶端與應(yīng)用端的通信,并且提出使用API網(wǎng)關(guān)的方式來(lái)解決這個(gè)問(wèn)題。

介紹

假設(shè)我們正在為一個(gè)商品應(yīng)用開(kāi)發(fā)一個(gè)原生移動(dòng)客戶端,我們應(yīng)該提供一個(gè)產(chǎn)品明細(xì)頁(yè)來(lái)展示指定產(chǎn)品的信息。
正如下圖所示,當(dāng)我們?cè)趤嗰R遜的安卓移動(dòng)應(yīng)用中滾動(dòng)產(chǎn)品明細(xì)頁(yè)時(shí),它將會(huì)呈現(xiàn)給我們:


Paste_Image.png

盡管這是移動(dòng)應(yīng)用,產(chǎn)品明細(xì)頁(yè)依然展示給我們很多信息,比如它不僅僅展示了產(chǎn)品的基本信息(比如名稱、描述、價(jià)格等),還展示了:

  • 購(gòu)物車中的條目數(shù)
  • 訂單歷史記錄
  • 用戶點(diǎn)評(píng)
  • 低庫(kù)存預(yù)警
  • 配送選項(xiàng)
  • 各項(xiàng)推薦,包括購(gòu)買本產(chǎn)品還經(jīng)常一起購(gòu)買了其它某產(chǎn)品,客戶買了這個(gè)產(chǎn)品同時(shí)還買了其他某產(chǎn)品,購(gòu)買該產(chǎn)品的用戶還瀏覽了哪些產(chǎn)品
  • 替代購(gòu)買選項(xiàng)

當(dāng)我們使用單體架構(gòu)模式的時(shí)候,一個(gè)移動(dòng)客戶端可能通過(guò)發(fā)送單一的REST調(diào)用請(qǐng)求(GET api.company.com/productdetails/productId) 來(lái)獲取展示的數(shù)據(jù),負(fù)載均衡器會(huì)把該請(qǐng)求路由到多個(gè)相同應(yīng)用實(shí)例的其中一臺(tái),應(yīng)用繼續(xù)查詢不同的數(shù)據(jù)庫(kù)表并返回請(qǐng)求數(shù)據(jù)給客戶端。

對(duì)應(yīng)的,在使用微服務(wù)架構(gòu)模式的時(shí)候,需要在產(chǎn)品明細(xì)頁(yè)展示的數(shù)據(jù)被多個(gè)微服務(wù)所擁有,下面是一些可能擁有需要展示數(shù)據(jù)在產(chǎn)品明細(xì)頁(yè)的微服務(wù):

  • 購(gòu)物車服務(wù):購(gòu)物車中的產(chǎn)品條目
  • 訂單服務(wù):訂單歷史
  • 目錄服務(wù):基本產(chǎn)品信息,比如名稱、圖片、價(jià)格等
  • 點(diǎn)評(píng)服務(wù):用戶點(diǎn)評(píng)
  • 庫(kù)存服務(wù):低庫(kù)存預(yù)警
  • 配送服務(wù):配送選項(xiàng)、時(shí)限以及來(lái)自配送提供者API計(jì)算出的費(fèi)用
  • 推薦服務(wù):建議購(gòu)買項(xiàng)


    Paste_Image.png

    我們需要決定移動(dòng)端如何訪問(wèn)這些服務(wù),先看下面的選項(xiàng):

客戶端直接與微服務(wù)通信

理論上客戶端可以直接與每一個(gè)微服務(wù)進(jìn)行通信,每個(gè)微服務(wù)將會(huì)有一個(gè)公開(kāi)的節(jié)點(diǎn)(https://serviceName.api.company.name**)),這個(gè)URL將會(huì)映射到負(fù)載均衡器,然后被分發(fā)到可用的實(shí)例上被處理,為了獲取產(chǎn)品明細(xì),移動(dòng)客戶端需要向上面列出的各個(gè)微服務(wù)發(fā)送請(qǐng)求。

非常不幸的是,這種方案有諸多挑戰(zhàn)和限制,問(wèn)題之一就是客戶端與每個(gè)微服務(wù)暴露出的細(xì)粒度API之間的不匹配,本例子中的客戶端需發(fā)送七個(gè)不同的請(qǐng)求,在一個(gè)更加復(fù)雜的應(yīng)用中請(qǐng)求數(shù)可能更多,比如亞馬遜在渲染產(chǎn)品頁(yè)的時(shí)候可能要調(diào)用上百個(gè)服務(wù)來(lái)渲染頁(yè)面,一個(gè)客戶端可以在LAN中發(fā)送多個(gè)請(qǐng)求,但是在公網(wǎng)上就特別低效,那就不用提在移動(dòng)設(shè)備上了,當(dāng)然,這種方式也使得客戶端異常的復(fù)雜。

客戶端直接調(diào)用微服務(wù)的另一個(gè)問(wèn)題是,一些服務(wù)可能使用對(duì)web并不友好的協(xié)議實(shí)現(xiàn)。一個(gè)服務(wù)可能使用Thrift二進(jìn)制的RPC而另一個(gè)服務(wù)可能使用AMQP消息協(xié)議。這些協(xié)議都不是瀏覽器和防火墻友好的,最好是在應(yīng)用內(nèi)部被使用。防火墻之外呢,應(yīng)用最好使用HTTP或者WebSocket。

這種方式另一個(gè)劣勢(shì)是使得微服務(wù)重構(gòu)變得困難,隨著時(shí)間推移,我們可能需要重新劃分、組織微服務(wù),比如我們可能合并兩個(gè)微服務(wù),也可能把某微服務(wù)拆分為兩個(gè)或多個(gè),如果客戶端直接與微服務(wù)交互的話,對(duì)這些微服務(wù)進(jìn)行重構(gòu)變得異常困難。

正是由于這些問(wèn)題,采用客戶端直接調(diào)用微服務(wù)的方式并不明智。

使用API網(wǎng)關(guān)

通常更好的方式是使用大家都熟知的API網(wǎng)關(guān),API網(wǎng)關(guān)是提供系統(tǒng)唯一入口的一臺(tái)服務(wù)器,它和面向?qū)ο笤O(shè)計(jì)模式中的門面類似:API網(wǎng)關(guān)封裝了內(nèi)部的系統(tǒng)架構(gòu)并向每個(gè)客戶端提供裁剪的API,它也可能負(fù)責(zé)諸如用戶驗(yàn)證、監(jiān)控、負(fù)載均衡、緩存、請(qǐng)求改造和管理以及靜態(tài)內(nèi)容響應(yīng)等職責(zé)。

下圖展示了API網(wǎng)關(guān)通常適應(yīng)的架構(gòu):


Paste_Image.png

API網(wǎng)關(guān)負(fù)責(zé)請(qǐng)求路由、組合以及協(xié)議轉(zhuǎn)換。所有來(lái)自客戶端的請(qǐng)求都先經(jīng)過(guò)API網(wǎng)關(guān),然后被路由分配到相應(yīng)的微服務(wù)中,API網(wǎng)關(guān)通常調(diào)用多個(gè)微服務(wù)并聚合其結(jié)果來(lái)處理請(qǐng)求,它可以在HTTP或者WebSocket這些web友好協(xié)議與內(nèi)部使用的web不友好協(xié)議間相互轉(zhuǎn)換。

API網(wǎng)關(guān)可以為每個(gè)客戶提供定制化的API,它通常為移動(dòng)客戶端暴露粗粒度的API,比如提供(/productdetails?productid=xxx**)節(jié)點(diǎn)使得移動(dòng)應(yīng)用單一請(qǐng)求就能獲取所有的產(chǎn)品明細(xì)。API網(wǎng)關(guān)調(diào)用產(chǎn)品信息、推薦、評(píng)分等服務(wù),組合這些結(jié)果來(lái)處理客戶端請(qǐng)求。

一個(gè)非常牛的例子就是Netflix API網(wǎng)關(guān),Netflix 流服務(wù)在上百種包含電視、機(jī)頂盒、智能手機(jī)、游戲系統(tǒng)、平板電腦等設(shè)備上都可用。起初Netflix想為它們的流服務(wù)提供一種 one?size?fits?all API,然而,他們發(fā)現(xiàn)由于設(shè)備的不同劃分以及獨(dú)特需求,這樣設(shè)計(jì)是不現(xiàn)實(shí)的。現(xiàn)在他們使用API網(wǎng)關(guān)通過(guò)運(yùn)行設(shè)備相關(guān)的適配器代碼為客戶端提供裁剪的API,適配器通常為每個(gè)請(qǐng)求調(diào)用平均六到七個(gè)后臺(tái)服務(wù), Netflix API網(wǎng)關(guān)現(xiàn)在每天處理上億請(qǐng)求。

使用API網(wǎng)關(guān)的優(yōu)勢(shì)與劣勢(shì)

正如你所想,使用API網(wǎng)關(guān)有優(yōu)勢(shì)也有劣勢(shì)。一個(gè)巨大優(yōu)勢(shì)就是它封裝了應(yīng)用的內(nèi)部結(jié)構(gòu),而不是讓客戶端直接調(diào)用每個(gè)服務(wù),客戶端只需要簡(jiǎn)單的與網(wǎng)關(guān)交互即可,另外API網(wǎng)關(guān)為不同客戶提供定制的API,并且減少了客戶端和應(yīng)用間的網(wǎng)絡(luò)調(diào)用,這也大大簡(jiǎn)化了客戶端代碼實(shí)現(xiàn)。

API網(wǎng)關(guān)也有一些劣勢(shì),它本身是一個(gè)新的高可用的組件,需要被開(kāi)發(fā)、部署和管理,同時(shí)API網(wǎng)關(guān)有可能成為開(kāi)發(fā)的瓶頸。開(kāi)發(fā)者為了暴露新的微服務(wù)節(jié)點(diǎn)必須更新API網(wǎng)關(guān),把更新網(wǎng)關(guān)的流程做的盡量輕量級(jí)是很重要的,不然的話,開(kāi)發(fā)者更新網(wǎng)關(guān)的時(shí)候就要被迫在線等待。盡管它有這些劣勢(shì),在實(shí)戰(zhàn)中,應(yīng)用使用API網(wǎng)關(guān)還是明智的選擇!

實(shí)現(xiàn)一個(gè)API網(wǎng)關(guān)

現(xiàn)在我們討論了API網(wǎng)關(guān)的動(dòng)機(jī)和一些權(quán)衡,現(xiàn)在來(lái)考慮一些設(shè)計(jì)的問(wèn)題吧:

性能與擴(kuò)展性

只有少數(shù)類似Netflix的公司需要每天處理上億的請(qǐng)求,然而,對(duì)大多數(shù)應(yīng)用來(lái)講,API網(wǎng)關(guān)的性能和擴(kuò)展性通常也非常的重要。在一個(gè)支持異步非阻塞IO的平臺(tái)上構(gòu)建API網(wǎng)關(guān)是明智的選擇,我們有多種技術(shù)可以用來(lái)實(shí)現(xiàn)可擴(kuò)展的API網(wǎng)關(guān)?;贘VM你可以選擇基于NIO的諸如Netty、Vertx、Spring Reactor或JBoss Undertow等框架,Node.js也是一個(gè)流行的選項(xiàng),它是一個(gè)構(gòu)建于Chrome JS引擎的平臺(tái),另一選擇是使用NGINX Plus,它提供了成熟、可擴(kuò)展、高性能的web服務(wù)器和反向代理,并可以方便的被部署、配置和編程, NGINX Plus 可以管理用戶校驗(yàn)、權(quán)限控制、請(qǐng)求負(fù)載均衡、響應(yīng)緩存以及應(yīng)用級(jí)別的健康檢查和監(jiān)控。

使用響應(yīng)式編程模型

API網(wǎng)關(guān)通過(guò)簡(jiǎn)單路由到相應(yīng)后臺(tái)服務(wù)來(lái)處理請(qǐng)求,通過(guò)調(diào)用多個(gè)后臺(tái)服務(wù)并聚合結(jié)果來(lái)處理它。對(duì)于一些請(qǐng)求,比如產(chǎn)品明細(xì)請(qǐng)求,后端對(duì)應(yīng)的服務(wù)是彼此獨(dú)立的,為減少請(qǐng)求時(shí)間,API網(wǎng)關(guān)應(yīng)該并行的處理這些請(qǐng)求。然而有時(shí)候,請(qǐng)求之間是有依賴關(guān)系的,API網(wǎng)關(guān)可能在路由請(qǐng)求到后臺(tái)服務(wù)之前先去調(diào)用用戶校驗(yàn)服務(wù)驗(yàn)證請(qǐng)求的合理性,類似的,在獲取用戶心愿單中的上的產(chǎn)品信息的時(shí)候,API網(wǎng)關(guān)必須先獲取包含那些信息的用戶檔案再去獲取每個(gè)產(chǎn)品的信息,另一個(gè)有趣的例子就是Netflix Video Grid

使用傳統(tǒng)的異步回調(diào)方式來(lái)寫API組合代碼很快就會(huì)把你帶進(jìn)回調(diào)地獄。代碼將會(huì)變得糾纏不清、難以理解也容易出錯(cuò)。更好的方式是使用響應(yīng)式方法來(lái)寫聲明式風(fēng)格的API網(wǎng)關(guān)代碼,比如,響應(yīng)式抽象包括Scala中的 Future 、Java 8中的CompletableFuture 以及JavaScript中的Promise ,還有微軟為.NET開(kāi)發(fā)的Reactive Extensions (also called Rx or ReactiveX), Netflix為了API網(wǎng)關(guān)的使用為基于JVM規(guī)范創(chuàng)造了RxJava,當(dāng)然還有為JavaScript創(chuàng)造的RxJS ,可以運(yùn)行在瀏覽器和Node.js中。使用響應(yīng)式風(fēng)格將會(huì)使你寫出更簡(jiǎn)單更高效的API網(wǎng)關(guān)代碼。

服務(wù)調(diào)用

微服務(wù)架構(gòu)的應(yīng)用是采用進(jìn)程間通信的分布式系統(tǒng),存在兩種進(jìn)程間通訊的方式:一種是采用異步基于消息機(jī)制的通信,比如使用消息中介產(chǎn)品 JMS 或者AMQP,當(dāng)然還有 Zeromq服務(wù)直接調(diào)用的無(wú)中介消息產(chǎn)品;另一種方式是使用HTTP或者Thrift這種同步機(jī)制進(jìn)行通信,一個(gè)系統(tǒng)應(yīng)該同時(shí)使用同步和異步風(fēng)格,甚至為每種方式使用不同的實(shí)現(xiàn),因此,API網(wǎng)關(guān)也必須支持這些不同的通信機(jī)制。

服務(wù)發(fā)現(xiàn)

API網(wǎng)關(guān)需要知道和它通信的每個(gè)服務(wù)的地址(IP地址和端口),在一個(gè)傳統(tǒng)應(yīng)用中,你可能硬編碼,但在一個(gè)流行的,基于云的微服務(wù)應(yīng)用中,這就是一個(gè)大問(wèn)題了?;A(chǔ)架構(gòu)服務(wù),比如消息中介,通常有一個(gè)靜態(tài)地址,我們可以在系統(tǒng)環(huán)境變量中之指定,然而,獲取一個(gè)應(yīng)用服務(wù)的地址就不是一件簡(jiǎn)單的事情了,應(yīng)用服務(wù)擁有動(dòng)態(tài)分配的地址,而且,一組服務(wù)實(shí)例可能因?yàn)樽詣?dòng)擴(kuò)展或升級(jí)而動(dòng)態(tài)的變化,因此,API網(wǎng)關(guān)應(yīng)該像系統(tǒng)中的其他服務(wù)客戶端一樣,需要服務(wù)發(fā)現(xiàn)機(jī)制:要么是服務(wù)端發(fā)現(xiàn) 或者是 客戶端發(fā)現(xiàn)。稍后的文章將會(huì)詳細(xì)介紹服務(wù)發(fā)現(xiàn)的問(wèn)題,現(xiàn)在,我們有必要意識(shí)到,如果系統(tǒng)使用客戶端服務(wù)發(fā)現(xiàn)的話,API網(wǎng)關(guān)應(yīng)該能夠查詢服務(wù)注冊(cè) Service Registry,服務(wù)注冊(cè)是所有服務(wù)實(shí)例登記其地址的數(shù)據(jù)庫(kù)。

處理局部故障

實(shí)現(xiàn)API網(wǎng)關(guān)時(shí)需要強(qiáng)調(diào)的另一個(gè)問(wèn)題是局部故障。這個(gè)問(wèn)題在分布式系統(tǒng)中很常見(jiàn),比如一個(gè)服務(wù)可能調(diào)用另一個(gè)響應(yīng)很慢或者不可用的服務(wù),API網(wǎng)關(guān)千萬(wàn)不要在等待已經(jīng)掛掉服務(wù)響應(yīng)的時(shí)候阻塞。當(dāng)然,如何處理錯(cuò)誤取決于具體的應(yīng)用場(chǎng)景或者具體因?yàn)槟膫€(gè)服務(wù)掛掉:比如,如果產(chǎn)品明細(xì)場(chǎng)景中的推薦服務(wù)掛掉了,那么API網(wǎng)關(guān)還是應(yīng)該返回其他的產(chǎn)品信息,保障產(chǎn)品對(duì)用戶仍然可以使用,推薦列表可以返回空或者預(yù)先硬編碼的Top 10商品,但是如果產(chǎn)品信息服務(wù)掛掉的話,API網(wǎng)關(guān)就要返回客戶一個(gè)錯(cuò)誤了。

API網(wǎng)關(guān)如果可能話也可以返回緩存的數(shù)據(jù),比如,由于產(chǎn)品價(jià)格很少變化,API網(wǎng)關(guān)可以在價(jià)格服務(wù)不可用時(shí)使用緩存,數(shù)據(jù)可能是API網(wǎng)關(guān)自己緩存,也可能緩存在諸如Redis和Memcached這樣的外部緩存中。通過(guò)返回默認(rèn)值或者緩存值,API網(wǎng)關(guān)確保局部故障不會(huì)影響用戶體驗(yàn)。

Netflix Hystrix在寫調(diào)用遠(yuǎn)端服務(wù)代碼時(shí)候是非常有用的,Hystrix 會(huì)標(biāo)記超過(guò)特定閥值的調(diào)用為超時(shí),它還實(shí)現(xiàn)了斷路器模式來(lái)阻止更多請(qǐng)求繼續(xù)調(diào)用沒(méi)有響應(yīng)的服務(wù),如果一個(gè)服務(wù)的出錯(cuò)率超過(guò)了指定閥值,它會(huì)觸發(fā)斷路器,使得所有的請(qǐng)求快速失敗一段時(shí)間,Hystrix也允許你定義請(qǐng)求失敗時(shí)的fallback動(dòng)作 ,比如讀取緩存或者返回一個(gè)默認(rèn)值。如果你使用JVM,那么希望你一定考慮使用Hystrix,如果你不使用JVM,那也要有類似的工具來(lái)幫助你。

總結(jié)

對(duì)大多數(shù)基于微服務(wù)的應(yīng)用來(lái)講,實(shí)現(xiàn)API網(wǎng)關(guān)是明智的,API網(wǎng)關(guān)就是一個(gè)應(yīng)用的單一入口,它還負(fù)責(zé)路由請(qǐng)求、組合、協(xié)議轉(zhuǎn)換等工作,它為每個(gè)應(yīng)用的客戶端提供定制化的API,它也可以通過(guò)返回默認(rèn)值或緩存值來(lái)處理失敗,下篇文章我們討論服務(wù)間的通信問(wèn)題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容