天天看點

服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐

在服務網格的流量管理和可觀測性實作上,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
基本方法 實作攔截器

ServerInterceptor

接口的

interceptCall(ServerCall<ReqT, RespT> call,final Metadata m,ServerCallHandler<ReqT, RespT> h)

方法,通過

String v = m.get(k)

擷取header資訊,

get

方法入參類型為

Metadata.Key<String>

metadata.FromIncomingContext(ctx)(md MD, ok bool)

MD

是一個

map[string][]string

call.metadata.getMap()

,傳回值類型是

[key: string]: MetadataValue

MetadataValue

類型定義為

string/Buffer

context.invocation_metadata()

,傳回值類型為2-tuple數組,2-tuple的形式為

('k','v')

,使用

m.key

,

m.value

周遊擷取鍵值對
Unary RPC 對Headers無感覺 在方法中直接調用

metadata.FromIncomingContext(ctx)

,上下文參數

ctx

來自Talk的入參
在方法内直接調用

call.metadata.getMap()

context.invocation_metadata()

Server streaming RPC 同上

metadata.FromIncomingContext(ctx)

ctx

從TalkOneAnswerMore的入參

stream

中擷取:

stream.Context()

Client streaming RPC

metadata.FromIncomingContext(ctx)

ctx

從TalkMoreAnswerOne的入參

stream

stream.Context()

Bidirectional streaming RPC

metadata.FromIncomingContext(ctx)

ctx

從TalkBidirectional的入參

stream

stream.Context()

用戶端發送Headers

ClientInterceptor

interceptCall(MethodDescriptor<ReqT, RespT> m, CallOptions o, Channel c)

方法,實作傳回值類型

ClientCall<ReqT, RespT>

start((Listener<RespT> l, Metadata h))

h.put(k, v)

填充header資訊,

put

方法入參

k

的類型為

Metadata.Key<String>

v

String

metadata.AppendToOutgoingContext(ctx,kv ...) context.Context

metadata=call.metadata.getMap()

metadata.add(key, headers[key])

metadata_dict = {}

變量填充

metadata_dict[c.key] = c.value

最終轉為list tuple類型

list(metadata_dict.items())

metadata.AppendToOutgoingContext(ctx,kv)

在方法内直接使用基本方法

Propaganda Headers

由于鍊路追蹤需要将上遊傳遞過來的鍊路中繼資料透傳給下遊,以形成同一條請求鍊路的完整資訊,我們需要将服務端擷取的Headers資訊中,和鍊路追蹤相關的Headers透傳給向下遊發起請求的用戶端。這就是"Propaganda Headers"的概念。

如上所述,除了Java語言的實作,其他語言的通信模型方法都對header有感覺,是以可以将"服務端讀取-傳遞-用戶端發送"這三個動作順序地在4種通信模型方法内部實作。

Java語言讀取和寫入Headers是通過兩個攔截器分别實作的,是以Propaganda Headers無法在一個順序的流程裡實作,且考慮到并發因素,以及隻有讀取攔截器知道鍊路追蹤的唯一ID,我們無法通過最直覺的緩存方式搭建兩個攔截器的橋梁。

那麼隻能借助上下文了。幸好Java語言的實作提供了一種Metadata-Context Propagation的機制。

服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐

在伺服器攔截器讀取階段,通過

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           

部署後的服務網格拓撲如下圖所示。

服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐

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

服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐
服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐
服務網格GRPC協定多種程式設計語言實踐-5.GRPC協定Headers網格實踐