牌桶限流相比於漏桶限流

有贊API網關實踐 https://tech.youzan.com/api-gateway-in-practice/ajax

 

有贊API網關實踐

1、API網關簡介

隨着移動互聯網的興起、開放合做思惟的盛行,不一樣終端和第三方開發者都須要大量的接入企業核心業務能力,此時各業務系統將會面臨同一系列的問題,例如:如何讓調用方快速接入、如何讓業務方安全地對外開放能力,如何應對和控制業務洪峯調用等等。因而就誕生了一個隔離企業內部業務系統和外部系統調用的屏障 - API網關,它負責在上層抽象出各業務系統須要的通用功能,例如:鑑權、限流、ACL、降級等。另外隨着近年來微服務的流行,API網關已經成爲一個微服務架構中的標配組件。數據庫

2、有贊API網關簡介

有贊API網關目前承載着微商城、零售、微小店、餐飲、美業、AppSDK、部分PC、三方開發者等多個業務的調用,天天有着億級別的流量。json

有贊後端服務最開始是由PHP搭建,隨着整個技術體系的升級,後面逐步從PHP遷移到Java體系。在API網關設計之初主要支持Dubbo、Http兩種協議。遷移過程當中,咱們發現部分服務須要經過RPC方式調用PHP服務,因而咱們(公司)基於Dubbo開發了一個新的框架Nova,兼容Dubbo調用,同時支持調用PHP服務。因而網關也支持了新的Nova協議,這樣就有Dubbo、Http、Nova三種協議。後端

隨着業務的不斷髮展,業務服務化速度加快,網關面臨各種新的需求。例如回調類型的API接入,這種API不須要鑑權,只須要一個限流服務,路由到後端服務便可;另外還有參數、返回值的轉換需求也不斷到來,這期間咱們快速迭代知足新的需求。而在這個過程當中咱們也走了不少彎路,例如API的規範,在最開始規範意識比較籠統,致使返回值在對外暴露時出現了不統一的狀況,後續作SDK自動化的時候比較棘手,通過不斷的約束開發者,最終作到了統一。api

3、架構與設計

1. 網關架構

部署架構圖

網關的調用方主要包括微商城、微小店、零售等App應用,以及三方開發者和部分PC業務。經過LVS作負載均衡,後端Tengine實現反向代理,網關應用調用到實際的業務集羣緩存

應用架構圖

網關核心由Pipe鏈構成,每一個Pipe負責一塊功能,同時使用緩存、異步等特性提高併發及性能安全

線程模型圖

網關採用Jetty部署,調用採用Http協議,請求由容器線程池處理(容器開啓了Servlet3.0異步,提高了較大的吞吐量),以後分發到應用線程池異步處理。應用線程池在設計之初考慮不一樣的任務執行可能會出現耗時不一的狀況,因此將任務分別拆分到不一樣的線程池,以提升不一樣類型任務的併發度,如圖分爲CommonGroup, ExecutionGroup, ResultGroup微信

CommonGroup執行通用任務,ExecutionGroup執行多協議路由及調用任務,ResultGroup執行結果處理任務(包含異常)架構

網關業務生態圖

網關生態主要包含控制檯、網關核心、網關統計與監控
控制檯主要對API生命週期進行管理,以及ACL、流量管控等功能;
網關核心主要處理API調用,包含鑑權、限流、路由、協議轉換等功能;
統計與監控模塊主要完成API調用的統計以及對店鋪、三方的一些報表統計,同時提供監控功能和報警功能併發

2. 網關核心設計

2.1 異步

咱們使用Jetty容器來部署應用,並開啓Servlet3.0的異步特性,因爲網關業務自己就是調用大量業務接口,所以IO操做會比較頻繁,使用該特性能較大提高網關總體併發能力及吞吐量。另外咱們在內部處理開啓多組線程池進行異步處理,以異步回調的方式通知任務完成,進一步提高併發量

image

2.2 二級緩存

爲了進一步提高網關的性能,咱們增長了一層分佈式緩存(借用Codis實現),將一些不常常變動的API元數據緩存下來,這樣不只減小了應用和DB的交互次數,還加快了讀取效率。咱們同時考慮到Codis在極端狀況下存在不穩定因素,所以咱們在本地再次作了本地緩存,這樣的讀取能夠從ms級別下降到ns級別。爲了實現多臺機器的本地緩存一致性,咱們使用了ZK監聽節點變化來更新各機器本地緩存

image

2.3 鏈式處理

在設計網關的時候,咱們採用責任鏈模式來實現網關的核心處理流程,將每一個處理邏輯當作一個Pipe,每一個Pipe按照預先設定的順序前後執行,與開源的Zuul 1.x相似,咱們也採用了PRPE模式(Pre、Routing、Post、Error),在咱們這裏Pre分爲PrePipe、RateLimitPipe、AuthPipe、AclPipe、FlowSepPipe,這些Pipe對數據進行預處理、限流、鑑權、訪問控制、分流,並將過濾後的Context向下傳遞;Routing分爲DubboPipe、HttpPipe,這些Pipe分別處理Dubbo協議、Http協議路由及調用;Post爲ResultPipe,處理正常返回值以及統計打點,Error爲ErrorPipe,處理異常場景

image

2.4 線程池隔離

Jetty容器線程池(QTP)負責接收Http請求,以後交由應用線程池CommonGroup,ExecutionGroup, ResultGroup,通用的操做將會被放到CommonGroup線程池執行,執行真實調用的被放到ExecutionGroup,結果處理放到ResultGroup。這樣部分Pipe之間線程隔離,一般前置Pipe處理都比較快,因此共享線程池便可,真實調用一般比較耗時,所以咱們放到獨立的線程池,同時結果處理也存在一些運算,所以也放到獨立線程池

image

2.5 平滑限流

最先咱們採用了簡單的分佈式緩存(Codis)計數實現限流,以IP、API維度構建Key進行累加,這種限流方式實現簡單,可是不能作到連續時間段內平滑限流。例如針對某個API每分鐘限流100次,第1秒發起20次,第二秒發起30次,第3秒發起40次,這樣的限流波動比較大,所以咱們決定將其改進。通過調研咱們最終選擇了令牌桶限流,令牌桶限流相比於漏桶限流能適應閒置較長時段後的尖峯調用,同時消除了簡單計數器限流帶來的短期內流量不均的問題。目前網關支持IP、店鋪、API、應用ID和三方ID等多個維度的限流,也支持各維度的自由組合限流,能夠很容易擴展出新的維度

image

2.6 熔斷降級

因爲咱們常常遇到調用後端接口超時,或者異常的狀況,後端服務沒法當即恢復,這種狀況下再將請求發到後端已沒有意義。因而咱們使用Hystrix進行熔斷降級處理。Hystrix支持線程池和信號量2種模式的隔離方案,網關的業務場景是多API和API分組,每一個API均可能路由到不一樣後端服務,若是咱們對API或者API分組作線程池隔離,就會產生大量的線程,因此咱們選擇了信號量作隔離。咱們爲每一個API提供一個降級配置,用戶能夠選擇本身配置的API在達到多少錯誤率時進行熔斷降級。
引入Hystrix後,Hystrix會對每一個API作統計,包括總量、正確率、QPS等指標,同時會產生大量事件,當API不少的時候,這些指標和事件會佔用大量內存,致使更加頻繁的YoungGC,這對應用性能產生了必定的影響,不過總體的收益仍是不錯的

另外有贊內部也開發了一個基於Hystrix的服務熔斷平臺(Tesla),平臺在可視化、易用性、擴展性上面均有較大程度的提高;後續網關會考慮熔斷模塊的實現基於服務熔斷平臺,以提供更好的服務

image

2.7 分流

有贊內部存在多種協議類型的後端服務,最原始的服務是PHP開發,後面逐漸遷移到Java,很早一部分API是由PHP暴露的,後續爲了能作灰度遷移到Java,咱們作了分流,將老的PHP接口的流量按照必定的比例分發到新的Java接口上

3. 控制檯

除了核心功能的調用外,網關還須要支持內部用戶(下稱業務方)快速配置接口暴露給開發者。 控制檯主要職責包括:快速配置API、一站式測試API、一鍵發佈API,自動化文檔生成,自動化SDK生成

  • 快速配置API:這塊咱們主要是按照對外、對內來進行配置,業務方將本身要對外公開的名稱、參數編輯好,再經過對內映射將對外參數映射到內部服務的接口裏面

image

  • 一站式測試API:API配置完成後,爲了能讓業務方快速測試,咱們作了一站式獲取鑑權值,參數值自動保存,作到一站式測試

image

  • 一鍵發佈API:在完成配置和測試後,API就能夠直接發佈,這個時候選擇對應環境的註冊中心或者服務域名便可

image

  • 自動化文檔生成:咱們針對文檔這塊作了文檔中心,對內部用戶,他們只須要到平臺來搜索便可,對外部用戶,能夠在有贊雲官網查看或者在控制檯直接導出pdf文件給用戶

image

  • 自動化SDK生成:對於開發者來講,接入一個平臺必然少不了SDK,咱們針對多語言作了自動化SDK生成,當用戶的接口發佈成功後,咱們會監聽到有新的接口,這時會觸發自動編譯(Java)SDK的模塊,將新接口打包成新版本的壓縮包,供開發者使用;若是編譯失敗(Java)則不會替換老的壓縮包,咱們會發送報警給相應的開發者,讓其調整不規範的地方

image

4. 數據統計

爲了讓業務方能在上線後瞭解本身的接口的運行情況,咱們作了API相關的統計。咱們經過在覈心模塊裏面打日誌,利用rsyslog採集數據到Kafka,而後從Kafka消費進行統計,以後迴流到數據庫供在線查詢

除此以外,咱們爲每一個商家作了他們受權的服務商調用接口的統計。這塊功能的實現,咱們經過Storm從Kafka實時消費,並實時統計落HBase,天天凌晨將前一天的數據同步到Hive進行統計並回流到數據庫

image

5. 報警監控

業務方API上線後,除了查看統計外,當API出問題時,還須要及時發現。咱們針對這塊作了API報警功能。用戶在平臺配置本身的API的報警,這裏咱們主要支持基於錯誤數或RT維度的報警。
咱們實時地從Kafka消費API調用日誌,若是發現某個API的RT或者錯誤次數超過配置的報警閾值,則會當即觸發報警

image

4、實踐總結

1. 規範

在網關上暴露的API不少,如何讓這些API按照統一的標準對外暴露,讓開發者可以低門檻快速接入是網關須要思考的問題

網關規範主要是對API的命名、入參(公用入參、業務入參)、內部服務返回值、錯誤碼(公用錯誤碼、業務錯誤碼)、出參(公用出參、業務出參),進行規範

在咱們的實踐過程當中,總結了如下規範:

  • 命名規範:youzan.[業務線(可選)].[應用名].[動做].[版本],例如:youzan.item.create.3.0.0
  • 入參規範:要求所有小寫,組合單詞如下劃線分隔,例如:title, item_id;入參若是是一個結構體,要求以json字符串傳入,而且json中的key必須小寫而且如下劃線分隔
  • 出參規範:要求所有小寫,組合單詞如下劃線分隔,例如:page_num, total_count;若是參數爲結構體,結構體裏面的key必須小寫且如下劃線分隔
  • 錯誤碼規範:咱們作了統一的錯誤碼,例如系統級錯誤碼51xxx,業務錯誤碼50000,詳情信息由msg顯示;業務級錯誤碼由業務方自行定義,同時約束每一個業務方的錯誤碼範圍
  • 服務返回值規範:針對不一樣的業務方,每一個API可能會有不一樣的業務錯誤,咱們須要將這部分業務級錯誤展現給開發者,所以咱們約定返回值須要按照一個POJO類型(包含code, msg, data)來返回,對於code爲200,咱們認爲正常返回,不然認爲是業務錯誤,將返回值包裝爲錯誤結果

2. 發佈

  • 咱們將API劃分到3個環境,分別爲測試環境、預發環境、生產環境。API的建立、編輯必須在測試環境進行,測試完成後,能夠將API發佈到預發環境,以後再從預發環境發佈到生產環境,這樣能夠保持三個環境的API數據一致。好處是:一方面可讓測試開發能在測試環境進行自動化驗證,另外一方面能夠防止用戶直接編輯線上接口引起故障

3. 工具化

  • 對於內部用戶常常可能須要排查問題,例如OAuth Token裏面帶的參數,須要常常查詢,咱們提供工具化的控制檯,能讓用戶方便查詢,從而減小答疑量
  • 咱們上線後也曾經出現過緩存不一致的狀況,爲了能快速排查問題,咱們作了緩存管理工具,能在圖形化界面上查看本地緩存以及Codis的緩存,能夠進行對比找出差別
  • 爲了更好的排查線上問題,咱們接入了有贊對比引擎(Replay)平臺,該平臺能將線上的流量引到預發,幫助開發者更快定位問題

5、踩過的坑

  • Meta區Full GC致使服務沒法響應

    現象:應用hung死,調用接口返回503,沒法服務

    排查過程:現場dump了內存,GC記錄,以及線程運行快照。首先看了GC發現是Full GC,可是不清楚是哪裏發生的,看線程運行快照也沒發現什麼問題。因而在本地用HeapAnalysis分析,堆區沒看出什麼問題,大對象都是應該佔用的;因而查看方法區,經過ClassLoader Analysis發現Fastjson相關的類較多,所以懷疑是class泄露,進一步經過MAT的OQL語法分析,發現是Fastjson在序列化Jetty容器的HttpServletRequest時,爲了加快速度因而建立新的類時拋了異常,致使動態建立的類在方法區堆積從而引起Full GC,後續咱們也向Fastjson提了相關bug

    解決方案:將序列化HttpServletRequest的代碼移除

  • 僞死循環致使CPU 100%

    現象:在有贊雙11全鏈路壓測期間,某個業務調用API,致使咱們的應用CPU幾乎接近100%

    排查過程:通過日誌分析,發現該接口存在大量超時,可是從代碼沒看出特別有問題的地方。因而咱們將接口在QA環境模擬調用,用VisualVM連上去,經過抽樣器抽樣CPU,發現某個方法消耗CPU較高,所以咱們迅速定位到源碼,發現這段代碼主要是執行輪詢任務是否完成,若是完成則調用完成回調,若是未完成繼續放到隊列。再結合以前的環境觀察發現大量超時的任務被放到隊列,致使任務被取出後,任務仍然是未完成狀態,這樣會將任務放回隊列,這樣其實構成了一個死循環

    解決方案:將主動輪詢改成異步通知,咱們這裏是Dubbo調用,Dubbo調用返回的Future實際是一個FutureAdapter,能夠獲取到裏面的ResponseFuture(DefaultFuture),這個類型的Future支持設置Callback,任務完成時會通知到設置的回調

6、將來展望

  1. 業務級資源組隔離。隨着業務的不斷髮展,當業務線較多時,能夠將重要的業務分配到更優質的資源組(例如:機器性能、線程池的大小),將通常業務放到普通資源組,這樣能夠更好的服務不一樣的業務場景
  2. 更高併發的線程池/IO的優化。隨着業務的發展,將來可能會出現更高的併發,須要更精良的線程及IO模型
  3. 更多的協議支持。之後技術的發展,Http2可能會蓬勃發展,這時須要接入Http2的協議

7、結語

有贊網關目前歸屬有贊共享技術-基礎服務中心團隊開發和維護;
該團隊目前主要分爲商品中心、庫存中心、物流中心、消息溝通平臺、雲生態5個小組;
商品/庫存/物流中心:經過不斷抽象上層業務,完成通用的模型建設;爲上層業務方提供高可用的服務,並快速響應多變的業務需求;針對秒殺、洪峯調用、及上層業務多變等需求,三個小組還齊力開發和持續完善着 對比引擎、服務熔斷、熱點探測等三個通用系統;
消息溝通平臺:提供幾乎一切消息溝通相關的能力及一套幫助商家與用戶聯繫的多客服系統,天天承載着上億次調用(短信、apppush、語音、微信、微博、多客服、郵件等通道);
雲生態:承擔着核心網關的建設和發展(上面的網關應用系統)、三方推送系統、有贊雲後臺、商業化訂購以及App Engine的預研和開發;

目前該團隊HC開放,期待有機會與各位共事;(內推郵箱:huangtao@youzan.com)

注,本文做者:有贊網關(黃濤、尹鐵夫、叮咚)

相關文章
相關標籤/搜索