亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

GO 調用鏈使用

2025-10-09 10:05:46
5
0

Go語言中的調用鏈實現

1. 核心概念

  • Span: 代表調用鏈中的一個操作單元

  • Trace: 一次完整的請求跟蹤,包含多個Span

  • Context: 在服務間傳遞跟蹤信息

2. 常用庫介紹

OpenTelemetry

OpenTelemetry是目前最主流的調用鏈解決方案:

package main

import (
    "context"
    "fmt"
    "log"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    "go.opentelemetry.io/otel/trace"
)

// 初始化Tracer Provider
func initTracer(url string) (*sdktrace.TracerProvider, error) {
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
    if err != nil {
        return nil, err
    }
    
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceNameKey.String("user-service"),
            attribute.String("environment", "production"),
        )),
    )
    
    otel.SetTracerProvider(tp)
    return tp, nil
}

// 業務函數示例
func getUserInfo(ctx context.Context, userID string) (string, error) {
    tracer := otel.Tracer("user-service")
    
    // 創建新的span
    ctx, span := tracer.Start(ctx, "getUserInfo", 
        trace.WithAttributes(attribute.String("user.id", userID)))
    defer span.End()
    
    // 模擬業務邏輯
    if userID == "123" {
        span.SetAttributes(attribute.Bool("user.found", true))
        return "John Doe", nil
    }
    
    span.SetAttributes(attribute.Bool("user.found", false))
    return "", fmt.Errorf("user not found")
}

// HTTP處理函數
func userHandler(w http.ResponseWriter, r *http.Request) {
    tracer := otel.Tracer("user-service")
    
    ctx, span := tracer.Start(r.Context(), "userHandler")
    defer span.End()
    
    userID := r.URL.Query().Get("id")
    userName, err := getUserInfo(ctx, userID)
    
    if err != nil {
        span.RecordError(err)
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }
    
    fmt.Fprintf(w, "User: %s", userName)
}

自定義簡易調用鏈實現

對于小型項目,可以基于context實現簡單的調用鏈:

package trace

import (
    "context"
    "encoding/json"
    "fmt"
    "time"
)

type Span struct {
    TraceID    string                 `json:"trace_id"`
    SpanID     string                 `json:"span_id"`
    ParentID   string                 `json:"parent_id,omitempty"`
    Name       string                 `json:"name"`
    StartTime  time.Time              `json:"start_time"`
    EndTime    time.Time              `json:"end_time,omitempty"`
    Attributes map[string]interface{} `json:"attributes,omitempty"`
    Error      error                  `json:"error,omitempty"`
}

type Trace struct {
    Spans []*Span `json:"spans"`
}

type traceKey struct{}

// 開始新的Span
func StartSpan(ctx context.Context, name string) (context.Context, *Span) {
    span := &Span{
        SpanID:     generateID(),
        Name:       name,
        StartTime:  time.Now(),
        Attributes: make(map[string]interface{}),
    }
    
    // 從父context獲取trace信息
    if parentSpan, ok := ctx.Value(traceKey{}).(*Span); ok {
        span.TraceID = parentSpan.TraceID
        span.ParentID = parentSpan.SpanID
    } else {
        span.TraceID = generateID()
    }
    
    // 設置屬性
    span.SetAttribute("service.name", "user-service")
    
    return context.WithValue(ctx, traceKey{}, span), span
}

// 結束Span
func (s *Span) End() {
    s.EndTime = time.Now()
}

// 設置屬性
func (s *Span) SetAttribute(key string, value interface{}) {
    s.Attributes[key] = value
}

// 記錄錯誤
func (s *Span) RecordError(err error) {
    s.Error = err
    s.SetAttribute("error", true)
    s.SetAttribute("error.message", err.Error())
}

// 從context獲取當前Span
func SpanFromContext(ctx context.Context) *Span {
    if span, ok := ctx.Value(traceKey{}).(*Span); ok {
        return span
    }
    return nil
}

3. 中間件集成

HTTP服務器中間件

func TracingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tracer := otel.Tracer("http-server")
        
        // 從header中提取trace context
        ctx := otel.GetTextMapPropagator().Extract(r.Context(), 
            propagation.HeaderCarrier(r.Header))
        
        // 開始新的span
        ctx, span := tracer.Start(ctx, "http.request",
            trace.WithAttributes(
                attribute.String("http.method", r.Method),
                attribute.String("http.url", r.URL.String()),
                attribute.String("http.user_agent", r.UserAgent()),
            ))
        defer span.End()
        
        // 記錄響應狀態碼
        rw := &responseWriter{ResponseWriter: w, statusCode: 200}
        
        // 處理請求
        next.ServeHTTP(rw, r.WithContext(ctx))
        
        span.SetAttributes(attribute.Int("http.status_code", rw.statusCode))
    })
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}

數據庫調用追蹤

type tracedDB struct {
    db     *sql.DB
    tracer trace.Tracer
}

func (t *tracedDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
    ctx, span := t.tracer.Start(ctx, "db.query",
        trace.WithAttributes(
            attribute.String("db.statement", query),
            attribute.String("db.system", "mysql"),
        ))
    defer span.End()
    
    start := time.Now()
    rows, err := t.db.QueryContext(ctx, query, args...)
    duration := time.Since(start)
    
    span.SetAttributes(attribute.String("db.duration", duration.String()))
    if err != nil {
        span.RecordError(err)
    }
    
    return rows, err
}

4. 配置和部署

Docker Compose部署Jaeger

version: '3.8'
services:
  jaeger:
    image: jaegertracing/all-in-one:1.35
    ports:
      - "16686:16686"  # UI
      - "14268:14268"  # Collector
    environment:
      - COLLECTOR_OTLP_ENABLED=true

主程序配置

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
    
    "go.opentelemetry.io/otel"
)

func main() {
    // 初始化調用鏈
    tp, err := initTracer("//localhost:14268/api/traces")
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err := tp.Shutdown(context.Background()); err != nil {
            log.Printf("Error shutting down tracer provider: %v", err)
        }
    }()
    
    // 設置路由
    mux := http.NewServeMux()
    mux.HandleFunc("/user", userHandler)
    
    // 添加中間件
    handler := TracingMiddleware(mux)
    
    server := &http.Server{
        Addr:    ":8080",
        Handler: handler,
    }
    
    // 啟動服務器
    go func() {
        log.Println("Starting server on :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()
    
    // 優雅關閉
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("Server shutdown error: %v", err)
    }
}

 

0條評論
作者已關閉評論
c****k
8文章數
0粉絲數
c****k
8 文章 | 0 粉絲
原創

GO 調用鏈使用

2025-10-09 10:05:46
5
0

Go語言中的調用鏈實現

1. 核心概念

  • Span: 代表調用鏈中的一個操作單元

  • Trace: 一次完整的請求跟蹤,包含多個Span

  • Context: 在服務間傳遞跟蹤信息

2. 常用庫介紹

OpenTelemetry

OpenTelemetry是目前最主流的調用鏈解決方案:

package main

import (
    "context"
    "fmt"
    "log"
    
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    "go.opentelemetry.io/otel/trace"
)

// 初始化Tracer Provider
func initTracer(url string) (*sdktrace.TracerProvider, error) {
    exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
    if err != nil {
        return nil, err
    }
    
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceNameKey.String("user-service"),
            attribute.String("environment", "production"),
        )),
    )
    
    otel.SetTracerProvider(tp)
    return tp, nil
}

// 業務函數示例
func getUserInfo(ctx context.Context, userID string) (string, error) {
    tracer := otel.Tracer("user-service")
    
    // 創建新的span
    ctx, span := tracer.Start(ctx, "getUserInfo", 
        trace.WithAttributes(attribute.String("user.id", userID)))
    defer span.End()
    
    // 模擬業務邏輯
    if userID == "123" {
        span.SetAttributes(attribute.Bool("user.found", true))
        return "John Doe", nil
    }
    
    span.SetAttributes(attribute.Bool("user.found", false))
    return "", fmt.Errorf("user not found")
}

// HTTP處理函數
func userHandler(w http.ResponseWriter, r *http.Request) {
    tracer := otel.Tracer("user-service")
    
    ctx, span := tracer.Start(r.Context(), "userHandler")
    defer span.End()
    
    userID := r.URL.Query().Get("id")
    userName, err := getUserInfo(ctx, userID)
    
    if err != nil {
        span.RecordError(err)
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }
    
    fmt.Fprintf(w, "User: %s", userName)
}

自定義簡易調用鏈實現

對于小型項目,可以基于context實現簡單的調用鏈:

package trace

import (
    "context"
    "encoding/json"
    "fmt"
    "time"
)

type Span struct {
    TraceID    string                 `json:"trace_id"`
    SpanID     string                 `json:"span_id"`
    ParentID   string                 `json:"parent_id,omitempty"`
    Name       string                 `json:"name"`
    StartTime  time.Time              `json:"start_time"`
    EndTime    time.Time              `json:"end_time,omitempty"`
    Attributes map[string]interface{} `json:"attributes,omitempty"`
    Error      error                  `json:"error,omitempty"`
}

type Trace struct {
    Spans []*Span `json:"spans"`
}

type traceKey struct{}

// 開始新的Span
func StartSpan(ctx context.Context, name string) (context.Context, *Span) {
    span := &Span{
        SpanID:     generateID(),
        Name:       name,
        StartTime:  time.Now(),
        Attributes: make(map[string]interface{}),
    }
    
    // 從父context獲取trace信息
    if parentSpan, ok := ctx.Value(traceKey{}).(*Span); ok {
        span.TraceID = parentSpan.TraceID
        span.ParentID = parentSpan.SpanID
    } else {
        span.TraceID = generateID()
    }
    
    // 設置屬性
    span.SetAttribute("service.name", "user-service")
    
    return context.WithValue(ctx, traceKey{}, span), span
}

// 結束Span
func (s *Span) End() {
    s.EndTime = time.Now()
}

// 設置屬性
func (s *Span) SetAttribute(key string, value interface{}) {
    s.Attributes[key] = value
}

// 記錄錯誤
func (s *Span) RecordError(err error) {
    s.Error = err
    s.SetAttribute("error", true)
    s.SetAttribute("error.message", err.Error())
}

// 從context獲取當前Span
func SpanFromContext(ctx context.Context) *Span {
    if span, ok := ctx.Value(traceKey{}).(*Span); ok {
        return span
    }
    return nil
}

3. 中間件集成

HTTP服務器中間件

func TracingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tracer := otel.Tracer("http-server")
        
        // 從header中提取trace context
        ctx := otel.GetTextMapPropagator().Extract(r.Context(), 
            propagation.HeaderCarrier(r.Header))
        
        // 開始新的span
        ctx, span := tracer.Start(ctx, "http.request",
            trace.WithAttributes(
                attribute.String("http.method", r.Method),
                attribute.String("http.url", r.URL.String()),
                attribute.String("http.user_agent", r.UserAgent()),
            ))
        defer span.End()
        
        // 記錄響應狀態碼
        rw := &responseWriter{ResponseWriter: w, statusCode: 200}
        
        // 處理請求
        next.ServeHTTP(rw, r.WithContext(ctx))
        
        span.SetAttributes(attribute.Int("http.status_code", rw.statusCode))
    })
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}

數據庫調用追蹤

type tracedDB struct {
    db     *sql.DB
    tracer trace.Tracer
}

func (t *tracedDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
    ctx, span := t.tracer.Start(ctx, "db.query",
        trace.WithAttributes(
            attribute.String("db.statement", query),
            attribute.String("db.system", "mysql"),
        ))
    defer span.End()
    
    start := time.Now()
    rows, err := t.db.QueryContext(ctx, query, args...)
    duration := time.Since(start)
    
    span.SetAttributes(attribute.String("db.duration", duration.String()))
    if err != nil {
        span.RecordError(err)
    }
    
    return rows, err
}

4. 配置和部署

Docker Compose部署Jaeger

version: '3.8'
services:
  jaeger:
    image: jaegertracing/all-in-one:1.35
    ports:
      - "16686:16686"  # UI
      - "14268:14268"  # Collector
    environment:
      - COLLECTOR_OTLP_ENABLED=true

主程序配置

package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
    
    "go.opentelemetry.io/otel"
)

func main() {
    // 初始化調用鏈
    tp, err := initTracer("//localhost:14268/api/traces")
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err := tp.Shutdown(context.Background()); err != nil {
            log.Printf("Error shutting down tracer provider: %v", err)
        }
    }()
    
    // 設置路由
    mux := http.NewServeMux()
    mux.HandleFunc("/user", userHandler)
    
    // 添加中間件
    handler := TracingMiddleware(mux)
    
    server := &http.Server{
        Addr:    ":8080",
        Handler: handler,
    }
    
    // 啟動服務器
    go func() {
        log.Println("Starting server on :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()
    
    // 優雅關閉
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("Server shutdown error: %v", err)
    }
}

 

文章來自個人專欄
文章 | 訂閱
0條評論
作者已關閉評論
作者已關閉評論
0
0