前言git
假設你正在開發一個電商網站,那麼這裏會涉及到不少後端的微服務,好比會員、商品、推薦服務等等。web
那麼這裏就會遇到一個問題,APP/Browser怎麼去訪問這些後端的服務? 若是業務比較簡單的話,能夠給每一個業務都分配一個獨立的域名(https://service.api.company.com),但這種方式會有幾個問題:redis
API Gatewayspring
更好的方式是採用API網關,實現一個API網關接管全部的入口流量,相似Nginx的做用,將全部用戶的請求轉發給後端的服務器,但網關作的不只僅只是簡單的轉發,也會針對流量作一些擴展,好比鑑權、限流、權限、熔斷、協議轉換、錯誤碼統1、緩存、日誌、監控、告警等,這樣將通用的邏輯抽出來,由網關統一去作,業務方也可以更專一於業務邏輯,提高迭代的效率。數據庫
經過引入API網關,客戶端只須要與API網關交互,而不用與各個業務方的接口分別通信,但多引入一個組件就多引入了一個潛在的故障點,所以要實現一個高性能、穩定的網關,也會涉及到不少點。apache
API註冊後端
業務方如何接入網關?通常來講有幾種方式。api
協議轉換緩存
內部的API多是由不少種不一樣的協議實現的,好比HTTP、Dubbo、GRPC等,但對於用戶來講其中不少都不是很友好,或者根本無法對外暴露,好比Dubbo服務,所以須要在網關層作一次協議轉換,將用戶的HTTP協議請求,在網關層轉換成底層對應的協議,好比HTTP -> Dubbo, 但這裏須要注意不少問題,好比參數類型,若是類型搞錯了,致使轉換出問題,而日誌又不夠詳細的話,問題會很難定位。服務器
服務發現
網關做爲流量的入口,負責請求的轉發,但首先須要知道轉發給誰,如何尋址,這裏有幾種方式:
服務調用
網關因爲對接不少種不一樣的協議,所以可能須要實現不少種調用方式,好比HTTP、Dubbo等,基於性能緣由,最好都採用異步的方式,而Http、Dubbo都是支持異步的,好比apache就提供了基於NIO實現的異步HTTP客戶端。
由於網關會涉及到不少異步調用,好比攔截器、HTTP客戶端、dubbo、redis等,所以須要考慮下異步調用的方式,若是基於回調或者future的話,代碼嵌套會很深,可讀性不好,能夠參考zuul和spring cloud gateway的方案,基於響應式進行改造。
優雅下線
優雅下線也是網關須要關注的一個問題,網關底層會涉及到不少種協議,好比HTTP、Dubbo,而HTTP又能夠繼續細分,好比域名、註冊中心等,有些自身就支持優雅下線,好比Nginx自身是支持健康監測機制的,若是檢測到某一個節點已經掛掉了,就會把這個節點摘掉,對於應用正常下線,須要結合發佈系統,首先進行邏輯下線,而後對後續Nginx的健康監測請求直接返回失敗(好比直接返回500),而後等待一段時間(根據Nginx配置決定),而後再將應用實際下線掉。另外對於註冊中心的其實也相似,通常註冊中心是隻支持手動下線的,能夠在邏輯下線階段調用註冊中心的接口將節點下線掉,而有些不支持主動下線的,須要結合緩存的配置,讓應用延遲下線。另外對於其餘好比Dubbo等原理也是相似。
性能
網關做爲全部流量的入口,性能是重中之重,早期大部分網關都是基於同步阻塞模型構建的,好比Zuul 1.x。但這種同步的模型咱們都知道,每一個請求/鏈接都會佔用一個線程,而線程在JVM中是一個很重的資源,好比Tomcat默認就是200個線程,若是網關隔離沒有作好的話,當發生網絡延遲、FullGC、第三方服務慢等狀況形成上游服務延遲時,線程池很容易會被打滿,形成新的請求被拒絕,但這個時候其實線程都阻塞在IO上,系統的資源被沒有獲得充分的利用。另一點,容易受網絡、磁盤IO等延遲影響。須要謹慎設置超時時間,若是設置不當,且服務隔離作的不是很完善的話,網關很容易被一個慢接口拖垮。
而異步化的方式則徹底不一樣,一般狀況下一個CPU核啓動一個線程便可處理全部的請求、響應。一個請求的生命週期再也不固定於一個線程,而是會分紅不一樣的階段交由不一樣的線程池處理,系統的資源可以獲得更充分的利用。並且由於線程再也不被某一個鏈接獨佔,一個鏈接所佔用的系統資源也會低得多,只是一個文件描述符加上幾個監聽器等,而在阻塞模型中,每條鏈接都會獨佔一個線程,而線程是一個很是重的資源。對於上游服務的延遲狀況,也可以獲得很大的緩解,由於在阻塞模型中,慢請求會獨佔一個線程資源,而異步化以後,由於單條鏈接所佔用的資源變的很是低,系統能夠同時處理大量的請求。
若是是JVM平臺,Zuul 二、Spring Cloud gateway等都是不錯的異步網關選型,另外也能夠基於Netty、Spring Boot2.x的webflux、vert.x或者servlet3.1的異步支持進行自研。
緩存
對於一些冪等的get請求,能夠在網關層面根據業務方指定的緩存頭作一層緩存,存儲到Redis等二級緩存中,這樣一些重複的請求,能夠在網關層直接處理,而不用打到業務線,下降業務方的壓力,另外若是業務方節點掛掉,網關也可以返回自身的緩存。
限流
限流對於每一個業務組件來講,能夠說都是一個必須的組件,若是限流作很差的話,當請求量突增時,很容易致使業務方的服務掛掉,好比雙十一、雙12等大促時,接口的請求量是平時的數倍,若是沒有評估好容量,又沒有作限流的話,很容易服務整個不可用,所以須要根據業務方接口的處理能力,作好限流策略,相信你們都見過淘寶、百度搶紅包時的降級頁面。
所以必定要在接入層作好限流策略,對於非核心接口能夠直接將降級掉,保障核心服務的可用性,對於核心接口,須要根據壓測時獲得的接口容量,制定對應的限流策略。限流又分爲幾種:
另外還有不一樣的策略:簡單計數、令牌桶等,大部分場景下其實簡單計數已經夠用了,但若是須要支持突發流量等場景時,能夠採用令牌桶等方案。還須要考慮根據什麼限流,好比是IP、接口、用戶維度、仍是請求參數中的某些值,這裏能夠採用表達式,相對比較靈活。
穩定性
穩定性是網關很是重要的一環,監控、告警須要作的很完善才能夠,好比接口調用量、響應時間、異常、錯誤碼、成功率等相關的監控告警,還有線程池相關的一些,好比活躍線程數、隊列積壓等,還有些系統層面的,好比CPU、內存、FullGC這些基本的。
網關是全部服務的入口,對於網關的穩定性的要求相對於其餘服務會更高,最好可以一直穩定的運行,儘可能少重啓,但當新增功能、或者加日誌排查問題時,不可避免的須要從新發布,所以能夠參考zuul的方式,將全部的核心功能都基於不一樣的攔截器實現,攔截器的代碼採用Groovy編寫,存儲到數據庫中,支持動態加載、編譯、運行,這樣在出了問題的時候可以第一時間定位並解決,而且若是網關須要開發新功能,只須要增長新的攔截器,並動態添加到網關便可,不須要從新發布。
熔斷降級
熔斷機制也是很是重要的一項。若某一個服務掛掉、接口響應嚴重超時等發生,則可能整個網關都被一個接口拖垮,所以須要增長熔斷降級,當發生特定異常的時候,對接口降級由網關直接返回,能夠基於Hystrix或者Resilience4j實現。
日誌
因爲全部的請求都是由網關處理的,所以日誌也須要相對比較完善,好比接口的耗時、請求方式、請求IP、請求參數、響應參數(注意脫敏)等,另外因爲可能涉及到不少微服務,所以須要提供一個統一的traceId方便關聯全部的日誌,能夠將這個traceId置於響應頭中,方便排查問題。
隔離
好比線程池、http鏈接池、redis等應用層面的隔離,另外也能夠根據業務場景,將核心業務部署帶單獨的網關集羣,與其餘非核心業務隔離開。
網關管控平臺
這塊也是很是重要的一環,須要考慮好整個流程的用戶體驗,好比接入到網關的這個流程,能不能儘可能簡化、智能,好比若是是dubbo接口,咱們能夠經過到git倉庫中獲取源碼、解析對應的類、方法,從而實現自動填充,儘可能幫用戶減小操做;另外接口通常是從測試->預發->線上,若是每次都要填寫一遍表單會很是麻煩,咱們能不能自動把這個事情作掉,另外若是網關部署到了多個可用區、甚至不一樣的國家,那這個時候,咱們還須要接口數據同步功能,否則用戶須要到每一個後臺都操做一遍,很是麻煩。
這塊我的的建議是直接參考阿里雲、aws等提供的網關服務便可,功能很是全面。
其餘
其餘還有些須要考慮到的點,好比接口mock,文檔生成、sdk代碼生成、錯誤碼統1、服務治理相關的等,這裏就不累述了。
總結
目前的網關仍是中心化的架構,全部的請求都須要走一次網關,所以當大促或者流量突增時,網關可能會成爲性能的瓶頸,並且當網關接入的大量接口的時候,作好流量評估也不是一項容易的工做,每次大促前都須要跟業務方一塊兒針對接口作壓測,評估出大體的容量,並對網關進行擴容,並且網關是全部流量的入口,全部的請求都是由網關處理,要想準確的評估出容量很複雜。能夠參考目前比較流行的ServiceMesh,採用去中心化的方案,將網關的邏輯下沉到sidecar中,sidecar和應用部署到同一個節點,並接管應用流入、流出的流量,這樣大促時,只須要對相關的業務壓測,並針對性擴容便可,另外升級也會更平滑,中心化的網關,即便灰度發佈,可是理論上全部業務方的流量都會流入到新版本的網關,若是出了問題,會影響到全部的業務,但這種去中心化的方式,能夠先針對非核心業務升級,觀察一段時間沒問題後,再全量推上線。另外ServiceMesh的方案,對於多語言支持也更友好。