foreword

Link tracking is a must-have tool for every microservice architecture. Of course, go-zero has already been considered for us, and it can be used only by adding configuration in the configuration.

About the principle of how go-zero is tracked, some students have shared it before, so I won’t say more here. If you want to know more about it, go to https://mp.weixin.qq.com/s/hJEWcWc3PnGfWfbPCHfM9g All right. By default, tracking will be added to the middleware of the api and the interceptor of the rpc. If you don’t know how go-zero uses the default link tracking by default, please move to my open source project go-zero-looklook document https://github. com/Mikaelemmmm/go-zero-looklook/blob/main/doc/chinese/12-%E9%93%BE%E8%B7%AF%E8%BF%BD%E8%B8%AA.md.

What I want to talk about today is that, in addition to the link tracking that go-zero has integrated for us by default in the middleware of the api and the interceptor of the rpc, we want to add the link tracking code in some local methods or we want to send a When the message is sent to mq service, I want to connect the whole link including mq’s producer and consumer. How to do it in go-zero.

Scenes

Let’s briefly talk about the scene of our small demo, a request comes in to call the api Login method, first call the rpc in the Login method GetUserByMobile method, and then call the api local local method, followed by calling rabbitmq Pass message to mq service.

go-zero integrates jaeger and zinpink by default, here we take jaeger as an example

The link we want to see is

api.Login -> rpc.GetUserByMobile”/></p><p>That is, three sub-links are derived from the api,<code>api.producerMq</code> There is a call <code>mq.Consumer</code> sublinks.</p><p>We need two factors to add a method to the link, a traceId and a span. When we open the span under the same traceId to connect the related spans in series, if we want to form a parent-child relationship, we must put the span are connected with each other because “<strong>Microservice Practice</strong>“There are too many principles explained in the official account, so I will briefly mention them here without too much involved. If you are not particularly familiar with the principles, you can read the article recommended at the beginning of the article. Here we only need to know <code>traceId</code> and <code>spanId</code> The relationship is fine.</p><h2>core business code</h2><p>1. First, in the API <code>LoginLogic</code> the code</p><pre><code class=type LoginLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic { return &amp;LoginLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } type MsgBody struct { Carrier *propagation.HeaderCarrier Msg string } func (l *LoginLogic) Login(req *types.RegisterReq) (*types.AccessTokenResp, error) { resp, err := l.svcCtx.UserRpc.GetUserByMobile(l.ctx, &amp;usercenter.GetUserByMobileReq{ Mobile: req.Mobile, }) if err != nil { return &amp;types.AccessTokenResp{}, nil } l.local() tracer := otel.GetTracerProvider().Tracer(trace.TraceName) spanCtx, span := tracer.Start(l.ctx, "send_msg_mq", oteltrace.WithSpanKind(oteltrace.SpanKindProducer)) carrier := &amp;propagation.HeaderCarrier{} otel.GetTextMapPropagator().Inject(spanCtx, carrier) producer := rabbit.NewRabbitmqPublisher(RabbitmqDNS) msg := &amp;MsgBody{ Carrier: carrier, Msg: req.Mobile, } b, err := json.Marshal(msg) if err != nil{ panic(err) } if err := producer.Publish(spanCtx, ExchangeName, RoutineKeys, b); err != nil { logx.Errorf("Publish Fail , msg :%s , err:%v", msg, err) } span.End() return &amp;types.AccessTokenResp{ AccessExpire: resp.User.Id, }, err } func (l *LoginLogic) local() { tracer := otel.GetTracerProvider().Tracer(trace.TraceName) _ , span := tracer.Start(l.ctx, "local", oteltrace.WithSpanKind(oteltrace.SpanKindInternal)) defer span.End() // 执行你的代码 ..... }

2. In rpc GetUserByMobile the code

func (s *Logic) GetUserByMobile(context.Context, *usercenterPb.GetUserByMobileReq) (*usercenterPb.GetUserByMobileResp, error) {
  vo := &amp;usercenterPb.UserVo{
    Id: 1,
  }
  return &amp;usercenterPb.GetUserByMobileResp{
    User: vo,
  }, nil
}

3. In mq Consumer the code

type MsgBody struct {
  Carrier *propagation.HeaderCarrier
  Msg     string
}

func (c *consumer) Consumer(ctx context.Context, data []byte) error {
  var msg MsgBody
  if err := json.Unmarshal(data, &amp;msg); err != nil {
    logx.Errorf(" consumer err : %v", err)
  } else {
    logx.Infof("consumerOne Consumer  , msg:%+v", msg)

    wireContext := otel.GetTextMapPropagator().Extract(ctx, msg.Carrier)
    tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
    _, span := tracer.Start(wireContext, "mq_consumer_msg", oteltrace.WithSpanKind(oteltrace.SpanKindConsumer))

    defer span.End()
  }

  return nil
}

Detailed code

1. go-zero default integration

When a request enters the api, we can see https://github.com/zeromicro/go-zero/blob/master/rest/engine.go#L92 in the go-zero source code. go-zero has helped us add the first layer of trace in the middleware of the api. When entering the Login method, we call the rpc GetUserByMobile Method, through the source code of go-zero https://github.com/zeromicro/go-zero/blob/master/zrpc/internal/rpcserver.go#L55, you can see that the interceptor in rpc is also added by default for us. These two layers are done by go-zero for us by default.

2. Local methods

When the rpc is called GetUserByMobile After that, the api calls the local localif we want to reflect it on the entire link and call the local local method, the default go-zero does not help us, we need to add it manually.

  tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
  _ , span := tracer.Start(l.ctx, "local",  oteltrace.WithSpanKind(oteltrace.SpanKindInternal))
  defer span.End()
 
// 执行你的代码 .....

We get the tracer through the above code, open a local span after ctx, because the parent span will be obtained from ctx at the time of start, so the parent-child call relationship will be connected in series between the local method and Login, so this operation will be added to this link

3. Mq producer to mq consumer

How do we connect this link in series in mq delivery?that is to form api.Login-&gt;api.producer-&gt;mq.Consumer.

Think about the principle, although across the network, api can pass header pass, rpc can pass metadata pass, then mq can also pass header,body Just pass it on. Let’s take a look at my code according to this idea.

  tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
  spanCtx , span := tracer.Start(l.ctx, "send_msg_mq", oteltrace.WithSpanKind(oteltrace.SpanKindProducer))
  carrier := &amp;propagation.HeaderCarrier{}
  otel.GetTextMapPropagator().Inject(spanCtx,carrier)

  producer := rabbit.NewRabbitmqPublisher(RabbitmqDNS)
  msg := &amp;MsgBody{
    Carrier: carrier,
    Msg:     req.Mobile,
  }
  b , err := json.Marshal(msg)
  if err != nil{
    panic(err)
  }

  if err := producer.Publish(spanCtx, ExchangeName, RoutineKeys, b); err != nil {
    logx.Errorf("Publish Fail, msg :%s, err:%v", msg, err)
  }
  span.End()

First get the global tracerthen turn on a producer of span,and local In the same way, we turn on producer of span time also passes ctx Get to the parent of the previous level spanso that the producer of span and Login form father and son span call relation, then we want to producer of span with mq consumer middle span How to form call parent-child relationship?We will api.producer of spanCtx injected into carrier In, here we pass mq’s body Will carrier send to consumersending complete our stop our producer,So producer This link is complete.

then we’ll see mq-consumer received body What to do after the news.

type MsgBody struct {
  Carrier *propagation.HeaderCarrier
  Msg     string
}

func (c *consumer) Consumer(ctx context.Context, data []byte) error {
  var msg MsgBody
  if err := json.Unmarshal(data, &amp;msg); err != nil {
    logx.Errorf(" consumer err : %v", err)
  } else {
    logx.Infof("consumerOne Consumer  , msg:%+v", msg)

    wireContext := otel.GetTextMapPropagator().Extract(ctx, msg.Carrier)
    tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
    _, span := tracer.Start(wireContext, "mq_consumer_msg", oteltrace.WithSpanKind(oteltrace.SpanKindConsumer))

    defer span.End()
  }

  return nil
}

consumer After receiving the message, deserialize it out Carrier *propagation.HeaderCarrierthen pass otel.GetTextMapPropagator().Extract take out api.producer injected wireContextafter passing tracer.Start,wireContext create consumer of span,so consumer that is api.producer son spanthe call link relationship is formed, and finally the relationship we get is

api.Login -> rpc.GetUserByMobile”/></p><p>Let’s call <code>Logic</code> Method, look at the link in jaeger, if it is consistent with our expected link, so happy~</p><p><img decoding=

project address

go-zero microservice framework: https://github.com/zeromicro/go-zero

https://gitee.com/kevwan/go-zero

go-zero microservice best practice project: https://github.com/Mikaelemmmm/go-zero-looklook

welcome go-zero and star Support us!

WeChat exchange group

focus on”Microservice Practice』Official account and click Exchange group Obtain the QR code of the community group.

#Fun #link #tracking #kevwans #personal #space #News Fast Delivery

Leave a Comment

Your email address will not be published. Required fields are marked *