在微服務架構中,不同的微服務可以有不同的網(wǎng)絡地址,各個微服務之間通過互相調(diào)用完成用戶請求,客戶端可能通過調(diào)用N個微服務的接口完成一個用戶請求。因此,在客戶端和服務端之間增加一個API網(wǎng)關成為多數(shù)微服務架構的必然選擇。
在個推的微服務實踐中,API網(wǎng)關也起著至關重要的作用。一方面, API網(wǎng)關是個推微服務體系對外的唯一入口 ;另一方面, API網(wǎng)關中實現(xiàn)了很多后端服務的共性需求,避免了重復建設 。
個推微服務網(wǎng)關的設計與實現(xiàn)
個推微服務主要是基于Docker和Kubernetes進行實踐的。 在整個微服務架構中,最底層的是個推私有部署的Kubernetes集群,在集群之上,部署了應用服務。
個推的應用服務體系共分為三層,最上一層是網(wǎng)關層,接著是業(yè)務層,最下面是基礎層服務。 在部署應用服務時,我們使用了Kubernetes的命名空間對不同產(chǎn)品線的產(chǎn)品進行隔離。除了應用服務外, Kubernetes集群上還部署了Consul來實現(xiàn)配置的管理、Kube-DNS實現(xiàn)服務注冊與發(fā)現(xiàn),以及一些輔助系統(tǒng)來進行應用和集群的管理。
下圖是個推微服務體系的架構圖。
個推對API網(wǎng)關的功能需求主要有以下幾方面:
1. 要支持配置多個產(chǎn)品,為不同的產(chǎn)品提供不同的端口;
2. 動態(tài)路由;
3. URI的重寫;
4. 服務的注冊與發(fā)現(xiàn);
5. 負載均衡;
6. 安全相關的需求,如session校驗等;
7. 流量控制;
8. 鏈路追蹤;
9. A/B Testing。
在對市面上已有的網(wǎng)關產(chǎn)品進行調(diào)研后,我們的技術團隊發(fā)現(xiàn),它們并不太適合應用于個推的微服務體系。第一,個推配置的管理都是基于Consul實現(xiàn)的,而大部分網(wǎng)關產(chǎn)品都需要基于一些DB存儲,來進行配置的管理;第二,大部分的網(wǎng)關產(chǎn)品提供的功能比較通用,也比較完善,這同時也降低了配置的復雜度以及靈活性;第三,大部分的網(wǎng)關產(chǎn)品很難直接融入到個推的微服務架構體系中。
最終, 個推選擇使用了OperResty和Lua進行自研網(wǎng)關 ,在自研的過程中,我們也借鑒了其他網(wǎng)關產(chǎn)品的一些設計,如Kong和Orange的插件機制等。
個推的API網(wǎng)關的插件設計如下圖所示。
OpenResty對請求的處理分為多個階段。 個推API網(wǎng)關的插件主要是在Set、Rewrite、Access、Header_filter、Body_filter、Log這六個階段做相應的處理 ,其中,每一個插件都可以在一個或多個階段起到相應的作用。在一個請求到達API網(wǎng)關之后,網(wǎng)關會根據(jù)配置為該請求選擇插件,然后根據(jù)每個插件的規(guī)則,進一步過濾出匹配規(guī)則的插件,最后對插件進行實例化,對流量進行相應的處理。
我們可以通過舉例來理解這個過程,如上圖所示,
localhost:8080/api/demo/test/hello這個請求到達網(wǎng)關后,網(wǎng)關會根據(jù)host和端口確定產(chǎn)品信息,并提取出URI(/api/demo/test/hello),然后根據(jù)產(chǎn)品的具體配置,篩選出需要使用的插件——Rewrite_URI、Dyups和Auth,接下來根據(jù)每個插件的規(guī)則配置進行過濾,過濾后,只有Rewrite_URI和Dyups兩個插件被選中。之后實例化這兩個插件,在各個階段對請求進行處理。請求被轉發(fā)到后端服務時,URI就被rewrite為“/demo/test/hello”,upstream也被設置為“prod1-svc1”。請求由后端服務處理之后,響應會經(jīng)網(wǎng)關返回給客戶端,這就是整個插件的設計和工作的流程。為了優(yōu)化性能,我們將插件的實例化延緩到了請求真正開始處理時,在此之前,網(wǎng)關會通過產(chǎn)品配置和規(guī)則,過濾掉不需要執(zhí)行的插件。從圖中也可以看出,每個插件的規(guī)則配置都很簡單,并且沒有統(tǒng)一的格式,這也確保了插件配置的簡單靈活。
網(wǎng)關的配置均為熱更新,通過Consul和Consul-Template來實現(xiàn),配置在Consul上進行更新后,Consul-Template會將其實時地拉取下來,然后通過以下兩種方式進行更新。
(1)通過調(diào)用Update API,將配置更新到shared-dict中。
(2)更新配置文件,利用Reload OpenResty實現(xiàn)配置文件的更新。
個推微服務網(wǎng)關提供的主要功能
1.動態(tài)路由
動態(tài)路由主要涉及到三個方面:服務注冊、服務發(fā)現(xiàn)和請求轉發(fā)。
如下圖所示,服務的注冊和發(fā)現(xiàn)是基于Kubernetes的Service和Kube-DNS實現(xiàn)的,在Consul中,會維持一個服務的映射表,應用的每一個微服務都對應Kubernetes上的一個Service,每創(chuàng)建一個Service都會在Consul上的服務映射表中添加一項(會被實時更新到網(wǎng)關的共享內(nèi)存中)。網(wǎng)關每收到一個請求都會從服務映射表中查詢到具體的后端服務(即Kubernetes中的Service名),并進行動態(tài)路由。Kube-DNS可以將Service的域名解析成Kubernetes內(nèi)部的ClusterIP,而Service代理了多個Pod,會將流量均衡地轉發(fā)到不同的Pod上。
2.流量控制
流量控制主要是通過一個名為“Counter”的后端服務和網(wǎng)關中的流控插件實現(xiàn)的。 Counter負責存儲請求的訪問次數(shù)和限值,并且支持按時間維度進行計數(shù)。 流控插件負責攔截流量,調(diào)用Counter的接口進行超限查詢,如果Counter返回請求超限,網(wǎng)關就會直接拒絕訪問,實現(xiàn)限次的功能,再結合時間維度就可以實現(xiàn)限頻的需求。同時流控插件通過輸出日志信息到fluent-bit,由fluent-bit聚合計次來更新Counter中的計數(shù)。
3.鏈路追蹤
整個微服務體系的鏈路追蹤是基于分布式的鏈路追蹤系統(tǒng)Zipkin來實現(xiàn)的。通過在網(wǎng)關安裝Zipkin插件和在后端服務中引入Zipkin中間件,實現(xiàn)最終的鏈路追蹤功能。具體架構如下圖所示。
4. A/B測試
在A/B測試的實現(xiàn)中,有以下幾個關鍵點:
(1)所有的策略信息都配置在Consul上,并通過Consul-Template實時生效到各個微服務的內(nèi)存中;
(2)每條策略均有指明,調(diào)用一個微服務時應調(diào)用A還是B(默認為A);
(3)網(wǎng)關中實現(xiàn)A/B插件,在請求到達網(wǎng)關時,通過A/B插件配置的規(guī)則,即可確定請求適用的A/B策略;
(4)網(wǎng)關會將請求適用的A/B策略通過URL參數(shù)傳遞下去;
(5)每個微服務通過傳遞下來的策略,選擇正確的服務進行訪問。
下圖給出了兩種場景下的調(diào)用鏈路。
總結
以上就是個推微服務網(wǎng)關的設計和主要功能的實現(xiàn)。之后,個推的技術團隊會不斷提升API網(wǎng)關的彈性設計,使其能夠在故障出現(xiàn)時,縮小故障的影響范圍;同時,我們也會繼續(xù)將網(wǎng)關與DevOps平臺做進一步地結合,以確保網(wǎng)關在迭代更新時,能夠有更多的自動化測試來保證質(zhì)量,實現(xiàn)更快速地部署。
關注頭條號《 JAVA 后端架構 》 ,話癆技術,職場,招聘,在線面試,進階提升。每天一篇技術分享