GRPC: 如何实现分布式日志跟踪?
簡介:?本文將介紹如何在 gRPC 分布式場景中,實現(xiàn) API 的日志跟蹤。
介紹
本文將介紹如何在 gRPC 分布式場景中,實現(xiàn) API 的日志追蹤。
什么是 API 日志追蹤?一個 API 請求會跨多個微服務(wù),我們希望通過一個唯一的 ID 檢索到整個鏈路的日志。
我們將會使用?rk-boot?來啟動 gRPC 服務(wù)。
請訪問如下地址獲取完整教程:
- https://rkdev.info/cn
- RK 文檔?(備用)
安裝
go get github.com/rookie-ninja/rk-boot快速開始
rk-boot?默認集成了 grpc-gateway,并且會默認啟動。
我們會創(chuàng)建 /api/v1/greeter API 進行驗證,同時開啟 logging, meta 和 tracing 攔截器以達到目的。
1. 創(chuàng)建 api/v1/greeter.proto
syntax = "proto3";package api.v1;option go_package = "api/v1/greeter";service Greeter {rpc Greeter (GreeterRequest) returns (GreeterResponse) {} }message GreeterRequest {string name = 1; }message GreeterResponse {string message = 1; }2. 創(chuàng)建 api/v1/gw_mapping.yaml
type: google.api.Service config_version: 3# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details. http:rules:- selector: api.v1.Greeter.Greeterget: /api/v1/greeter3. 創(chuàng)建 buf.yaml
version: v1beta1 name: github.com/rk-dev/rk-demo build:roots:- api4. 創(chuàng)建 buf.gen.yaml
version: v1beta1 plugins:# protoc-gen-go needs to be installed, generate go files based on proto files- name: goout: api/genopt:- paths=source_relative# protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files- name: go-grpcout: api/genopt:- paths=source_relative- require_unimplemented_servers=false# protoc-gen-grpc-gateway needs to be installed, generate grpc-gateway go files based on proto files- name: grpc-gatewayout: api/genopt:- paths=source_relative- grpc_api_configuration=api/v1/gw_mapping.yaml# protoc-gen-openapiv2 needs to be installed, generate swagger config files based on proto files- name: openapiv2out: api/genopt:- grpc_api_configuration=api/v1/gw_mapping.yaml5. 編譯 proto file
$ buf generate 如下的文件會被創(chuàng)建。 $ tree api/gen api/gen └── v1├── greeter.pb.go├── greeter.pb.gw.go├── greeter.swagger.json└── greeter_grpc.pb.go1 directory, 4 files6. 創(chuàng)建 bootA.yaml & serverA.go
Server-A 監(jiān)聽 1949 端口,并且發(fā)送請求給 Server-B。
我們通過 rkgrpcctx.InjectSpanToNewContext() 方法把 Tracing 信息注入到 Context 中,發(fā)送給 Server-B。
--- grpc:- name: greeter # Name of grpc entryport: 1949 # Port of grpc entryenabled: true # Enable grpc entryinterceptors:loggingZap:enabled: truemeta:enabled: truetracingTelemetry:enabled: true package mainimport ("context""demo/api/gen/v1""fmt""github.com/rookie-ninja/rk-boot""github.com/rookie-ninja/rk-grpc/interceptor/context""google.golang.org/grpc" )// Application entrance. func main() {// Create a new boot instance.boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootA.yaml"))// Get grpc entry with namegrpcEntry := boot.GetGrpcEntry("greeter")grpcEntry.AddRegFuncGrpc(registerGreeter)grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint)// Bootstrapboot.Bootstrap(context.Background())// Wait for shutdown sigboot.WaitForShutdownSig(context.Background()) }func registerGreeter(server *grpc.Server) {greeter.RegisterGreeterServer(server, &GreeterServer{}) }type GreeterServer struct{}func (server *GreeterServer) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {// Call serverB at 2008 with grpc clientopts := []grpc.DialOption{grpc.WithBlock(),grpc.WithInsecure(),}conn, _ := grpc.Dial("localhost:2008", opts...)defer conn.Close()client := greeter.NewGreeterClient(conn)// Inject current trace information into contextnewCtx := rkgrpcctx.InjectSpanToNewContext(ctx)client.Greeter(newCtx, &greeter.GreeterRequest{Name: "A"})return &greeter.GreeterResponse{Message: fmt.Sprintf("Hello %s!", request.Name),}, nil }7. 創(chuàng)建 bootB.yaml & serverB.go
Server-B 監(jiān)聽 2008 端口。
--- grpc:- name: greeter # Name of grpc entryport: 2008 # Port of grpc entryenabled: true # Enable grpc entryinterceptors:loggingZap:enabled: truemeta:enabled: truetracingTelemetry:enabled: true package mainimport ("context""demo/api/gen/v1""fmt""github.com/rookie-ninja/rk-boot""google.golang.org/grpc" )// Application entrance. func main() {// Create a new boot instance.boot := rkboot.NewBoot(rkboot.WithBootConfigPath("bootB.yaml"))// Get grpc entry with namegrpcEntry := boot.GetGrpcEntry("greeter")grpcEntry.AddRegFuncGrpc(registerGreeterB)grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint)// Bootstrapboot.Bootstrap(context.Background())// Wait for shutdown sigboot.WaitForShutdownSig(context.Background()) }func registerGreeterB(server *grpc.Server) {greeter.RegisterGreeterServer(server, &GreeterServerB{}) }type GreeterServerB struct{}func (server *GreeterServerB) Greeter(ctx context.Context, request *greeter.GreeterRequest) (*greeter.GreeterResponse, error) {return &greeter.GreeterResponse{Message: fmt.Sprintf("Hello %s!", request.Name),}, nil }8. 文件夾結(jié)構(gòu)
├── api │ ├── gen │ │ └── v1 │ │ ├── greeter.pb.go │ │ ├── greeter.pb.gw.go │ │ ├── greeter.swagger.json │ │ └── greeter_grpc.pb.go │ └── v1 │ ├── greeter.proto │ └── gw_mapping.yaml ├── bootA.yaml ├── bootB.yaml ├── buf.gen.yaml ├── buf.yaml ├── go.mod ├── go.sum ├── serverA.go └── serverB.go9. 啟動 ServerA & ServerB
$ go run serverA.go $ go run serverB.go10. 往 ServerA 發(fā)送請求
¥ curl "localhost:1949/api/v1/greeter?name=rk-dev"11. 驗證日志
兩個服務(wù)的日志中,會有同樣的 traceId,不同的 requestId。
我們可以通過 grep traceId 來追蹤 RPC。
- ServerA
- ServerB
概念
當(dāng)我們沒有使用例如 jaeger 調(diào)用鏈服務(wù)的時候,我們希望通過日志來追蹤分布式系統(tǒng)里的 RPC 請求。
rk-boot 的攔截器會通過 openTelemetry 庫來向日志寫入 traceId 來追蹤 RPC。
當(dāng)啟動了日志攔截器,原數(shù)據(jù)攔截器,調(diào)用鏈攔截器的時候,攔截器會往日志里寫入如下三種 ID。
EventId
當(dāng)啟動了日志攔截器,EventId 會自動生成。
--- grpc:- name: greeter # Name of grpc entryport: 1949 # Port of grpc entryenabled: true # Enable grpc entryinterceptors:loggingZap:enabled: true ------------------------------------------------------------------------ ... ids={"eventId":"cd617f0c-2d93-45e1-bef0-95c89972530d"} ...RequestId
當(dāng)啟動了日志攔截器和原數(shù)據(jù)攔截器,RequestId 和 EventId 會自動生成,并且這兩個 ID 會一致。
--- grpc:- name: greeter # Name of grpc entryport: 1949 # Port of grpc entryenabled: true # Enable grpc entryinterceptors:loggingZap:enabled: truemeta:enabled: true ------------------------------------------------------------------------ ... ids={"eventId":"8226ba9b-424e-4e19-ba63-d37ca69028b3","requestId":"8226ba9b-424e-4e19-ba63-d37ca69028b3"} ... 即使用戶覆蓋了 RequestId,EventId 也會保持一致。 rkgrpcctx.AddHeaderToClient(ctx, rkgrpcctx.RequestIdKey, "overridden-request-id") ------------------------------------------------------------------------ ... ids={"eventId":"overridden-request-id","requestId":"overridden-request-id"} ...TraceId
當(dāng)啟動了調(diào)用鏈攔截器,traceId 會自動生成。
--- grpc:- name: greeter # Name of grpc entryport: 1949 # Port of grpc entryenabled: true # Enable grpc entryinterceptors:loggingZap:enabled: truemeta:enabled: truetracingTelemetry:enabled: true ------------------------------------------------------------------------ ... ids={"eventId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","requestId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","traceId":"316a7b475ff500a76bfcd6147036951c"} ...原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的GRPC: 如何实现分布式日志跟踪?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云图数据库GDB V3引擎发布,加速
- 下一篇: 如何形成统一设计风格-实践篇