简介: 本文将介绍如何在 gRPC 分布式场景中,实现 API 的日志跟踪。

介绍

本文将介绍如何在 gRPC 分布式场景中,实现 API 的日志追踪。

什么是 API 日志追踪?

一个 API 请求会跨多个微服务,我们希望通过一个唯一的 ID 检索到整个链路的日志。

我们将会使用 rk-boot 来启动 gRPC 服务。

请访问如下地址获取完整教程:

安装

go get github.com/rookie-ninja/rk-boot

快速开始

rk-boot 默认集成了 grpc-gateway,并且会默认启动。

我们会创建 /api/v1/greeter API 进行验证,同时开启 logging, meta 和 tracing 拦截器以达到目的。

1. 创建 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. 创建 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.Greeter
get: /api/v1/greeter

3. 创建 buf.yaml

version: v1beta1
name: github.com/rk-dev/rk-demo
build:
roots:
- api

4. 创建 buf.gen.yaml

version: v1beta1
plugins:
# protoc-gen-go needs to be installed, generate go files based on proto files
- name: go
out: api/gen
opt:
- paths=source_relative
# protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files
- name: go-grpc
out: api/gen
opt:
- 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-gateway
out: api/gen
opt:
- 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: openapiv2
out: api/gen
opt:
- grpc_api_configuration=api/v1/gw_mapping.yaml

5. 编译 proto file

$ buf generate

如下的文件会被创建。

$ tree api/gen
api/gen
└── v1
├── greeter.pb.go
├── greeter.pb.gw.go
├── greeter.swagger.json
└── greeter_grpc.pb.go 1 directory, 4 files

6. 创建 bootA.yaml & serverA.go

Server-A 监听 1949 端口,并且发送请求给 Server-B。

我们通过 rkgrpcctx.InjectSpanToNewContext() 方法把 Tracing 信息注入到 Context 中,发送给 Server-B。

---
grpc:
- name: greeter # Name of grpc entry
port: 1949 # Port of grpc entry
enabled: true # Enable grpc entry
interceptors:
loggingZap:
enabled: true
meta:
enabled: true
tracingTelemetry:
enabled: true

package main

import (
"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 name
grpcEntry := boot.GetGrpcEntry("greeter")
grpcEntry.AddRegFuncGrpc(registerGreeter)
grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint) // Bootstrap
boot.Bootstrap(context.Background()) // Wait for shutdown sig
boot.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 client
opts := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithInsecure(),
}
conn, _ := grpc.Dial("localhost:2008", opts...)
defer conn.Close()
client := greeter.NewGreeterClient(conn) // Inject current trace information into context
newCtx := rkgrpcctx.InjectSpanToNewContext(ctx)
client.Greeter(newCtx, &greeter.GreeterRequest{Name: "A"}) return &greeter.GreeterResponse{
Message: fmt.Sprintf("Hello %s!", request.Name),
}, nil
}

7. 创建 bootB.yaml & serverB.go

Server-B 监听 2008 端口。

---
grpc:
- name: greeter # Name of grpc entry
port: 2008 # Port of grpc entry
enabled: true # Enable grpc entry
interceptors:
loggingZap:
enabled: true
meta:
enabled: true
tracingTelemetry:
enabled: true

package main

import (
"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 name
grpcEntry := boot.GetGrpcEntry("greeter")
grpcEntry.AddRegFuncGrpc(registerGreeterB)
grpcEntry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint) // Bootstrap
boot.Bootstrap(context.Background()) // Wait for shutdown sig
boot.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. 文件夹结构

├── 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.go

9. 启动 ServerA & ServerB

$ go run serverA.go
$ go run serverB.go

10. 往 ServerA 发送请求

¥ curl "localhost:1949/api/v1/greeter?name=rk-dev"

11. 验证日志

两个服务的日志中,会有同样的 traceId,不同的 requestId。

我们可以通过 grep traceId 来追踪 RPC。

  • ServerA
------------------------------------------------------------------------
endTime=2021-10-20T00:02:21.739688+08:00
...
ids={"eventId":"0d145356-998a-4999-ab62-6f1b805274a0","requestId":"0d145356-998a-4999-ab62-6f1b805274a0","traceId":"c36a45eb076066df39fa407174012369"}
...
operation=/api.v1.Greeter/Greeter
resCode=OK
eventStatus=Ended
EOE

  • ServerB
------------------------------------------------------------------------
endTime=2021-10-20T00:02:21.739125+08:00
...
ids={"eventId":"8858a6eb-e953-42ad-bdc3-c466bbbd798e","requestId":"8858a6eb-e953-42ad-bdc3-c466bbbd798e","traceId":"c36a45eb076066df39fa407174012369"}
...
operation=/api.v1.Greeter/Greeter
resCode=OK
eventStatus=Ended
EOE

概念

当我们没有使用例如 jaeger 调用链服务的时候,我们希望通过日志来追踪分布式系统里的 RPC 请求。

rk-boot 的拦截器会通过 openTelemetry 库来向日志写入 traceId 来追踪 RPC。

当启动了日志拦截器,原数据拦截器,调用链拦截器的时候,拦截器会往日志里写入如下三种 ID。

EventId

当启动了日志拦截器,EventId 会自动生成。

---
grpc:
- name: greeter # Name of grpc entry
port: 1949 # Port of grpc entry
enabled: true # Enable grpc entry
interceptors:
loggingZap:
enabled: true

------------------------------------------------------------------------
...
ids={"eventId":"cd617f0c-2d93-45e1-bef0-95c89972530d"}
...

RequestId

当启动了日志拦截器和原数据拦截器,RequestId 和 EventId 会自动生成,并且这两个 ID 会一致。

---
grpc:
- name: greeter # Name of grpc entry
port: 1949 # Port of grpc entry
enabled: true # Enable grpc entry
interceptors:
loggingZap:
enabled: true
meta:
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

当启动了调用链拦截器,traceId 会自动生成。

---
grpc:
- name: greeter # Name of grpc entry
port: 1949 # Port of grpc entry
enabled: true # Enable grpc entry
interceptors:
loggingZap:
enabled: true
meta:
enabled: true
tracingTelemetry:
enabled: true

------------------------------------------------------------------------
...
ids={"eventId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","requestId":"dd19cf9a-c7be-486c-b29d-7af777a78ebe","traceId":"316a7b475ff500a76bfcd6147036951c"}
...

原文链接

本文为阿里云原创内容,未经允许不得转载。

GRPC: 如何实现分布式日志跟踪?的更多相关文章

  1. 【SpringCloud构建微服务系列】分布式链路跟踪Spring Cloud Sleuth

    一.背景 随着业务的发展,系统规模越来越大,各微服务直接的调用关系也变得越来越复杂.通常一个由客户端发起的请求在后端系统中会经过多个不同的微服务调用协同产生最后的请求结果,几乎每一个前端请求都会形成一 ...

  2. 循序渐进看Java web日志跟踪(1)-Tomcat 日志追踪与配置

    日志,是软件运行过程中,对各类操作中重要信息的记录. 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用.项目开发过程中,对日志的记录规则,也将 ...

  3. spring-cloud-sleuth 和 分布式链路跟踪系统

    ==================spring-cloud-sleuth==================spring-cloud-sleuth 可以用来增强 log 的跟踪识别能力, 经常在微服 ...

  4. springcloud 分布式服务跟踪sleuth+zipkin

    原文:https://www.jianshu.com/p/6ef0b76b9c26 分布式服务跟踪需求 随着分布式服务越来越多,调用关系越来越复杂,组合接口越来越多,要进行分布式服务跟踪监控的需求也越 ...

  5. 第11章 分布式服务跟踪: Spring Cloud Sleuth

    通常一个由客户端发起的请求在后端系统中会经过多个不同的微服务调用来协同产生最后的请求结果, 在复杂的微服务架构系统中, 几乎每一个前端请求都会形成一条复杂的分布式服务调用链路, 在每条链路中任何一个依 ...

  6. 跟我学SpringCloud | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪

    SpringCloud系列教程 | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪 Springboot: 2.1.6.RELEASE SpringCloud: ...

  7. Spring Cloud第九篇 | 分布式服务跟踪Sleuth

    ​ ​本文是Spring Cloud专栏的第九篇文章,了解前八篇文章内容有助于更好的理解本文: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring Cl ...

  8. Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践

    Java生鲜电商平台-SpringCloud分布式请求跟踪系统设计与实践 Java生鲜电商平台微服务现状 某个服务挂了,导致上游大量报警,如何快速定位哪个服务出问题? 某个核心挂了,导致大量报错,如何 ...

  9. 【Spring Cloud】Spring Cloud之Spring Cloud Sleuth,分布式服务跟踪(1)

    一.Spring Cloud Sleuth组件的作用 为微服务架构增加分布式服务跟踪的能力,对于每个请求,进行全链路调用的跟踪,可以帮助我们快速发现错误根源以及监控分析每条请求链路上的性能瓶颈等. 二 ...

  10. SpringCloud入门(十一):Sleuth 与 Zipkin分布式链路跟踪

    现今业界分布式服务跟踪的理论基础主要来自于 Google 的一篇论文<Dapper, a Large-Scale Distributed Systems Tracing Infrastructu ...

随机推荐

  1. FFmpeg命令行之FFmpeg 采集设备

    在使用 FFmpeg 作为编码器时,可以使用FFmpeg采集本地的音视频采集设备的数据,然后进行编码.封装.传输等操作. 例如,我们可以采集摄像头的图像作为视频,采集麦克风的数据作为音频,然后对采集的 ...

  2. C++ Qt开发:QTcpSocket网络通信组件

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QTc ...

  3. 记录--求你了,别再说不会JSONP了

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 JSONP是一种很远古用来解决跨域问题的技术,当然现在实际工作当中很少用到该技术了,但是很多同学在找工作面试过程中还是经常被问到,本文将带 ...

  4. OpenCV常量值含义表

    色彩空间转换常量 常量值 说明 cv2.COLOR_BGR2GRAY 从 BGR 色彩空间转换到 GRAY 色彩空间 cv2.COLOR_RGB2GRAY 从 RGB 色彩空间转换到 GRAY 色彩空 ...

  5. ZYNQ系列学习GPIO实验

    GPIO实验 一.实验原理 调用GPIO实现PS对引脚的控制 二.实验步骤 1.建立工程 这部分是ivado的操作内容,这里不做过多说明. 2.添加ZYNQ处理器IP 在左侧菜单栏中双击Create  ...

  6. BeautifulSoup 库 和 re 库 解析腾讯视频电影

    1 import requests 2 import json 3 from bs4 import BeautifulSoup #网页解析获取数据 4 import sys 5 import re 6 ...

  7. #树上带修莫队,树链剖分#洛谷 4074 [WC2013]糖果公园

    题目 分析 考虑将树转换成序列求解,那就用欧拉序,入栈一次出栈一次正好抵消掉 注意当起点不是LCA的时候要将起点加入,剩下就是带修莫队板子题了 代码 #include <cstdio> # ...

  8. 编译opencv: Linux编译opencv

    opencv官网:https://opencv.org/releases/ github下载地址:https://github.com/opencv/opencv/releases     mkdir ...

  9. Qt 5.12.10 国际化

    网上有资料但是不全,所以这里记录一份比较全的 1.创建项目 2.编辑 demo.cpp 这里写button用来做国际化示例,运行软件后是这个样子 #include "demo.h" ...

  10. Jenkins首次启动慢

    场景描述启动Jenkins后,打开网站,发现一直卡在这个启动页面,慢,很慢,非常慢 解决方法 进入Jenkins的安装目录,找到"hudson.model.UpdateCenter.xml& ...