「每日分享」餓了麼 API-Router的設計與實現

點擊上方"java全棧技術"關注,每天學習一個java知識點

背景

餓了麼隨著業務量的增長,單個數據中心的容量無法支撐全部流量,同時傳統機房冷備份方式的缺陷,促使餓了麼異地多活應運而生;而作為多活重要組件之一的API-Router,扮演著客戶端入口流量到後端機房路由的重要角色,當一個機房出現故障甚至宕機,可以實現機房快速切換。

設計目標:

1、入口流量可以根據sharding key規則路由到對應的機房(準確的說是ezone);

2、配合機房切換規則,能實現流量的轉移;

3、儘可能的實現高性能、高可用、高併發,保護後端服務、削峰填谷;

前兩點,主要是API-Router所需要實現的主要功能;第三點,是面對餓了麼業務體量成倍的上漲帶來的挑戰所需要解決的問題;因此,API-Router定位為一個HTTP反向代理和負載均衡器,在此基礎上實現路由的分發。

一、流量路由實現篇

API-Router根據業務分集群部署在雲上,參考餓了麼異地多活的整體架構,API-Router的物理架構如下,

「每日分享」餓了麼 API-Router的設計與實現

1、API-Router從Router控制檯獲取維護的每個域名的ezone到數據中心地址的映射關係;

2、API-Router將訂閱GZS的關於shard到機房ezone的路由表;

3、API-Router在內存中,將請求頭中的sharding key(地理位置、商戶ID、訂單ID),根據一套sharding規則,計算出shard;最常見的一種規則算法是,根據用戶的經緯度,通過地裡圍欄算法,計算出相應的多邊形,而這個多邊形提前被分配了一個shard;

3、根據前3步,API-Router就擁有了sharding key-->shard-->ezone-->數據中心的映射表,最後將請求轉發到對應的數據中心上;

流量轉移的實現:

當後端機房故障時,API-Router會根據GZS主動推送的shard到ezone映射關係變更的消息,實現流量到ezone的切換,從而保證了服務的高可用;

二、技術實現篇

API-Router採用了基於Netty的全後端異步非阻塞模型,以此來儘可能減少請求在這一層上的網絡損耗,Epoll的特性使其支持更高的連接數,同時採取一些保護後端服務的措施來保證整個服務鏈路的穩定運行。

1、削峰填谷:

從架構圖上可知,API-Router處於網站的入口,因此在這一層對流量進行削峰填谷,可以對後端服務免受瞬時流量的衝擊起很大的保護作用。

每個http請求過來,首先會根據路由規則計算出後端的upstream,每個upstream對應一個scheduler,然後在scheduler裡面依次通過信號量來限併發、通過入隊列排隊來堆積請求,從而實現流量的整形,示意圖如下:

「每日分享」餓了麼 API-Router的設計與實現

2、連接複用

隨著餓了麼用戶量增長,客戶端到router機器連接數越來越高,鑑於TCP連接對性能的影響,router到後端採用了連接池複用技術,因此減少了到後端的連接數(以生產某機器為例,前端到Router連接數在10w左右,經過連接複用到後端減少到140+),示意圖如下:

「每日分享」餓了麼 API-Router的設計與實現

3、熱加載filter,處理request & response

API-Router作為處於網站鏈路最前端的一個Proxy,會有很多需要處理請求/響應的需求。為此,我們實現了一個類似攔截器鏈的功能,每個攔截器稱之為Filter,可以攔截request和response,然後對其做一些類似限流、熔斷、反爬蟲、白名單、跨域等處理。而每個filter都是動態從控制檯下發的,根據自定義的ClassLoader實現修改然後被動態加載到JVM,從而減少了發佈的次數。Filter的示意圖如下:

「每日分享」餓了麼 API-Router的設計與實現

4、多協議支持

API-Router目前支持了包括

  • http/https
  • websocket
  • grpc
  • http2
  • TCP

5、更多

為了進一步提升單節點每秒所承載的流量,降低資源損耗,我們還從以下等方面實現無阻塞,提高性能。

  • 異步日誌
  • 異步心跳
  • 異步打點
  • 異步Filter
  • 異步鏈接創建

展望未來

1、針對餓了麼外賣訂單高峰低谷波動特點,實現服務器資源分鐘級動態擴容和縮容,提高資源利用率;

2、進一步提供高系統的可用性,防爬、防刷、防網絡攻擊;

作者:羅輝, 2015年加入餓了麼, 現任餓了麼框架工具部資深工程師, 負責餓了麼API-Router項目。


分享到:


相關文章: