構(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ū)動的數(shù)據(jù)管理
  6. 選擇一種微服務(wù)部署策略
  7. 重構(gòu)單體應(yīng)用到微服務(wù)

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

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

介紹

假設(shè)我們正在為一個商品應(yīng)用開發(fā)一個原生移動客戶端梅鹦,我們應(yīng)該提供一個產(chǎn)品明細(xì)頁來展示指定產(chǎn)品的信息。
正如下圖所示冗锁,當(dāng)我們在亞馬遜的安卓移動應(yīng)用中滾動產(chǎn)品明細(xì)頁時齐唆,它將會呈現(xiàn)給我們:


Paste_Image.png

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

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

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

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

  • 購物車服務(wù):購物車中的產(chǎn)品條目
  • 訂單服務(wù):訂單歷史
  • 目錄服務(wù):基本產(chǎn)品信息,比如名稱土匀、圖片子房、價格等
  • 點評服務(wù):用戶點評
  • 庫存服務(wù):低庫存預(yù)警
  • 配送服務(wù):配送選項、時限以及來自配送提供者API計算出的費(fèi)用
  • 推薦服務(wù):建議購買項


    Paste_Image.png

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

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

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

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

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

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

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

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

通常更好的方式是使用大家都熟知的API網(wǎng)關(guān)勒极,API網(wǎng)關(guān)是提供系統(tǒng)唯一入口的一臺服務(wù)器,它和面向?qū)ο笤O(shè)計模式中的門面類似:API網(wǎng)關(guān)封裝了內(nèi)部的系統(tǒng)架構(gòu)并向每個客戶端提供裁剪的API虑鼎,它也可能負(fù)責(zé)諸如用戶驗證辱匿、監(jiān)控冀惭、負(fù)載均衡、緩存掀鹅、請求改造和管理以及靜態(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é)請求路由、組合以及協(xié)議轉(zhuǎn)換乐尊。所有來自客戶端的請求都先經(jīng)過API網(wǎng)關(guān)戚丸,然后被路由分配到相應(yīng)的微服務(wù)中,API網(wǎng)關(guān)通常調(diào)用多個微服務(wù)并聚合其結(jié)果來處理請求扔嵌,它可以在HTTP或者WebSocket這些web友好協(xié)議與內(nèi)部使用的web不友好協(xié)議間相互轉(zhuǎn)換限府。

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

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

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

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

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

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

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

性能與擴(kuò)展性

只有少數(shù)類似Netflix的公司需要每天處理上億的請求微峰,然而,對大多數(shù)應(yīng)用來講抒钱,API網(wǎng)關(guān)的性能和擴(kuò)展性通常也非常的重要蜓肆。在一個支持異步非阻塞IO的平臺上構(gòu)建API網(wǎng)關(guān)是明智的選擇,我們有多種技術(shù)可以用來實現(xiàn)可擴(kuò)展的API網(wǎng)關(guān)谋币≈⑿樱基于JVM你可以選擇基于NIO的諸如Netty、Vertx瑞信、Spring Reactor或JBoss Undertow等框架厉颤,Node.js也是一個流行的選項,它是一個構(gòu)建于Chrome JS引擎的平臺凡简,另一選擇是使用NGINX Plus逼友,它提供了成熟、可擴(kuò)展秤涩、高性能的web服務(wù)器和反向代理帜乞,并可以方便的被部署、配置和編程筐眷, NGINX Plus 可以管理用戶校驗黎烈、權(quán)限控制、請求負(fù)載均衡匀谣、響應(yīng)緩存以及應(yīng)用級別的健康檢查和監(jiān)控照棋。

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

API網(wǎng)關(guān)通過簡單路由到相應(yīng)后臺服務(wù)來處理請求,通過調(diào)用多個后臺服務(wù)并聚合結(jié)果來處理它武翎。對于一些請求烈炭,比如產(chǎn)品明細(xì)請求,后端對應(yīng)的服務(wù)是彼此獨立的宝恶,為減少請求時間符隙,API網(wǎng)關(guān)應(yīng)該并行的處理這些請求趴捅。然而有時候,請求之間是有依賴關(guān)系的霹疫,API網(wǎng)關(guān)可能在路由請求到后臺服務(wù)之前先去調(diào)用用戶校驗服務(wù)驗證請求的合理性拱绑,類似的,在獲取用戶心愿單中的上的產(chǎn)品信息的時候丽蝎,API網(wǎng)關(guān)必須先獲取包含那些信息的用戶檔案再去獲取每個產(chǎn)品的信息猎拨,另一個有趣的例子就是Netflix Video Grid

使用傳統(tǒng)的異步回調(diào)方式來寫API組合代碼很快就會把你帶進(jìn)回調(diào)地獄征峦。代碼將會變得糾纏不清迟几、難以理解也容易出錯。更好的方式是使用響應(yīng)式方法來寫聲明式風(fēng)格的API網(wǎng)關(guān)代碼栏笆,比如类腮,響應(yīng)式抽象包括Scala中的 Future 、Java 8中的CompletableFuture 以及JavaScript中的Promise 蛉加,還有微軟為.NET開發(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)格將會使你寫出更簡單更高效的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)用的無中介消息產(chǎn)品苞七;另一種方式是使用HTTP或者Thrift這種同步機(jī)制進(jìn)行通信藐守,一個系統(tǒng)應(yīng)該同時使用同步和異步風(fēng)格,甚至為每種方式使用不同的實現(xiàn)蹂风,因此卢厂,API網(wǎng)關(guān)也必須支持這些不同的通信機(jī)制。

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

API網(wǎng)關(guān)需要知道和它通信的每個服務(wù)的地址(IP地址和端口)惠啄,在一個傳統(tǒng)應(yīng)用中慎恒,你可能硬編碼,但在一個流行的撵渡,基于云的微服務(wù)應(yīng)用中融柬,這就是一個大問題了±驯眨基礎(chǔ)架構(gòu)服務(wù)丹鸿,比如消息中介,通常有一個靜態(tài)地址棚品,我們可以在系統(tǒng)環(huán)境變量中之指定靠欢,然而,獲取一個應(yīng)用服務(wù)的地址就不是一件簡單的事情了铜跑,應(yīng)用服務(wù)擁有動態(tài)分配的地址门怪,而且,一組服務(wù)實例可能因為自動擴(kuò)展或升級而動態(tài)的變化锅纺,因此掷空,API網(wǎng)關(guān)應(yīng)該像系統(tǒng)中的其他服務(wù)客戶端一樣,需要服務(wù)發(fā)現(xiàn)機(jī)制:要么是服務(wù)端發(fā)現(xiàn) 或者是 客戶端發(fā)現(xiàn)囤锉。稍后的文章將會詳細(xì)介紹服務(wù)發(fā)現(xiàn)的問題坦弟,現(xiàn)在,我們有必要意識到官地,如果系統(tǒng)使用客戶端服務(wù)發(fā)現(xiàn)的話酿傍,API網(wǎng)關(guān)應(yīng)該能夠查詢服務(wù)注冊 Service Registry,服務(wù)注冊是所有服務(wù)實例登記其地址的數(shù)據(jù)庫驱入。

處理局部故障

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

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

Netflix Hystrix在寫調(diào)用遠(yuǎn)端服務(wù)代碼時候是非常有用的图仓,Hystrix 會標(biāo)記超過特定閥值的調(diào)用為超時罐盔,它還實現(xiàn)了斷路器模式來阻止更多請求繼續(xù)調(diào)用沒有響應(yīng)的服務(wù),如果一個服務(wù)的出錯率超過了指定閥值救崔,它會觸發(fā)斷路器惶看,使得所有的請求快速失敗一段時間捏顺,Hystrix也允許你定義請求失敗時的fallback動作 ,比如讀取緩存或者返回一個默認(rèn)值纬黎。如果你使用JVM幅骄,那么希望你一定考慮使用Hystrix,如果你不使用JVM本今,那也要有類似的工具來幫助你拆座。

總結(jié)

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市杭抠,隨后出現(xiàn)的幾起案子脸甘,更是在濱河造成了極大的恐慌,老刑警劉巖偏灿,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丹诀,死亡現(xiàn)場離奇詭異,居然都是意外死亡翁垂,警方通過查閱死者的電腦和手機(jī)铆遭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沿猜,“玉大人枚荣,你說我怎么就攤上這事√浼纾” “怎么了橄妆?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祈坠。 經(jīng)常有香客問我害碾,道長,這世上最難降的妖魔是什么赦拘? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任慌随,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阁猜。我一直安慰自己丸逸,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布蹦漠。 她就那樣靜靜地躺著椭员,像睡著了一般车海。 火紅的嫁衣襯著肌膚如雪笛园。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天侍芝,我揣著相機(jī)與錄音研铆,去河邊找鬼。 笑死州叠,一個胖子當(dāng)著我的面吹牛棵红,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咧栗,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼逆甜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了致板?” 一聲冷哼從身側(cè)響起交煞,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎斟或,沒想到半個月后素征,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡萝挤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年御毅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怜珍。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡端蛆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酥泛,到底是詐尸還是另有隱情今豆,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布揭璃,位于F島的核電站晚凿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瘦馍。R本人自食惡果不足惜歼秽,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望情组。 院中可真熱鬧燥筷,春花似錦箩祥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谢揪,卻和暖如春蕉陋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拨扶。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工凳鬓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人患民。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓缩举,卻偏偏與公主長得像,于是被迫代替她去往敵國和親匹颤。 傳聞我的和親對象是個殘疾皇子仅孩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容