Zipkin : Golang 微服务全链路监控(二)

news2025/2/26 3:42:20

Zipkin : Golang 微服务全链路监控(二)

Golang 微服务全链路监控实现

  1. broker-service -> auth-service -> postgres db
  2. zipkin 监控:需代码入侵

一、auth-service

  1. 通过 context 传递 span
    main.go
package main

import (
	"broker-service/auth-service"
	"broker-service/auth-service/data"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/opentracing/opentracing-go"
	zipkinot "github.com/openzipkin-contrib/zipkin-go-opentracing"
	"github.com/openzipkin/zipkin-go"
	zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"

	_ "github.com/jackc/pgconn"
	_ "github.com/jackc/pgx/v4"
	_ "github.com/jackc/pgx/v4/stdlib"
)

const (
	// Our service name.
	serviceName = "auth"

	// Host + port of our service.
	hostPort = "localhost:8090"

	// Endpoint to send Zipkin spans to.
	zipkinHTTPEndpoint = "http://localhost:9411/api/v2/spans"
)

var counts int

//auth
func main() {
	// set up a span reporter
	reporter := zipkinhttp.NewReporter(zipkinHTTPEndpoint)
	defer reporter.Close()

	// create our local service endpoint
	endpoint, err := zipkin.NewEndpoint(serviceName, hostPort)
	if err != nil {
		log.Fatalf("unable to create local endpoint: %+v\n", err)
	}

	// initialize our tracer
	nativeTracer, err := zipkin.NewTracer(reporter, zipkin.WithLocalEndpoint(endpoint))
	if err != nil {
		log.Fatalf("unable to create tracer: %+v\n", err)
	}

	// use zipkin-go-opentracing to wrap our tracer
	tracer := zipkinot.Wrap(nativeTracer)

	// optionally set as Global OpenTracing tracer instance
	opentracing.SetGlobalTracer(tracer)

	//connect to DB
	conn := connectToDB()
	if conn == nil {
		log.Panic("Can't connect to Postgres!")
	}

	// create the service implementation
	service := auth.NewService(conn, data.New(conn))

	// create the HTTP Server Handler for the service
	handler := auth.NewHTTPHandler(tracer, service)

	// start the service
	fmt.Printf("Starting %s on %s\n", serviceName, hostPort)
	http.ListenAndServe(hostPort, handler)
}

func openDB(dsn string) (*sql.DB, error) {
	db, err := sql.Open("pgx", dsn)
	if err != nil {
		return nil, err
	}

	err = db.Ping()
	if err != nil {
		return nil, err
	}

	return db, nil
}

func connectToDB() *sql.DB {
	dsn := os.Getenv("DSN")
	if len(dsn) == 0 {
		dsn = "host=localhost port=5432 user=postgres password=password dbname=users sslmode=disable timezone=Asia/Shanghai connect_timeout=5"
	}

	for {
		connection, err := openDB(dsn)
		if err != nil {
			log.Println(dsn)
			log.Println("postgres is not ready...")
			time.Sleep(2 * time.Second)
			counts++
		} else {
			log.Println("connected to postgres")
			return connection
		}

		if counts > 100 {
			log.Panic(err)
		}
	}
}

  1. 路由到 auth 服务
    httpService.go
package auth

import (
	"log"
	"net/http"

	opentracing "github.com/opentracing/opentracing-go"

	"broker-service/middleware"
)

type httpService struct {
	service Service
}

type RequestPayload struct {
	Action string        `json:"action"`
	Auth   AuthPayload   `json:"auth,omitempty"`
	Log    loggerPayload `json:"log,omitempty"`
}

type AuthPayload struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

type loggerPayload struct {
	Name string `json:"name"`
	Data string `json:"data"`
}

// sumHandler is our HTTP Handlerfunc for a Sum request.
func (s *httpService) authHandler(w http.ResponseWriter, req *http.Request) {
	var requestPayload AuthPayload

	err := s.readJSON(w, req, &requestPayload)
	if err != nil {
		s.errorJSON(w, err)
		return
	}
	log.Println("requestPayload:", requestPayload)
	// call our Sum binding
	result, err := s.service.Auth(req.Context(), requestPayload)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	// return the result
	s.writeJSON(w, http.StatusAccepted, result)
}

// NewHTTPHandler returns a new HTTP handler our svc2.
func NewHTTPHandler(tracer opentracing.Tracer, service Service) http.Handler {
	// Create our HTTP Service.
	svc := &httpService{service: service}

	// Create the mux.
	mux := http.NewServeMux()

	// Create the Sum handler.
	var authHandler http.Handler
	authHandler = http.HandlerFunc(svc.authHandler)

	// Wrap the Sum handler with our tracing middleware.
	authHandler = middleware.FromHTTPRequest(tracer, "Auth")(authHandler)

	// Wire up the mux.
	mux.Handle("/auth/", authHandler)

	// Return the mux.
	return mux
}

service.go

package auth

import (
	"context"
	"errors"
)

// Service errors
var (
	ErrIntOverflow = errors.New("integer overflow occurred")
)

// Service interface to our svc2 service.
type Service interface {
	Auth(ctx context.Context, a AuthPayload) (jsonResponse, error)
}

二、middleware.go

自定义 middleware.go,context 传递 Http 请求

// Package middleware provides some usable transport middleware to deal with
// propagating Zipkin traces across service boundaries.
package middleware

import (
	"fmt"
	"net"
	"net/http"
	"strconv"

	opentracing "github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
)

// RequestFunc is a middleware function for outgoing HTTP requests.
type RequestFunc func(req *http.Request) *http.Request

// ToHTTPRequest returns a RequestFunc that injects an OpenTracing Span found in
// context into the HTTP Headers. If no such Span can be found, the RequestFunc
// is a noop.
func ToHTTPRequest(tracer opentracing.Tracer) RequestFunc {
	return func(req *http.Request) *http.Request {
		// Retrieve the Span from context.
		if span := opentracing.SpanFromContext(req.Context()); span != nil {

			// We are going to use this span in a client request, so mark as such.
			ext.SpanKindRPCClient.Set(span)

			// Add some standard OpenTracing tags, useful in an HTTP request.
			ext.HTTPMethod.Set(span, req.Method)
			span.SetTag("http.host", req.URL.Host)
			span.SetTag("http.path", req.URL.Path)
			ext.HTTPUrl.Set(
				span,
				fmt.Sprintf("%s://%s%s", req.URL.Scheme, req.URL.Host, req.URL.Path),
			)

			// Add information on the peer service we're about to contact.
			if host, portString, err := net.SplitHostPort(req.URL.Host); err == nil {
				ext.PeerHostname.Set(span, host)
				if port, err := strconv.Atoi(portString); err != nil {
					ext.PeerPort.Set(span, uint16(port))
				}
			} else {
				ext.PeerHostname.Set(span, req.URL.Host)
			}

			// Inject the Span context into the outgoing HTTP Request.
			if err := tracer.Inject(
				span.Context(),
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			); err != nil {
				fmt.Printf("error encountered while trying to inject span: %+v\n", err)
			}
		}
		return req
	}
}

// HandlerFunc is a middleware function for incoming HTTP requests.
type HandlerFunc func(next http.Handler) http.Handler

// FromHTTPRequest returns a Middleware HandlerFunc that tries to join with an
// OpenTracing trace found in the HTTP request headers and starts a new Span
// called `operationName`. If no trace could be found in the HTTP request
// headers, the Span will be a trace root. The Span is incorporated in the
// HTTP Context object and can be retrieved with
// opentracing.SpanFromContext(ctx).
func FromHTTPRequest(tracer opentracing.Tracer, operationName string,
) HandlerFunc {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			// Try to join to a trace propagated in `req`.
			wireContext, err := tracer.Extract(
				opentracing.TextMap,
				opentracing.HTTPHeadersCarrier(req.Header),
			)
			if err != nil {
				fmt.Printf("error encountered while trying to extract span: %+v\n", err)
			}

			// create span
			span := tracer.StartSpan(operationName, ext.RPCServerOption(wireContext))
			defer span.Finish()

			// store span in context
			ctx := opentracing.ContextWithSpan(req.Context(), span)

			// update request context to include our new span
			req = req.WithContext(ctx)

			// next middleware or actual request handler
			next.ServeHTTP(w, req)
		})
	}
}

三、implementation.go

处理实现验证服务

package auth

import (
	"broker-service/auth-service/data"
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/opentracing/opentracing-go"
)

// Auth is our actual service implementation.
type auth struct {
	DB     *sql.DB
	Models data.Models
}

// NewService returns a new implementation of our Service.
func NewService(db *sql.DB, models data.Models) Service {
	return &auth{
		DB:     db,
		Models: models,
	}
}

// Auth implements our Service interface.
func (auth *auth) Auth(ctx context.Context, a AuthPayload) (jsonResponse, error) {
	var jsonResp jsonResponse
	jsonResp.Error = true
	jsonResp.Message = "Auth fialed"

	// Pull span from context.
	span := opentracing.SpanFromContext(ctx)

	// Example binary annotations.
	span.SetTag("service", "auth")
	span.SetTag("AuthPayload", a)

	user, err := auth.Models.User.GetByEmail(span, a.Email)
	if err != nil {
		log.Println("get user failed from db: ", err)
		span.SetTag("error", err.Error())
		return jsonResp, err
	}
	log.Println("user:", user)

	valid, err := user.PasswordMatches(a.Password)
	if err != nil || !valid {
		log.Println("invalid user: ", err)
		span.SetTag("error", err.Error())
		return jsonResp, err
	}

	jsonResp = jsonResponse{
		Error:   false,
		Message: fmt.Sprintf("Logged in user %s", user.Email),
		Data:    user,
	}

	log.Println("auth response: ", jsonResp)

	return jsonResp, nil
}

四、httpclinet.go

通过 client 向服务发送验证请求(由 broker-service 调用

package auth

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"broker-service/middleware"

	opentracing "github.com/opentracing/opentracing-go"
)

// client is our actual client implementation
type client struct {
	baseURL      string
	httpClient   *http.Client
	tracer       opentracing.Tracer
	traceRequest middleware.RequestFunc
}

// Auth implements our Service interface.
func (c *client) Auth(ctx context.Context, a AuthPayload) (data jsonResponse, err error) {
	// create new span using span found in context as parent (if none is found,
	// our span becomes the trace root).
	span, ctx := opentracing.StartSpanFromContext(ctx, "Sum")
	defer span.Finish()

	log.Println("auth: ", a)
	jsonData, _ := json.Marshal(a)
	url := fmt.Sprintf("%s/auth/", c.baseURL)

	var payload jsonResponse
	payload.Error = true
	payload.Message = "Authenticatioin failed!"

	// create the HTTP request
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		span.SetTag("error", err.Error())
		return payload, err
	}

	// use our middleware to propagate our trace
	req = c.traceRequest(req.WithContext(ctx))

	// execute the HTTP request
	resp, err := c.httpClient.Do(req)
	if err != nil {
		// annotate our span with the error condition
		span.SetTag("error", err.Error())
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusAccepted {
		err = json.NewDecoder(resp.Body).Decode(&data)
		log.Println("result: ", data)
		if err != nil {
			span.SetTag("error", err.Error())
			return
		}
		if data.Error {
			span.SetTag("error", data.Error)
			return
		}
		return data, nil
	}
	return
}

// NewHTTPClient returns a new client instance to our auth using the HTTP
// transport.
func NewHTTPClient(tracer opentracing.Tracer, baseURL string) Service {
	return &client{
		baseURL:      baseURL,
		httpClient:   &http.Client{},
		tracer:       tracer,
		traceRequest: middleware.ToHTTPRequest(tracer),
	}
}

五、结果显示

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSDN 编辑器 Marddown 语法备忘

原文链接:https://blog.csdn.net/blogdevteam/article/details/103478461 本文对其二次加工,增加渲染样式、补充例程、添加未收录的常用语法。 CSDN Markdown 编辑器遵循 CommonMark spec 语法规范。 快捷键 撤销:Ctrl/Command Z 重做&…

成都链安受邀参加第五届CCF中国区块链技术大会

2月10-12日,由中国计算机学会主办的,2023年国内首场大型区块链学术会议—第五届CCF中国区块链技术大会在无锡市成功举办,成都链安作为区块链安全头部企业受邀参加此次大会。大会上,成都链安创始人&CTO郭文生教授与锡东新城商务…

微信小程序开发(五)小程序代码组成2

微信小程序开发(五)小程序代码组成2 为了进一步加深我们对小程序基础知识的了解和掌握,需要更进一步的了解小程序的代码组成以及一些简单的代码的编写。 参考小程序官方的的代码组成文档:https://developers.weixin.qq.com/ebook?…

海量并发低延时 RTC-CDN 系统架构设计(上)

随着近几年音视频流媒体行业的持续发展,海量并发、低延时和低成本作为三大核心诉求依旧需要不断深挖,同时随着 RTC 和 CDN 这两种技术的界线越来越模糊,因此有必要从底层架构层面重新思考 RTC 与 CDN 的融合之道。本文将重点分享:…

通过 Colab 下载 Google Driver 上的大文件到内网服务器

这里写自定义目录标题1. 将 Google Driver 上的大文件下载到 Colab1.1 获取文件唯一的 fileID1.2 查看文件的 MD5 值1.3 获取 API 的 Access Token1.4 下载文件到 Colab2. 将文件从 Colab 下载到公网服务器3. 将文件从公网服务器下载到内网服务器4. 参考链接由于众所周知的原因…

【正点原子FPGA连载】第十章PS SYSMON测量温度电压实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第十章PS SYSMON…

如何正确的在Flutter中添加webview

前言 为什么要在flutter中引入webview?这不是废话么,当然是为了加载一个网页,这不是移动端最基本的需求么,哈哈!说的真不错,接下来我要是告诉你我的用法,你可能要大吃一惊。我的用处很简单&…

【音视频处理】码率、帧率越高越清晰?分辨率、像素、dpi之间是什么关系?码率的真实作用,I帧、B帧、P帧是什么

大家好,欢迎来到停止重构的频道。本期我们介绍一下视频的一些基础概念,如帧率、码率、分辨率、像素、dpi、视频帧、I帧、P帧、gop等。会解释多少码率是清晰的,是否帧率越高越流畅等问题。这些概念是比较杂乱的,我们按这样的顺序介…

Seata源码学习(四)-数据源代理

Seata源码分析-数据源代理 上节课我们分析了整体的Seata-AT模式的2PC执行流程,那么这节课我们要分析的就是在AT模式中的关键点,数据源代理 AT模式的核心点: 获取全局锁、开启全局事务解析SQL并写入undolog 那么上节课其实我们已经把第一步…

【目标检测】K-means和K-means++计算anchors结果比较(附完整代码,全网最详细的手把手教程)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大努力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 一、介绍 YOLO系列目标检测算法中基于anchor的模型还是比较多的,例如YOLOv3、YOLOv4、YOLOv5等,我们可以随机初始化a…

简约精美电商小程序【源码好优多】

简介 一款开源的电商系统,包含微信小程序和H5端,为大中小企业提供移动电子商务优秀的解决方案。 后台采用Thinkphp5.1框架开发,执行效率、扩展性、稳定性值得信赖。并且Jshop小程序商城上手难度低,可大量节省定制化开发周期。 功…

设计模式(适配器模式)

设计模式(适配器模式) 第二章 设计模式之适配器模式(Adapter) 一、Adapter模式介绍 适配器模式位于实际情况和需求之间,填补两者之间的差距。 二、示例程序1(使用继承的适配器) 1.示例程序示…

操作系统(五):编译过程,静态链接,目标文件,动态链接

文章目录一、程序的编译过程二、静态链接三、目标文件四、动态链接一、程序的编译过程 在linux上编译一个c文件hello.c的命令为: g -o hello hello.c 整个过程大致要经过一下四步: 预处理 : 处理以#开头的预处理命令编译 : 翻译成汇编文件汇编 &…

HTTPS基础原理和配置-3

书接上文:HTTPS 基础原理和配置 - 2,接下来介绍: 配置 NGINX后端 HTTPS检查配置配置 HSTSOCSP Stapling 重要部分来了。如何使用这些选项并配置NGINX? 一、NGINX 的 HTTPS 配置 这里有一些基本的原语(或叫做指令)…

GEE学习笔记八十八:在自己的APP中使用绘制矢量(上)

在GEE中尤其是自己的APP中调用绘制的矢量图形方法之前没有合适的方法,但是现在可以通过ui.Map.DrawingTools(...)以及ui.Map.GeometryLayer(...)结合来做。具体的API如下图: 在这一篇中我先通过一个简单的例子来展示一下使用这些API后可以实现什么效果&a…

C#底层库--日期扩展类(上周、本周、明年、前年等)

系列文章 C#底层库–记录日志帮助类 本文链接:https://blog.csdn.net/youcheng_ge/article/details/124187709 C#底层库–数据库访问帮助类(MySQL版) 本文链接:https://blog.csdn.net/youcheng_ge/article/details/126886379 …

axios中的GET POST PUT PATCH,发送请求时params和data的区别

axios 中 get/post请求方式 1. 前言 最近突然发现post请求可以使用params方式传值,然后想总结一下其中的用法。 2.1 分类 经过查阅资料,get请求是可以通过body传输数据的,但是许多工具类并不支持此功能。 在postman中,选择get请…

以before为例 完成一个aop代理强化方法案例

观看本文 首先 您需要做好Spring aop的准备工作 具体可以参考我的文章 java Spring aop入门准备工作 首先 我们创建一个包 我这里叫 Aop 然后在Aop包下创建一个类 叫 User 参考代码如下 package Aop;public class User {public void add(){System.out.println("add....…

android kotlin 协程(三) 理解挂起,恢复以及job

android kotlin 协程(三) 理解挂起,恢复以及job 前言: 通过上两篇的基础入门,相信大家对协程api已经有了一个基本的影响,本篇开始尝试理解挂起于恢复. 本篇不涉及源码, 通过很多应用案例,来理解挂起于恢复! 协程执行流程理解 还是老套路,先来看原始效果 参考图1 在这里我们知…

电子技术——目录

电子技术——目录 第一章——信号与放大器 第二章——运算放大器 第三章——半导体器件 第四章——二极管 第五章——金属氧化物场效应晶体管 目录: MOS管的物理结构MOS管的CV特性MOS放大器基础MOS管的小信号模型基本MOS放大器配置MOS放大器的DC偏置分立MOS放…