kratos项目中使用jaeger做链路追踪

项目地址

https://gitee.com/huoyingwhw/kratos-trace

简单说明

1、注意项目中使用同一个版本的otel包!

2、设置属性的时候,需要用到otel/semconv这个包,我这里用的是1.12.0,如果用低版本的话,semconv.SchemaURL 这个失效了,项目会报错!

jaeger界面

1、可以看到链路关系:

 

2、可以看到一个操作详细的调用过程以及执行的时间,也可以看到redis操作的命令:

使用trace包发送http请求并将http请求与响应信息放在trace中 *****

实际中也可以将业务中想要跟踪的数据用同样的方式放到trace中,比如userId等...

resty包地址:https://github.com/go-resty/resty

resty包测试以及相关方法

package http_resty

import (
    "github.com/go-resty/resty/v2"
    "time"
)

func NewRestyHttpClient() *resty.Client {
    client := resty.New()
    client.SetTimeout(time.Second) // 可以设置超时时间
    return client
}
resty.go
package http_resty

import (
    "fmt"
    "testing"
)

// Notice 只测试GET请求 主要看一下返回的结果以及trace的结果
// Notice 详细的demo参考官方文档的例子:https://github.com/go-resty/resty
func TestGetRequest(t *testing.T) {

    client := NewRestyHttpClient()

    resp, err := client.R().
        SetQueryParams(map[string]string{
            "name": "whw",
        }).
        EnableTrace().
        Get("http://127.0.0.1:9099/index")

    // Explore response object
    fmt.Println("Response Info:")
    fmt.Println("  Error      :", err)
    fmt.Println("  Status Code:", resp.StatusCode())
    fmt.Println("  Status     :", resp.Status())
    fmt.Println("  Proto      :", resp.Proto())
    fmt.Println("  Time       :", resp.Time())
    fmt.Println("  Received At:", resp.ReceivedAt())
    fmt.Println("  Body       :", resp)
    fmt.Println()

    // Explore trace info
    fmt.Println("Request Trace Info:")
    ti := resp.Request.TraceInfo()
    fmt.Println("  DNSLookup     :", ti.DNSLookup)
    fmt.Println("  ConnTime      :", ti.ConnTime)
    fmt.Println("  TCPConnTime   :", ti.TCPConnTime)
    fmt.Println("  TLSHandshake  :", ti.TLSHandshake)
    fmt.Println("  ServerTime    :", ti.ServerTime)
    fmt.Println("  ResponseTime  :", ti.ResponseTime)
    fmt.Println("  TotalTime     :", ti.TotalTime)
    fmt.Println("  IsConnReused  :", ti.IsConnReused)
    fmt.Println("  IsConnWasIdle :", ti.IsConnWasIdle)
    fmt.Println("  ConnIdleTime  :", ti.ConnIdleTime)
    fmt.Println("  RequestAttempt:", ti.RequestAttempt)
    fmt.Println("  RemoteAddr    :", ti.RemoteAddr.String())

    /*
        Response Info:
          Error      : <nil>
          Status Code: 200
          Status     : 200 OK
          Proto      : HTTP/1.1
          Time       : 5.019581959s
          Received At: 2023-06-12 16:16:07.815538 +0800 CST m=+5.021425710
          Body       : hello GET whw

        Request Trace Info:
          DNSLookup     : 0s
          ConnTime      : 526.208µs
          TCPConnTime   : 313.709µs
          TLSHandshake  : 0s
          ServerTime    : 5.018870584s
          ResponseTime  : 386.25µs
          TotalTime     : 5.019581959s
          IsConnReused  : false
          IsConnWasIdle : false
          ConnIdleTime  : 0s
          RequestAttempt: 1
          RemoteAddr    : 127.0.0.1:9099

    */

}
resty_test.go

链路

使用ctxWithoutCancel异步发http请求时主协程的ctx停了不影响子协程 *****

新的发送http请求的方法在biz/httpPushCtxWithoutCancel.go中,并且为了测试效果每个方法都先sleep了2秒才使用ctx发送http请求。

使用主协程的ctx测试效果

使用ctxWithoutCancel测试效果

utils包中封装的方法与测试用例如下:

package utils

import (
    "context"
    "time"
)

type WithoutCancelContext struct {
    Context context.Context
}

func (w *WithoutCancelContext) Done() <-chan struct{} {
    return make(chan struct{})
}

func (w *WithoutCancelContext) Err() error {
    return nil
}

func (w *WithoutCancelContext) Deadline() (time.Time, bool) {
    return time.Time{}, false
}

func (w *WithoutCancelContext) Value(key interface{}) interface{} {
    return w.Context.Value(key)
}

func WithoutCancel(ctx context.Context) context.Context {
    return &WithoutCancelContext{Context: ctx}
}
ctx_without_cancel.go
package utils

import (
    "context"
    "fmt"
    "sync"
    "testing"
    "time"
)

func TestT1(t *testing.T) {
    // 创建一个带取消功能的context
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()

    // 将带取消功能的context转换为无取消功能的context
    ctxWithoutCancel := WithoutCancel(ctx)

    // 使用无取消功能的context执行任务
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()

        select {
        case <-ctxWithoutCancel.Done():
            fmt.Println("Context canceled")
        default:
            fmt.Println("Context not canceled")
        }
    }()

    // 模拟一段时间后取消context
    time.Sleep(2 * time.Second)
    cancel()

    wg.Wait()
}
ctx_without_cancel_test.go

初始化一个ctxWithoutCancel测试一下:

链路也有数据:

~~~

碎碎念 

~我出的这些视频偏向基础使用方面,我是将实际中的大项目的每一个点拆分出来在单独的项目框架中做一个分享。

~实际中我们去公司的第一件事情是实现需求~实现需求就需要了解项目中每一个组件具体的实现细节,这样我们在写业务代码的时候才不会出现严重的bug!

~~在现实中大家的精力其实都是有限的,我有一个体会,比如说学了很多细节的知识点,golang的异步知识,什么基础的waitgroup、等待锁、非等待锁、信号量、sync.Once实现单例模式这些,再比如etcd、各种消息中间件的原理啥的,但是实际在写业务代码的时候用到的点非常分散,而且学了的知识不可能立刻就用,有可能得隔一段时间甚至几个月才会有业务涉及到,所以我觉得学知识要功利一些,我们去公司拿到一个大项目,先把里面涉及到的点拆解出来,写业务的时候先用起来,因为业务代码中正在使用的东西已经是比较稳定的了,自己学到的底层的知识点更多的是在实现业务、跟同事讨论业务细节之前,因为某一些机制有些方案不能这样实现~~等等~

~~所以我个人的经验是对于一些公司项目中自己遇到的知识点,先学会使用,然后自己根据自己的想法实现一遍并写一个小的demo,中间遇到问题再查阅资料,尤其是包版本的问题,公司线上的代码使用的模块或者包不会太新,自己使用新版本的包实现一下跟原来的效果比较一下,如果不同的话再具体研究下,总结一下~比如自己之前遇到过kratos-transport包版本的问题,线上自己上了一个新版的,结果发现不稳定,消费消息的时候总有EOF,换成旧版本就没问题了~~

~~后面有时间,再慢慢去研究详细的细节以及某一些工具的使用原理,比如使用链路追踪,我们先知道它在项目中的必要性,然后学习下既有项目怎么用的,自己新建一个项目把它跑起来,然后再慢慢去各种社区论坛中看看别人的文章、帖子,拓展自己的知识面。

~至于底层原理,比如kratos-transport、链路、服务发现etcd这些当然作为后端来说了解的越多、越细致越好。

posted on 2023-05-15 16:44  江湖乄夜雨  阅读(254)  评论(0编辑  收藏  举报