search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

京東京麥開放平台的高可用架構之路

作者|張松然 編輯|雨多田光

京麥是京東商家的多端開放式工作平台,是京東十萬商家唯一的店鋪運營管理平台,為京東商家提供在移動和桌面端的操作業務,京麥本身是一個開放的端體系架構,由京東官方和 ISV 為商家提供多樣的應用服務。

京麥開發平台是京東系統與外部系統通訊的重要平台,技術架構從早期的單一 Nginx+Tomcat 部署,到現在的單一職責,獨立部署,去中心化,以及自主研發了 JSF/HTTP 等多種協議下的 API 網關、TCP 消息推送、APNs 推送、降級、限流等技術。

京麥開放平台每天承載海量的 API 調用、消息推送,經歷了 4 年京東 618 的流量洗禮。本文將為您揭開京麥開放平台高性能 API 網關、高可靠的消息服務的技術內幕。

高性能 API 網關

京東內部的數據分佈在各個獨立的業務系統中,包括訂單中心、商品中心、商家中心等,各個獨立系統間通過 JSF(Jingdong Service Framework)進行數據交換。而 API 網關基於 OAuth2 協議提供,ISV 調用是通過 HTTP 的 JSON 協議。

如何將這些內部數據安全可控地開放給外部 ISV 進行服務調用,以及如何快速地進行 API 接入實現數據報文轉化,在這個背景下 API 網關誕生。

API 網關在架構設計上採用了多層介面,到達網關的請求首先由網關接入層攔截處理,在接入層進行兩個主要環節的處理:

  • 網關防禦校驗:這裡包含降級和限流,以及多級緩存等,進行數據正確性校驗;

  • 網關接入分發:網關分發會根據網關註冊中心的數據進行協議解析,之後動態構建調用實例,完成服務泛化調用。

API 網關是為了滿足 618 高併發請求下的應用場景,網關在服務調度、身份授權、報文轉換、負載與緩存、監控與日誌等關鍵點上進行了針對性的架構優化。

API 元數據統一配置

API 的調用依賴對元數據獲取,比如 API 的欄位信息、流控信息、APP 密鑰、IP 白名單等、許可權配置等。在 618 場景下,元數據獲取性能是 API 網關的關鍵點。基於 DB 元數據讀取是不可取的,即使對 DB 做分庫分表處理也不行,因為 DB 就不是用來抗量的。

其次,要考慮到元數據的更新問題,定時的輪訓更新會產生極大延遲性,而且空輪訓也是對系統資源的極大浪費,採用 MQ 廣播通知不失為一種解決辦法,但 MQ 僅僅解決數據同步的問題,數據緩存在集群里服務如何保證數據一致性和數據容災,又極大的增加了系統複雜度。

所以綜合考慮伺服器性能和網路 IO 等因素,在 API 元數據讀取採用基於 ZooKeeper 的統一配置,並自研實現多級緩存容災架構方案,從 ZooKeeper、內存和本地文件等進行多級緩存,同時支持數據變更時即時同步,以及系統宕機網路異常等情況下的數據自動容災等策略。

以讀為例,網關首先從內存中讀取配置,如無數據,從 ZooKeeper 讀取,讀取后同步到內存,並非同步保存本次快照。如果 ZooKeeper 數據變更,通過監聽 ZooKeeper 的 DataChangeWatcher 變更同步數據。如果 ZooKeeper 宕機,重啟伺服器,系統還可以通過本地快照恢復最近一次的元數據配置。

TCP 全雙工的長鏈接會話通道

API HTTP 網關通過介面提供服務調用獲取請求數據的,而搭建客戶端與服務平台的 TCP 網關的雙向通道,以保持客戶端與服務平台的會話狀態,則可以在 HTTP 網關基礎上提供更多、更靈活的技術實現和業務實現。

在業務服務調用上通過 HTTP 網關,在平台服務調用上則通過 TCP 網關,實現平台與業務解耦,並且平台採用 TCP 通道還可以增加對平台的控制力,在此背景下誕生了 TCP 網關。

TCP 網關採用長連接通道,實現全雙工會話。TCP 網關採用 Netty 作為 TCP 容器,在 ChannelPipe中載入自定義 ChannelHandler,構建 Container 容器,將每個 TCP Connection 封裝到一個 Session 會話中,保存在 Container 容器中,由 Container 容器構建 Session 會話層提供邏輯層請求調用。

自研構建 Session 會話層是因為 HTTP 屬於 OSI 的應用層,而 TCP 屬於 OSI 的傳輸層,面向連接的編程極大的增加程序複雜度,所以將 Connection 封裝在每一個 Session 會話里,再以微服務的方式提供服務調用,極大的精簡了 TCP 編程。

斷線重連

客戶端與服務端通過 TCP 長連接進行通信,但在複雜的網路環境下,移動客戶端可能由於網路抖動、弱網路情況下,遭遇非正常網路閃斷,如何處理斷開后的斷線重連,保證客戶端與服務端的通訊穩定呢?

客戶端每通過 TCP 與服務端進行一次建連,都會在服務容器里創建一個 Session 會話,該會話保存 Connection 的句柄,對應 Netty 的一個 Channel 通道。建連成功后,通過定時的心跳保持 Channel 屬於 Active 活躍。但客戶端進入弱網路環境下,客戶端可能已經掉線,但並未向服務端主動發送關閉 Channel 請求,而服務端仍認為該 Channel 仍存活。直到在由服務端的會話存活檢測機制檢測到 Channel 已經 InActive,才會由服務端銷毀該 Channel。

服務端的會話存活檢測是 5 分鐘一次,所以存在客戶端掉線后,在 5 分鐘內又重新建連,而這時服務端的建連邏輯,不是重新創建一個 Session,而是去尋找上一次的 Session,並更新標識存活。具體的實現是在每次建連的 Channel 里存入 SessionId,當網路閃斷後,判斷 Channel 是否存在 Session,之所以實現是得益於 Netty 的 ChannelHandlerContext,可以存儲一個自定義屬性到 Channel 的上下文中。

當然,TCP 網關一定是集群,所以,斷線重連也是極有可能請求到不同的伺服器上,而這種情況按照新 Connection 創建的 Session 處理,只有出現重連到同一伺服器時,才需要考慮上述的處理邏輯。

Protobuf 數據交換格式

HTTP 網關基於 JSON 進行數據傳輸,JSON 是 key-value 的鍵值對通信協議,所以生成報文會很大,所以影響傳輸性能。考慮到報文傳輸大小,在 TCP 網關中則通過 Protobuf 定義通信協議,提升數據傳輸效率。

Protobuf 支持 Java、Objective-C 和 C++ 等語言,現支持了京麥平台 PC 桌面客戶端、移動 iOS 和 Android 客戶端基於 Protobuf 通過 TCP 與服務端進行通信。

多維度流量控制

由於各個 API 的服務能力不一致,為了保證各個 API 能夠穩定提供服務,不會被暴漲的請求流量擊垮,那麼多維度流量控制是 API 網關的一個重要環節。

目前 API 網關是採用令牌桶的方法,實現方式是 Guava RateLimter,簡單有效,再結合統一配置中心,可以動態調整限流閾值。不用重啟伺服器即可實現快速限流策略調整。

在 API 網關裡面還有一個設置,就是併發度,這個是方法粒度的,對每一個調用介面都有一個併發度數值設置,而且是動態設置,也是通過 ZooKeeper 下發到每一個服務節點上。併發度的具體實現是通過 JDK 的 Semaphore。

高可靠的消息服務

API 網關提供 ISV 獲取數據,但實時數據的獲取,如果通過輪詢網關,大量空轉不僅非常的低效且浪費伺服器資源。基於此,開放平台推出了消息推送技術,提供一個實時的、可靠的、非同步的雙向數據交換通道,提升 API 網關性能。

AnyCall 和推送系統

AnyCall

負責接收各業務中心的訂單、商品、商家等消息,進行統一的消息過濾、轉換、存儲,及監控和統計等。各個過程中的消息狀態,通過消息採集器存儲到 ElasticSearch 和 HBase 進行存儲。

推送系統

基於 Netty 作為網路層框架,構建海量推送模型,使用靜默長連接通道,實現從消息接收、推送、確認,整個過程的完全非同步化處理。

解耦消息接入層和消息推送層,消息接入層只負責Request-Response和 Notice-Repley,而消息解析、適配、推送等邏輯處理都全部由消息推送層處理,而消息接入層和消息推送層之間則有消息隊列非同步進行通信。

半推半拉還是半推半查?

半推半拉

半推半拉模式中的「推」指的是由伺服器推送 消息通知 到客戶端,「拉」指的是客戶端收到通知后再從伺服器拉取 消息實體 到客戶端本地存儲。

其中消息通知發送的僅是一個命令關鍵字,這樣的設計是考慮消息推送可能存在丟失,通過拉取的方式,確保即使消息通知未送達,在下次消息通知觸發下的拉取也能把上一次消息拉取到本地。採用的半推半拉,每次僅推送通知,推送量小,實時性高

半推半查

後期京麥消息推送模式由「拉」改「查」,「查」指的是消息通知依舊推送,但客戶端收到消息通知后 不再拉取消息實體,僅更新消息未讀數和進行消息提醒等操作,而消息內容則是由服務端進行雲端存儲,採用輕客戶端,重服務端的架構方案,只有用戶點擊查詢消息時,才會按需進行數據查詢,在客戶端展示,但不存儲。

這種推送模式的改動主要考慮了客戶端拉取消息內容到本地存儲,佔用資源,重裝之後客戶端會丟失消息,以及多端存儲的數據存在不一致等問題。消息雲端存儲基於 ElasticSearch 進行消息存儲,並根據業務類型區分索引,通過 Routing 優化查詢性能,支持多維度進行查詢,性能穩定。

消息確認

評估消息系統的一個核心指標是消息送達率。為保證每一條消息準確送達,為每條消息都會開啟一個事務,從推送開始,到確認結束,如果超時未確認就會重發這條消息,這就是消息確認。

由於互聯網環境複雜,消息超時時間不能設置太短,尤其在移動弱網路環境下。在本系統的中超時設置為 10 秒。

我們通過實現 Future 自定義 NotifyFuture,為每個下行通知分配一個 seq,並定義 NotifyFuture 的 timeout。即每個下行通知分配一個 seq 存儲緩存中,等待客戶端回應這個應答,如果應答, 則從緩存移出這個 seq,否則等待超時,自動從緩存中被移出。

APNs 消息推送

iOS 在系統層面與蘋果 APNs(Apple Push Notification Service)伺服器建立連接,應用通過 Socket 向 APNs Server 推送消息,然後再由 APNs 進行推送。但是基於 Socket 的 APNs 協議是一種反人類的設計,在推送消息存在很多問題。

鑒於此,對 APNs 推送服務進行重構,基於 Netty 構建了 HTTP2 協議的推送服務,支持同步和非同步的推送方式;解決 Channel 異常及 InActive 時重連等問題,保證 HTTP2 推送管道的問題;同時通過 IdleStateHandler 保持 HTTP2 長連接的心跳 。

總結和感悟

最後,總結歷次的大促,京麥開發平台在進行服務化架構的演進過程中,所面臨的技術難點,最重要的還是服務治理,即調用關係的梳理。因為我們要打造的不是一個系統,也不是一堆系統,而是一個平台生態,能夠持續地提高系統的運營能力。

這裡以「精打細算,大道至簡」這句話結束此次京麥開放平台的總結。

作者介紹

張松然 ,2013 年加入京東,一直從事京東商家麥開放平台的架構設計和開發工作,熟悉大規模分散式系統架構。在 Web 開發、架構優化上有較豐富的實戰經歷。有多年 NIO 領域的設計、開發經驗,對 HTTP、TCP 長連接有深入研究與領悟。目前主要致力於多端開放平台技術架構的優化與實現。

是什麼導致了證券行業的軟體研發水平遠低於互聯網公司?

百度技術沙龍免費報名啦!百度大牛現場深度解析 Apollo 平台與智能駕駛方案——百度宣布開放 Apollo 自動駕駛平台以來,受到了社會各界的廣泛關注,更是在自動駕駛開發者群體中引發了熱烈反響。很多開發者非常期待可以深入了解 Apollo 平台的開放內容,以便更充分高效的利用 Apollo 平台,研究並落地自己對於自動駕駛的諸多想法。為此,本屆百度沙龍特地組織了 Apollo 自動駕駛平台專場。請點擊「閱讀原文」或者識別圖片中的二維碼免費報名。



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦