有赞API网关实践
https://tech.youzan.com/api-gateway-in-practice/
一、API網關簡介
隨著移動互聯網的興起、開放合作思維的盛行,不同終端和第三方開發者都需要大量的接入企業核心業務能力,此時各業務系統將會面臨同一系列的問題,例如:如何讓調用方快速接入、如何讓業務方安全地對外開放能力,如何應對和控制業務洪峰調用等等。于是就誕生了一個隔離企業內部業務系統和外部系統調用的屏障 - API網關,它負責在上層抽象出各業務系統需要的通用功能,例如:鑒權、限流、ACL、降級等。另外隨著近年來微服務的流行,API網關已經成為一個微服務架構中的標配組件。
二、有贊API網關簡介
有贊API網關目前承載著微商城、零售、微小店、餐飲、美業、AppSDK、部分PC、三方開發者等多個業務的調用,每天有著億級別的流量。
有贊后端服務最開始是由PHP搭建,隨著整個技術體系的升級,后面逐步從PHP遷移到Java體系。在API網關設計之初主要支持Dubbo、Http兩種協議。遷移過程中,我們發現部分服務需要通過RPC方式調用PHP服務,于是我們(公司)基于Dubbo開發了一個新的框架Nova,兼容Dubbo調用,同時支持調用PHP服務。于是網關也支持了新的Nova協議,這樣就有Dubbo、Http、Nova三種協議。
隨著業務的不斷發展,業務服務化速度加快,網關面臨各類新的需求。例如回調類型的API接入,這種API不需要鑒權,只需要一個限流服務,路由到后端服務即可;另外還有參數、返回值的轉換需求也不斷到來,這期間我們快速迭代滿足新的需求。而在這個過程中我們也走了很多彎路,例如API的規范,在最開始規范意識比較籠統,導致返回值在對外暴露時出現了不統一的情況,后續做SDK自動化的時候比較棘手,經過不斷的約束開發者,最終做到了統一。
三、架構與設計
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操作會比較頻繁,使用該特性能較大提升網關整體并發能力及吞吐量。另外我們在內部處理開啟多組線程池進行異步處理,以異步回調的方式通知任務完成,進一步提升并發量
2.2 二級緩存
為了進一步提升網關的性能,我們增加了一層分布式緩存(借用Codis實現),將一些不經常變更的API元數據緩存下來,這樣不僅減少了應用和DB的交互次數,還加快了讀取效率。我們同時考慮到Codis在極端情況下存在不穩定因素,因此我們在本地再次做了本地緩存,這樣的讀取可以從ms級別降低到ns級別。為了實現多臺機器的本地緩存一致性,我們使用了ZK監聽節點變化來更新各機器本地緩存
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,處理異常場景
2.4 線程池隔離
Jetty容器線程池(QTP)負責接收Http請求,之后交由應用線程池CommonGroup,ExecutionGroup, ResultGroup,通用的操作將會被放到CommonGroup線程池執行,執行真實調用的被放到ExecutionGroup,結果處理放到ResultGroup。這樣部分Pipe之間線程隔離,通常前置Pipe處理都比較快,所以共享線程池即可,真實調用通常比較耗時,因此我們放到獨立的線程池,同時結果處理也存在一些運算,因此也放到獨立線程池
2.5 平滑限流
最早我們采用了簡單的分布式緩存(Codis)計數實現限流,以IP、API維度構建Key進行累加,這種限流方式實現簡單,但是不能做到連續時間段內平滑限流。例如針對某個API每分鐘限流100次,第1秒發起20次,第二秒發起30次,第3秒發起40次,這樣的限流波動比較大,因此我們決定將其改進。經過調研我們最終選擇了令牌桶限流,令牌桶限流相比于漏桶限流能適應閑置較長時段后的尖峰調用,同時消除了簡單計數器限流帶來的短時間內流量不均的問題。目前網關支持IP、店鋪、API、應用ID和三方ID等多個維度的限流,也支持各維度的自由組合限流,可以很容易擴展出新的維度
2.6 熔斷降級
由于我們經常遇到調用后端接口超時,或者異常的情況,后端服務無法立即恢復,這種情況下再將請求發到后端已沒有意義。于是我們使用Hystrix進行熔斷降級處理。Hystrix支持線程池和信號量2種模式的隔離方案,網關的業務場景是多API和API分組,每個API都可能路由到不同后端服務,如果我們對API或者API分組做線程池隔離,就會產生大量的線程,所以我們選擇了信號量做隔離。我們為每個API提供一個降級配置,用戶可以選擇自己配置的API在達到多少錯誤率時進行熔斷降級。
引入Hystrix后,Hystrix會對每個API做統計,包括總量、正確率、QPS等指標,同時會產生大量事件,當API很多的時候,這些指標和事件會占用大量內存,導致更加頻繁的YoungGC,這對應用性能產生了一定的影響,不過整體的收益還是不錯的
另外有贊內部也開發了一個基于Hystrix的服務熔斷平臺(Tesla),平臺在可視化、易用性、擴展性上面均有較大程度的提升;后續網關會考慮熔斷模塊的實現基于服務熔斷平臺,以提供更好的服務
2.7 分流
有贊內部存在多種協議類型的后端服務,最原始的服務是PHP開發,后面逐漸遷移到Java,很早一部分API是由PHP暴露的,后續為了能做灰度遷移到Java,我們做了分流,將老的PHP接口的流量按照一定的比例分發到新的Java接口上
3. 控制臺
除了核心功能的調用外,網關還需要支持內部用戶(下稱業務方)快速配置接口暴露給開發者。 控制臺主要職責包括:快速配置API、一站式測試API、一鍵發布API,自動化文檔生成,自動化SDK生成
- 快速配置API:這塊我們主要是按照對外、對內來進行配置,業務方將自己要對外公開的名稱、參數編輯好,再通過對內映射將對外參數映射到內部服務的接口里面
- 一站式測試API:API配置完成后,為了能讓業務方快速測試,我們做了一站式獲取鑒權值,參數值自動保存,做到一站式測試
- 一鍵發布API:在完成配置和測試后,API就可以直接發布,這個時候選擇對應環境的注冊中心或者服務域名即可
- 自動化文檔生成:我們針對文檔這塊做了文檔中心,對內部用戶,他們只需要到平臺來搜索即可,對外部用戶,可以在有贊云官網查看或者在控制臺直接導出pdf文件給用戶
- 自動化SDK生成:對于開發者來說,接入一個平臺必然少不了SDK,我們針對多語言做了自動化SDK生成,當用戶的接口發布成功后,我們會監聽到有新的接口,這時會觸發自動編譯(Java)SDK的模塊,將新接口打包成新版本的壓縮包,供開發者使用;如果編譯失敗(Java)則不會替換老的壓縮包,我們會發送報警給相應的開發者,讓其調整不規范的地方
4. 數據統計
為了讓業務方能在上線后了解自己的接口的運行狀況,我們做了API相關的統計。我們通過在核心模塊里面打日志,利用rsyslog采集數據到Kafka,然后從Kafka消費進行統計,之后回流到數據庫供在線查詢
除此之外,我們為每個商家做了他們授權的服務商調用接口的統計。這塊功能的實現,我們通過Storm從Kafka實時消費,并實時統計落HBase,每天凌晨將前一天的數據同步到Hive進行統計并回流到數據庫
5. 報警監控
業務方API上線后,除了查看統計外,當API出問題時,還需要及時發現。我們針對這塊做了API報警功能。用戶在平臺配置自己的API的報警,這里我們主要支持基于錯誤數或RT維度的報警。
我們實時地從Kafka消費API調用日志,如果發現某個API的RT或者錯誤次數超過配置的報警閾值,則會立即觸發報警
四、實踐總結
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)平臺,該平臺能將線上的流量引到預發,幫助開發者更快定位問題
五、踩過的坑
-
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,任務完成時會通知到設置的回調
六、未來展望
七、結語
有贊網關目前歸屬有贊共享技術-基礎服務中心團隊開發和維護;
該團隊目前主要分為商品中心、庫存中心、物流中心、消息溝通平臺、云生態5個小組;
商品/庫存/物流中心:通過不斷抽象上層業務,完成通用的模型建設;為上層業務方提供高可用的服務,并快速響應多變的業務需求;針對秒殺、洪峰調用、及上層業務多變等需求,三個小組還齊力開發和持續完善著 對比引擎、服務熔斷、熱點探測等三個通用系統;
消息溝通平臺:提供幾乎一切消息溝通相關的能力及一套幫助商家與用戶聯系的多客服系統,每天承載著上億次調用(短信、apppush、語音、微信、微博、多客服、郵件等通道);
云生態:承擔著核心網關的建設和發展(上面的網關應用系統)、三方推送系統、有贊云后臺、商業化訂購以及App Engine的預研和開發
轉載于:https://www.cnblogs.com/davidwang456/articles/9238890.html
總結
- 上一篇: Lambda架构在有赞广告平台的应用与演
- 下一篇: 在Mybatis-spring上基于注解