在服務網格的流量管理和可觀測性實作上,Headers發揮着非常關鍵的作用。相比而言,HTTP協定的Headers實作較為容易,因為HTTP是同步阻塞式的請求響應模式,可以很容易在GET/POST/UPDATE/DELETE方法中定義和使用讀寫Header的API。GRPC協定的Headers則要複雜一些,各種程式設計語言在4種不同通信模型中,讀寫Header的形式的差異化很大,同時還要考慮流式和異步的程式設計實作。
本篇首先介紹4種程式設計語言的Headers程式設計實踐,然後講述在服務網格實踐中,GRPC協定Headers的兩個重要實踐:流量管理和可觀測性。
1 GRPC協定Headers程式設計實踐
服務端擷取Headers
GRPC通信模型程式設計語言 | Java | Go | NodeJs | Python |
---|---|---|---|---|
基本方法 | 實作攔截器 接口的 方法,通過 擷取header資訊, 方法入參類型為 | , 是一個 | ,傳回值類型是 類型定義為 | ,傳回值類型為2-tuple數組,2-tuple的形式為 ,使用 , 周遊擷取鍵值對 |
Unary RPC | 對Headers無感覺 | 在方法中直接調用 ,上下文參數 來自Talk的入參 | 在方法内直接調用 | |
Server streaming RPC | 同上 | 從TalkOneAnswerMore的入參 中擷取: | ||
Client streaming RPC | 從TalkMoreAnswerOne的入參 | |||
Bidirectional streaming RPC | 從TalkBidirectional的入參 |
用戶端發送Headers
方法,實作傳回值類型 的 填充header資訊, 方法入參 的類型為 | | | 變量填充 最終轉為list tuple類型 | |
| 在方法内直接使用基本方法 | |||
Propaganda Headers
由于鍊路追蹤需要将上遊傳遞過來的鍊路中繼資料透傳給下遊,以形成同一條請求鍊路的完整資訊,我們需要将服務端擷取的Headers資訊中,和鍊路追蹤相關的Headers透傳給向下遊發起請求的用戶端。這就是"Propaganda Headers"的概念。
如上所述,除了Java語言的實作,其他語言的通信模型方法都對header有感覺,是以可以将"服務端讀取-傳遞-用戶端發送"這三個動作順序地在4種通信模型方法内部實作。
Java語言讀取和寫入Headers是通過兩個攔截器分别實作的,是以Propaganda Headers無法在一個順序的流程裡實作,且考慮到并發因素,以及隻有讀取攔截器知道鍊路追蹤的唯一ID,我們無法通過最直覺的緩存方式搭建兩個攔截器的橋梁。
那麼隻能借助上下文了。幸好Java語言的實作提供了一種Metadata-Context Propagation的機制。
在伺服器攔截器讀取階段,通過
ctx.withValue(key, metadata)
将Metadata/Header存入Context,其中
key
是
Context.Key<String>
類型。然後在用戶端攔截器中,通過
key.get()
将Metadata從Context讀出,
get
方法預設使用
Context.current()
上下文,這就保證了一次請求的Headers讀取和寫入使用的是同一個上下文。
有了Propaganda Headers的實作,基于GRPC的鍊路追蹤就有了機制上的保證。
2 網格拓撲
完成GRPC的Headers處理後,我們進入部署和驗證目錄
tracing。該目錄下包含4種程式設計語言的部署腳本。我們以go版本為例,執行如下腳本進行部署和驗證。
cd go
# 部署
sh apply.sh
# 驗證
sh test.sh
部署後的服務網格拓撲如下圖所示。
3 流量轉移
在VirtualService中通過定義Header鍵值的比對條件,可以實作根據請求動态地進行流量轉移。如果再結合前一篇中講述的按API切流、按版本切流的實踐,就可以完成應用級的精細化流量管理。
進入服務網格(ASM)執行個體,建立VirtualService,将如下内容複制儲存。這個
VirtualService
定義了Header中
server-version=go
的請求100%流量路由到go版本服務。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
namespace: grpc-best
name: grpc-server-vs
spec:
hosts:
- "*"
gateways:
- grpc-gateway
http:
- match:
- headers:
server-version:
exact: go
route:
- destination:
host: grpc-server-svc
subset: v2
weight: 100
4 鍊路追蹤
進入服務網格(ASM)執行個體,在功能設定中勾選啟用鍊路追蹤,采樣方式選擇阿裡雲XTrace。
在本地執行如下請求腳本,向Ingressgateway發起多次請求。
USER_CONFIG=~/shop_config/ack_bj
alias k="kubectl --kubeconfig $USER_CONFIG"
INGRESS_IP=$(k -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
docker run -d --name grpc_client_node -e GRPC_SERVER="${INGRESS_IP}" registry.cn-beijing.aliyuncs.com/asm_repo/grpc_client_node:1.0.0 /bin/sleep 3650d
client_node_container=$(docker ps -q)
echo "Test in a loop:"
for i in {1..100}; do
docker exec -e GRPC_SERVER="${INGRESS_IP}" -it "$client_node_container" node mesh_client.js ${INGRESS_IP} 6666
done
在服務網格(ASM)執行個體的左側菜單中點選"鍊路追蹤",檢視請求鍊路資訊。如下圖所示,完整的鍊路包括:
本地請求端
-
Ingressgateway
grpc-server-svc1
grpc-server-svc2
grpc-server-svc3
。