40分钟学 Go 语言高并发:微服务架构设计

微服务架构设计

一、知识要点总览

核心模块 重要性 掌握程度
服务拆分 深入理解DDD领域驱动设计
接口设计 掌握RESTful和gRPC设计规范
服务治理 理解服务注册、发现、熔断、限流等机制
部署策略 掌握Docker+K8s容器化部署方案

二、详细内容讲解

1. 服务拆分

服务拆分是微服务架构设计的第一步,我们通常采用DDD(Domain-Driven Design)领域驱动设计的方法进行服务边界划分。

1.1 拆分原则
  • 单一职责原则
  • 高内聚低耦合
  • 服务粒度适中
  • 业务边界清晰

让我们通过一个电商系统的服务拆分示例来理解:

2. 接口设计

我们将通过一个具体的商品服务示例来展示Go微服务的接口设计。

go 复制代码
// product.proto
syntax = "proto3";
package product;
option go_package = "./product";

service ProductService {
    rpc CreateProduct (CreateProductRequest) returns (CreateProductResponse);
    rpc GetProduct (GetProductRequest) returns (GetProductResponse);
    rpc UpdateProduct (UpdateProductRequest) returns (UpdateProductResponse);
    rpc ListProducts (ListProductsRequest) returns (ListProductsResponse);
}

message Product {
    string id = 1;
    string name = 2;
    string description = 3;
    double price = 4;
    int32 stock = 5;
    string category = 6;
    repeated string images = 7;
}

message CreateProductRequest {
    Product product = 1;
}

message CreateProductResponse {
    Product product = 1;
}

message GetProductRequest {
    string id = 1;
}

message GetProductResponse {
    Product product = 1;
}

message UpdateProductRequest {
    Product product = 1;
}

message UpdateProductResponse {
    Product product = 1;
}

message ListProductsRequest {
    int32 page = 1;
    int32 page_size = 2;
    string category = 3;
}

message ListProductsResponse {
    repeated Product products = 1;
    int32 total = 2;
}

// product_service.go
package service

import (
    "context"
    "database/sql"
    pb "yourproject/product"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

type ProductService struct {
    pb.UnimplementedProductServiceServer
    db *sql.DB
}

func NewProductService(db *sql.DB) *ProductService {
    return &ProductService{db: db}
}

func (s *ProductService) CreateProduct(ctx context.Context, req *pb.CreateProductRequest) (*pb.CreateProductResponse, error) {
    if req.Product == nil {
        return nil, status.Error(codes.InvalidArgument, "product is required")
    }
    
    // 开启事务
    tx, err := s.db.BeginTx(ctx, nil)
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to begin transaction")
    }
    defer tx.Rollback()
    
    // 插入商品信息
    query := `
        INSERT INTO products (name, description, price, stock, category)
        VALUES ($1, $2, $3, $4, $5)
        RETURNING id`
        
    var id string
    err = tx.QueryRowContext(ctx, query,
        req.Product.Name,
        req.Product.Description,
        req.Product.Price,
        req.Product.Stock,
        req.Product.Category,
    ).Scan(&id)
    
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to create product")
    }
    
    // 提交事务
    if err := tx.Commit(); err != nil {
        return nil, status.Error(codes.Internal, "failed to commit transaction")
    }
    
    req.Product.Id = id
    return &pb.CreateProductResponse{
        Product: req.Product,
    }, nil
}

func (s *ProductService) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.GetProductResponse, error) {
    query := `
        SELECT id, name, description, price, stock, category
        FROM products
        WHERE id = $1`
        
    product := &pb.Product{}
    err := s.db.QueryRowContext(ctx, query, req.Id).Scan(
        &product.Id,
        &product.Name,
        &product.Description,
        &product.Price,
        &product.Stock,
        &product.Category,
    )
    
    if err == sql.ErrNoRows {
        return nil, status.Error(codes.NotFound, "product not found")
    }
    if err != nil {
        return nil, status.Error(codes.Internal, "failed to get product")
    }
    
    return &pb.GetProductResponse{
        Product: product,
    }, nil
}

3. 服务治理

服务治理是确保微服务架构可靠运行的关键。让我们实现一个包含服务注册、发现和熔断器的完整示例。

go 复制代码
// service_registry.go
package registry

import (
    "context"
    "sync"
    "time"
    "github.com/go-kit/kit/sd/etcdv3"
    "go.etcd.io/etcd/client/v3"
)

type ServiceRegistry struct {
    client     *clientv3.Client
    registrar  *etcdv3.Registrar
    mutex      sync.RWMutex
    instances  map[string]string
}

func NewServiceRegistry(endpoints []string) (*ServiceRegistry, error) {
    client, err := clientv3.New(clientv3.Config{
        Endpoints:   endpoints,
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        return nil, err
    }
    
    return &ServiceRegistry{
        client:    client,
        instances: make(map[string]string),
    }, nil
}

func (sr *ServiceRegistry) Register(ctx context.Context, serviceName, serviceAddr string) error {
    registrar := etcdv3.NewRegistrar(sr.client, etcdv3.Service{
        Key:   "/services/" + serviceName,
        Value: serviceAddr,
    }, log.NewNopLogger())
    
    sr.mutex.Lock()
    sr.registrar = registrar
    sr.instances[serviceName] = serviceAddr
    sr.mutex.Unlock()
    
    return registrar.Register()
}

func (sr *ServiceRegistry) Deregister(ctx context.Context, serviceName string) error {
    sr.mutex.Lock()
    defer sr.mutex.Unlock()
    
    if sr.registrar != nil {
        sr.registrar.Deregister()
    }
    
    delete(sr.instances, serviceName)
    return nil
}

// circuit_breaker.go
package circuitbreaker

import (
    "github.com/sony/gobreaker"
    "time"
)

type CircuitBreaker struct {
    cb *gobreaker.CircuitBreaker
}

func NewCircuitBreaker(name string) *CircuitBreaker {
    settings := gobreaker.Settings{
        Name:        name,
        MaxRequests: 3,
        Interval:    10 * time.Second,
        Timeout:     60 * time.Second,
        ReadyToTrip: func(counts gobreaker.Counts) bool {
            failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
            return counts.Requests >= 3 && failureRatio >= 0.6
        },
        OnStateChange: func(name string, from gobreaker.State, to gobreaker.State) {
            // 状态变更记录
            log.Printf("Circuit Breaker %s state change from %s to %s", name, from, to)
        },
    }
    
    return &CircuitBreaker{
        cb: gobreaker.NewCircuitBreaker(settings),
    }
}

func (c *CircuitBreaker) Execute(req func() (interface{}, error)) (interface{}, error) {
    return c.cb.Execute(req)
}

// rate_limiter.go
package ratelimit

import (
    "golang.org/x/time/rate"
    "context"
)

type RateLimiter struct {
    limiter *rate.Limiter
}

func NewRateLimiter(rps float64, burst int) *RateLimiter {
    return &RateLimiter{
        limiter: rate.NewLimiter(rate.Limit(rps), burst),
    }
}

func (r *RateLimiter) Allow() bool {
    return r.limiter.Allow()
}

func (r *RateLimiter) Wait(ctx context.Context) error {
    return r.limiter.Wait(ctx)
}

4. 部署策略

现在让我们看一个具体的部署配置示例:

yaml 复制代码
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
  namespace: microservices
spec:
  replicas: 3
  selector:
    matchLabels:
      app: product-service
  template:
    metadata:
      labels:
        app: product-service
    spec:
      containers:
      - name: product-service
        image: your-registry/product-service:v1.0.0
        ports:
        - containerPort: 50051
        resources:
          limits:
            cpu: "1"
            memory: "1Gi"
          requests:
            cpu: "500m"
            memory: "512Mi"
        readinessProbe:
          grpc:
            port: 50051
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          grpc:
            port: 50051
          initialDelaySeconds: 15
          periodSeconds: 20
        env:
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: product-service-config
              key: db_host
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: product-service-secrets
              key: db_password

---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: product-service
  namespace: microservices
spec:
  selector:
    app: product-service
  ports:
  - port: 50051
    targetPort: 50

让我们继续深入微服务架构设计的其他重要方面:

5. 服务间通信模式

在微服务架构中,服务间通信是一个核心问题,我们来看几种主要的通信模式:

让我们实现一个基于消息队列的异步通信示例:

go 复制代码
// event/event.go
package event

type OrderEvent struct {
    EventID     string    `json:"event_id"`
    EventType   string    `json:"event_type"`
    OrderID     string    `json:"order_id"`
    UserID      string    `json:"user_id"`
    ProductID   string    `json:"product_id"`
    Quantity    int       `json:"quantity"`
    TotalPrice  float64   `json:"total_price"`
    Timestamp   int64     `json:"timestamp"`
}

// kafka/producer.go
package kafka

import (
    "context"
    "encoding/json"
    "github.com/Shopify/sarama"
)

type Producer struct {
    producer sarama.SyncProducer
    topic    string
}

func NewProducer(brokers []string, topic string) (*Producer, error) {
    config := sarama.NewConfig()
    config.Producer.RequiredAcks = sarama.WaitForAll
    config.Producer.Retry.Max = 5
    config.Producer.Return.Successes = true

    producer, err := sarama.NewSyncProducer(brokers, config)
    if err != nil {
        return nil, err
    }

    return &Producer{
        producer: producer,
        topic:    topic,
    }, nil
}

func (p *Producer) PublishOrderEvent(ctx context.Context, event *OrderEvent) error {
    eventJSON, err := json.Marshal(event)
    if err != nil {
        return err
    }

    msg := &sarama.ProducerMessage{
        Topic: p.topic,
        Key:   sarama.StringEncoder(event.OrderID),
        Value: sarama.ByteEncoder(eventJSON),
    }

    _, _, err = p.producer.SendMessage(msg)
    return err
}

// kafka/consumer.go
package kafka

import (
    "context"
    "encoding/json"
    "github.com/Shopify/sarama"
    "log"
)

type Consumer struct {
    consumer sarama.ConsumerGroup
    topic    string
    handler  func(event *OrderEvent) error
}

func NewConsumer(brokers []string, groupID string, topic string, handler func(event *OrderEvent) error) (*Consumer, error) {
    config := sarama.NewConfig()
    config.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRoundRobin
    config.Consumer.Offsets.Initial = sarama.OffsetOldest

    group, err := sarama.NewConsumerGroup(brokers, groupID, config)
    if err != nil {
        return nil, err
    }

    return &Consumer{
        consumer: group,
        topic:    topic,
        handler:  handler,
    }, nil
}

func (c *Consumer) Start(ctx context.Context) error {
    topics := []string{c.topic}
    handler := &consumerGroupHandler{handler: c.handler}

    for {
        err := c.consumer.Consume(ctx, topics, handler)
        if err != nil {
            log.Printf("Error from consumer: %v", err)
        }
        if ctx.Err() != nil {
            return ctx.Err()
        }
    }
}

type consumerGroupHandler struct {
    handler func(event *OrderEvent) error
}

func (h *consumerGroupHandler) Setup(_ sarama.ConsumerGroupSession) error   { return nil }
func (h *consumerGroupHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
func (h *consumerGroupHandler) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
    for message := range claim.Messages() {
        var event OrderEvent
        if err := json.Unmarshal(message.Value, &event); err != nil {
            log.Printf("Error unmarshaling message: %v", err)
            continue
        }

        if err := h.handler(&event); err != nil {
            log.Printf("Error handling message: %v", err)
            continue
        }

        session.MarkMessage(message, "")
    }
    return nil
}

// main.go 使用示例
func main() {
    ctx := context.Background()
    brokers := []string{"localhost:9092"}
    topic := "order_events"

    // 创建生产者
    producer, err := NewProducer(brokers, topic)
    if err != nil {
        log.Fatal(err)
    }

    // 创建消费者
    consumer, err := NewConsumer(brokers, "order-processor", topic, func(event *OrderEvent) error {
        log.Printf("Processing order event: %+v", event)
        // 处理订单事件的业务逻辑
        return nil
    })
    if err != nil {
        log.Fatal(err)
    }

    // 启动消费者
    go func() {
        if err := consumer.Start(ctx); err != nil {
            log.Printf("Consumer error: %v", err)
        }
    }()

    // 发布订单事件
    event := &OrderEvent{
        EventID:    "evt_123",
        EventType:  "order_created",
        OrderID:    "ord_123",
        UserID:     "usr_123",
        ProductID:  "prod_123",
        Quantity:   2,
        TotalPrice: 199.99,
        Timestamp:  time.Now().Unix(),
    }

    if err := producer.PublishOrderEvent(ctx, event); err != nil {
        log.Printf("Error publishing event: %v", err)
    }
}

6. API 网关设计

API网关是微服务架构中的重要组件,负责请求路由、认证授权、限流等功能。让我们看一个实现示例:

go 复制代码
// gateway/main.go
package main

import (
    "context"
    "github.com/gin-gonic/gin"
    "google.golang.org/grpc"
    "log"
    "net/http"
    "time"
)

type Gateway struct {
    router         *gin.Engine
    productClient  pb.ProductServiceClient
    orderClient    pb.OrderServiceClient
    rateLimiter    *ratelimit.RateLimiter
    circuitBreaker *circuitbreaker.CircuitBreaker
}

func NewGateway() *Gateway {
    router := gin.Default()
    
    // 初始化限流器
    rateLimiter := ratelimit.NewRateLimiter(100, 20) // 每秒100个请求,突发20个
    
    // 初始化熔断器
    circuitBreaker := circuitbreaker.NewCircuitBreaker("gateway")
    
    return &Gateway{
        router:         router,
        rateLimiter:    rateLimiter,
        circuitBreaker: circuitBreaker,
    }
}

func (g *Gateway) InitRoutes() {
    // 中间件
    g.router.Use(g.authMiddleware())
    g.router.Use(g.rateLimitMiddleware())
    
    // API路由
    v1 := g.router.Group("/api/v1")
    {
        products := v1.Group("/products")
        {
            products.GET("", g.ListProducts)
            products.GET("/:id", g.GetProduct)
            products.POST("", g.CreateProduct)
            products.PUT("/:id", g.UpdateProduct)
        }
        
        orders := v1.Group("/orders")
        {
            orders.POST("", g.CreateOrder)
            orders.GET("/:id", g.GetOrder)
        }
    }
}

// 认证中间件
func (g *Gateway) authMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
            c.Abort()
            return
        }
        
        // 验证token
        claims, err := validateToken(token)
        if err != nil {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
            c.Abort()
            return
        }
        
        c.Set("user_id", claims.UserID)
        c.Next()
    }
}

// 限流中间件
func (g *Gateway) rateLimitMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        if !g.rateLimiter.Allow() {
            c.JSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"})
            c.Abort()
            return
        }
        c.Next()
    }
}

// API处理函数
func (g *Gateway) ListProducts(c *gin.Context) {
    result, err := g.circuitBreaker.Execute(func() (interface{}, error) {
        ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second)
        defer cancel()
        
        req := &pb.ListProductsRequest{
            Page:     1,
            PageSize: 10,
        }
        
        return g.productClient.ListProducts(ctx, req)
    })
    
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
        return
    }
    
    response := result.(*pb.ListProductsResponse)
    c.JSON(http.StatusOK, response)
}

func main() {
    gateway := NewGateway()
    gateway.InitRoutes()
    
    if err := gateway.router.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}

7. 监控和追踪

微服务架构中的监控和追踪也是非常重要的部分,我们来看看如何集成OpenTelemetry进行分布式追踪:

go 复制代码
// monitoring/metrics.go
package monitoring

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
    "sync"
)

type MetricsCollector struct {
    requestCounter   *prometheus.CounterVec
    requestDuration  *prometheus.HistogramVec
    activeRequests   *prometheus.GaugeVec
    errorCounter     *prometheus.CounterVec
    queueLength      *prometheus.GaugeVec
    cpuUsage         *prometheus.GaugeVec
    memoryUsage      *prometheus.GaugeVec
    mu               sync.RWMutex
}

func NewMetricsCollector(serviceName string) *MetricsCollector {
    return &MetricsCollector{
        requestCounter: promauto.NewCounterVec(
            prometheus.CounterOpts{
                Name: "requests_total",
                Help: "Total number of requests processed",
            },
            []string{"service", "method", "status"},
        ),
        requestDuration: promauto.NewHistogramVec(
            prometheus.HistogramOpts{
                Name:    "request_duration_seconds",
                Help:    "Request duration in seconds",
                Buckets: prometheus.DefBuckets,
            },
            []string{"service", "method"},
        ),
        activeRequests: promauto.NewGaugeVec(
            prometheus.GaugeOpts{
                Name: "active_requests",
                Help: "Number of requests currently being processed",
            },
            []string{"service"},
        ),
        errorCounter: promauto.NewCounterVec(
            prometheus.CounterOpts{
                Name: "errors_total",
                Help: "Total number of errors",
            },
            []string{"service", "type"},
        ),
        queueLength: promauto.NewGaugeVec(
            prometheus.GaugeOpts{
                Name: "queue_length",
                Help: "Current length of the request queue",
            },
            []string{"service", "queue_name"},
        ),
        cpuUsage: promauto.NewGaugeVec(
            prometheus.GaugeOpts{
                Name: "cpu_usage_percent",
                Help: "Current CPU usage in percentage",
            },
            []string{"service"},
        ),
        memoryUsage: promauto.NewGaugeVec(
            prometheus.GaugeOpts{
                Name: "memory_usage_bytes",
                Help: "Current memory usage in bytes",
            },
            []string{"service"},
        ),
    }
}

func (m *MetricsCollector) RecordRequest(service, method, status string) {
    m.requestCounter.WithLabelValues(service, method, status).Inc()
}

func (m *MetricsCollector) RecordDuration(service, method string, duration float64) {
    m.requestDuration.WithLabelValues(service, method).Observe(duration)
}

func (m *MetricsCollector) UpdateActiveRequests(service string, delta float64) {
    m.activeRequests.WithLabelValues(service).Add(delta)
}

func (m *MetricsCollector) RecordError(service, errorType string) {
    m.errorCounter.WithLabelValues(service, errorType).Inc()
}

func (m *MetricsCollector) UpdateQueueLength(service, queueName string, length float64) {
    m.queueLength.WithLabelValues(service, queueName).Set(length)
}

func (m *MetricsCollector) UpdateCPUUsage(service string, usage float64) {
    m.cpuUsage.WithLabelValues(service).Set(usage)
}

func (m *MetricsCollector) UpdateMemoryUsage(service string, usage float64) {
    m.memoryUsage.WithLabelValues(service).Set(usage)
}

// tracing/tracer.go
package tracing

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

type Tracer struct {
    tracer     trace.Tracer
    serviceName string
}

func NewTracer(serviceName, jaegerEndpoint string) (*Tracer, error) {
    // 创建Jaeger导出器
    exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(
        jaeger.WithEndpoint(jaegerEndpoint),
    ))
    if err != nil {
        return nil, fmt.Errorf("failed to create jaeger exporter: %w", err)
    }

    // 创建资源
    res, err := resource.New(context.Background(),
        resource.WithAttributes(
            semconv.ServiceNameKey.String(serviceName),
            attribute.String("environment", "production"),
        ),
    )
    if err != nil {
        return nil, fmt.Errorf("failed to create resource: %w", err)
    }

    // 创建TracerProvider
    tp := tracesdk.NewTracerProvider(
        tracesdk.WithBatcher(exporter),
        tracesdk.WithResource(res),
        tracesdk.WithSampler(tracesdk.AlwaysSample()),
    )

    // 设置全局TracerProvider
    otel.SetTracerProvider(tp)

    return &Tracer{
        tracer:      tp.Tracer(serviceName),
        serviceName: serviceName,
    }, nil
}

func (t *Tracer) StartSpan(ctx context.Context, name string) (context.Context, trace.Span) {
    return t.tracer.Start(ctx, name)
}

// 中间件 - gRPC拦截器
func (t *Tracer) UnaryServerInterceptor() grpc.UnaryServerInterceptor {
    return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 
        handler grpc.UnaryHandler) (interface{}, error) {
        
        spanCtx, span := t.StartSpan(ctx, info.FullMethod)
        defer span.End()

        span.SetAttributes(
            attribute.String("service", t.serviceName),
            attribute.String("method", info.FullMethod),
        )

        return handler(spanCtx, req)
    }
}

// health/health_checker.go
package health

import (
    "context"
    "sync"
    "time"
)

type HealthChecker struct {
    services    map[string]ServiceHealth
    mu          sync.RWMutex
    metrics     *monitoring.MetricsCollector
}

type ServiceHealth struct {
    Status    string
    LastCheck time.Time
    Error     error
}

func NewHealthChecker(metrics *monitoring.MetricsCollector) *HealthChecker {
    return &HealthChecker{
        services: make(map[string]ServiceHealth),
        metrics:  metrics,
    }
}

func (h *HealthChecker) RegisterService(serviceName string) {
    h.mu.Lock()
    defer h.mu.Unlock()
    h.services[serviceName] = ServiceHealth{
        Status:    "UNKNOWN",
        LastCheck: time.Now(),
    }
}

func (h *HealthChecker) UpdateStatus(serviceName, status string, err error) {
    h.mu.Lock()
    defer h.mu.Unlock()
    
    h.services[serviceName] = ServiceHealth{
        Status:    status,
        LastCheck: time.Now(),
        Error:     err,
    }

    if err != nil {
        h.metrics.RecordError(serviceName, "health_check")
    }
}

func (h *HealthChecker) GetStatus(serviceName string) ServiceHealth {
    h.mu.RLock()
    defer h.mu.RUnlock()
    return h.services[serviceName]
}

// 使用示例
func main() {
    // 初始化metrics collector
    metrics := monitoring.NewMetricsCollector("product-service")

    // 初始化tracer
    tracer, err := tracing.NewTracer("product-service", "http://jaeger:14268/api/traces")
    if err != nil {
        log.Fatal(err)
    }

    // 初始化health checker
    healthChecker := health.NewHealthChecker(metrics)
    healthChecker.RegisterService("product-service")

    // 创建gRPC服务器并添加拦截器
    server := grpc.NewServer(
        grpc.UnaryInterceptor(tracer.UnaryServerInterceptor()),
    )

    // 启动指标收集
    go func() {
        for {
            metrics.UpdateActiveRequests("product-service", float64(runtime.NumGoroutine()))
            
            var m runtime.MemStats
            runtime.ReadMemStats(&m)
            metrics.UpdateMemoryUsage("product-service", float64(m.Alloc))
            
            time.Sleep(15 * time.Second)
        }
    }()

    // 启动健康检查
    go func() {
        for {
            // 执行健康检查
            err := checkDependencies()
            status := "UP"
            if err != nil {
                status = "DOWN"
            }
            healthChecker.UpdateStatus("product-service", status, err)
            
            time.Sleep(30 * time.Second)
        }
    }()

    // 启动HTTP服务器暴露指标
    http.Handle("/metrics", promhttp.Handler())
    go func() {
        if err := http.ListenAndServe(":9090", nil); err != nil {
            log.Printf("Metrics server error: %v", err)
        }
    }()

    // 启动gRPC服务器
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }
    
    log.Printf("Server listening at %v", lis.Addr())
    if err := server.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}

func checkDependencies() error {
    // 检查数据库连接
    if err := checkDatabase(); err != nil {
        return fmt.Errorf("database check failed: %w", err)
    }
    
    // 检查缓存服务
    if err := checkCache(); err != nil {
        return fmt.Errorf("cache check failed: %w", err)
    }
    
    // 检查消息队列
    if err := checkMessageQueue(); err != nil {
        return fmt.Errorf("message queue check failed: %w", err)
    }
    
    return nil
}

8. 服务配置管理

在微服务架构中,配置管理是一个重要的话题。我们来看看如何实现统一的配置管理:

go 复制代码
// config/config.go
package config

import (
    "context"
    "encoding/json"
    "github.com/coreos/etcd/clientv3"
    "time"
)

type Config struct {
    Database struct {
        Host     string `json:"host"`
        Port     int    `json:"port"`
        User     string `json:"user"`
        Password string `json:"password"`
        DBName   string `json:"dbname"`
    } `json:"database"`
    
    Redis struct {
        Host     string `json:"host"`
        Port     int    `json:"port"`
        Password string `json:"password"`
        DB       int    `json:"db"`
    } `json:"redis"`
    
    Service struct {
        Port         int    `json:"port"`
        Environment string `json:"environment"`
        LogLevel    string `json:"log_level"`
    } `json:"service"`
}

type ConfigManager struct {
    client  *clientv3.Client
    prefix  string
    config  *Config
    watches map[string]clientv3.WatchChan
}

func NewConfigManager(endpoints []string, prefix string) (*ConfigManager, error) {
    client, err := clientv3.New(clientv3.Config{
        Endpoints:   endpoints,
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        return nil, err
    }
    
    return &ConfigManager{
        client:  client,
        prefix:  prefix,
        config:  &Config{},
        watches: make(map[string]clientv3.WatchChan),
    }, nil
}

func (cm *ConfigManager) LoadConfig(ctx context.Context) error {
    resp, err := cm.client.Get(ctx, cm.prefix, clientv3.WithPrefix())
    if err != nil {
        return err
    }
    
    for _, kv := range resp.Kvs {
        if err := json.Unmarshal(kv.Value, cm.config); err != nil {
            return err
        }
    }
    
    return nil
}

func (cm *ConfigManager) WatchConfig(ctx context.Context, callback func(*Config)) {
    watchChan := cm.client.Watch(ctx, cm.prefix, clientv3.WithPrefix())
    
    go func() {
        for watchResp := range watchChan {
            for _, event := range watchResp.Events {
                switch event.Type {
                case clientv3.EventTypePut:
                    if err := json.Unmarshal(event.Kv.Value, cm.config); err != nil {
                        continue
                    }
                    callback(cm.config)
                }
            }
        }
    }()
}

// 使用示例
func main() {
    ctx := context.Background()
    
    // 创建配置管理器
    cm, err := NewConfigManager([]string{"localhost:2379"}, "/config/product-service")
    if err != nil {
        log.Fatal(err)
    }
    
    // 加载初始配置
    if err := cm.LoadConfig(ctx); err != nil {
        log.Fatal(err)
    }
    
    // 监听配置变更
    cm.WatchConfig(ctx, func(config *Config) {
        log.Printf("Configuration updated: %+v", config)
        // 这里可以进行配置热更新的相关操作
    })
}

9. 服务优雅关闭

在微服务架构中,服务的优雅关闭是保证系统稳定性的重要环节。让我们看看如何实现:

go 复制代码
// server/server.go
package server

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"
)

type Server struct {
    httpServer     *http.Server
    grpcServer     *grpc.Server
    metricsServer  *http.Server
    registry       *registry.ServiceRegistry
    connections    sync.WaitGroup
    shutdownTimeout time.Duration
}

func NewServer(config *Config) *Server {
    return &Server{
        httpServer: &http.Server{
            Addr:    config.HTTPAddr,
            Handler: config.HTTPHandler,
        },
        grpcServer: config.GRPCServer,
        metricsServer: &http.Server{
            Addr:    config.MetricsAddr,
            Handler: config.MetricsHandler,
        },
        registry:        config.Registry,
        shutdownTimeout: 30 * time.Second,
    }
}

func (s *Server) Start() error {
    // 启动HTTP服务
    go func() {
        log.Printf("Starting HTTP server on %s", s.httpServer.Addr)
        if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed {
            log.Printf("HTTP server error: %v", err)
        }
    }()

    // 启动gRPC服务
    go func() {
        log.Printf("Starting gRPC server")
        if err := s.grpcServer.Serve(lis); err != nil {
            log.Printf("gRPC server error: %v", err)
        }
    }()

    // 启动metrics服务
    go func() {
        log.Printf("Starting metrics server on %s", s.metricsServer.Addr)
        if err := s.metricsServer.ListenAndServe(); err != http.ErrServerClosed {
            log.Printf("Metrics server error: %v", err)
        }
    }()

    return nil
}

func (s *Server) WaitForShutdown() {
    // 监听系统信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    sig := <-quit
    log.Printf("Received signal: %v", sig)

    ctx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout)
    defer cancel()

    // 从服务注册中心注销
    if err := s.registry.Deregister(ctx); err != nil {
        log.Printf("Failed to deregister service: %v", err)
    }

    // 优雅关闭HTTP服务
    if err := s.httpServer.Shutdown(ctx); err != nil {
        log.Printf("HTTP server shutdown error: %v", err)
    }

    // 优雅关闭gRPC服务
    s.grpcServer.GracefulStop()

    // 优雅关闭metrics服务
    if err := s.metricsServer.Shutdown(ctx); err != nil {
        log.Printf("Metrics server shutdown error: %v", err)
    }

    // 等待所有连接处理完成
    done := make(chan struct{})
    go func() {
        s.connections.Wait()
        close(done)
    }()

    select {
    case <-ctx.Done():
        log.Printf("Shutdown timeout: forcing exit")
    case <-done:
        log.Printf("All connections have been closed")
    }
}

// 连接处理中间件
func (s *Server) ConnectionMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        s.connections.Add(1)
        defer s.connections.Done()
        next.ServeHTTP(w, r)
    })
}

// 使用示例
func main() {
    config := &Config{
        HTTPAddr:       ":8080",
        MetricsAddr:    ":9090",
        HTTPHandler:    httpRouter,
        GRPCServer:    grpcServer,
        MetricsHandler: metricsHandler,
        Registry:      serviceRegistry,
    }

    server := NewServer(config)

    if err := server.Start(); err != nil {
        log.Fatal(err)
    }

    server.WaitForShutdown()
}

10. 服务安全

微服务架构中的安全性同样重要,让我们实现一个包含认证和授权的安全模块:

go 复制代码
// security/jwt.go
package security

import (
    "github.com/dgrijalva/jwt-go"
    "time"
)

type Claims struct {
    UserID    string   `json:"user_id"`
    Username  string   `json:"username"`
    Roles     []string `json:"roles"`
    jwt.StandardClaims
}

type JWTManager struct {
    secretKey     []byte
    tokenDuration time.Duration
}

func NewJWTManager(secretKey string, tokenDuration time.Duration) *JWTManager {
    return &JWTManager{
        secretKey:     []byte(secretKey),
        tokenDuration: tokenDuration,
    }
}

func (m *JWTManager) GenerateToken(userID, username string, roles []string) (string, error) {
    claims := &Claims{
        UserID:   userID,
        Username: username,
        Roles:    roles,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(m.tokenDuration).Unix(),
            IssuedAt:  time.Now().Unix(),
        },
    }

    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(m.secretKey)
}

func (m *JWTManager) ValidateToken(accessToken string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(
        accessToken,
        &Claims{},
        func(token *jwt.Token) (interface{}, error) {
            _, ok := token.Method.(*jwt.SigningMethodHMAC)
            if !ok {
                return nil, fmt.Errorf("unexpected token signing method")
            }
            return m.secretKey, nil
        },
    )

    if err != nil {
        return nil, fmt.Errorf("invalid token: %w", err)
    }

    claims, ok := token.Claims.(*Claims)
    if !ok {
        return nil, fmt.Errorf("invalid token claims")
    }

    return claims, nil
}

// security/rbac.go
package security

type Permission struct {
    Resource string
    Action   string
}

type Role struct {
    Name        string
    Permissions map[Permission]bool
}

type RBAC struct {
    roles map[string]*Role
}

func NewRBAC() *RBAC {
    return &RBAC{
        roles: make(map[string]*Role),
    }
}

func (r *RBAC) AddRole(roleName string) {
    if _, exists := r.roles[roleName]; !exists {
        r.roles[roleName] = &Role{
            Name:        roleName,
            Permissions: make(map[Permission]bool),
        }
    }
}

func (r *RBAC) AddPermission(roleName string, resource string, action string) error {
    role, exists := r.roles[roleName]
    if !exists {
        return fmt.Errorf("role %s does not exist", roleName)
    }

    permission := Permission{
        Resource: resource,
        Action:   action,
    }
    role.Permissions[permission] = true
    return nil
}

func (r *RBAC) CheckPermission(roles []string, resource string, action string) bool {
    permission := Permission{
        Resource: resource,
        Action:   action,
    }

    for _, roleName := range roles {
        if role, exists := r.roles[roleName]; exists {
            if allowed := role.Permissions[permission]; allowed {
                return true
            }
        }
    }
    return false
}

// middleware/auth.go
package middleware

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/metadata"
    "google.golang.org/grpc/status"
)

type AuthInterceptor struct {
    jwtManager *security.JWTManager
    rbac       *security.RBAC
}

func NewAuthInterceptor(jwtManager *security.JWTManager, rbac *security.RBAC) *AuthInterceptor {
    return &AuthInterceptor{
        jwtManager: jwtManager,
        rbac:       rbac,
    }
}

// Unary interceptor for gRPC servers
func (i *AuthInterceptor) Unary() grpc.UnaryServerInterceptor {
    return func(
        ctx context.Context,
        req interface{},
        info *grpc.UnaryServerInfo,
        handler grpc.UnaryHandler,
    ) (interface{}, error) {
        if !requiresAuth(info.FullMethod) {
            return handler(ctx, req)
        }

        claims, err := i.authorize(ctx)
        if err != nil {
            return nil, err
        }

        resource, action := extractResourceAndAction(info.FullMethod)
        if !i.rbac.CheckPermission(claims.Roles, resource, action) {
            return nil, status.Errorf(codes.PermissionDenied, "no permission to access this RPC")
        }

        return handler(ctx, req)
    }
}

// Stream interceptor for gRPC servers
func (i *AuthInterceptor) Stream() grpc.StreamServerInterceptor {
    return func(
        srv interface{},
        stream grpc.ServerStream,
        info *grpc.StreamServerInfo,
        handler grpc.StreamHandler,
    ) error {
        if !requiresAuth(info.FullMethod) {
            return handler(srv, stream)
        }

        claims, err := i.authorize(stream.Context())
        if err != nil {
            return err
        }

        resource, action := extractResourceAndAction(info.FullMethod)
        if !i.rbac.CheckPermission(claims.Roles, resource, action) {
            return status.Errorf(codes.PermissionDenied, "no permission to access this RPC")
        }

        return handler(srv, stream)
    }
}

func (i *AuthInterceptor) authorize(ctx context.Context) (*security.Claims, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return nil, status.Errorf(codes.Unauthenticated, "metadata is not provided")
    }

    values := md["authorization"]
    if len(values) == 0 {
        return nil, status.Errorf(codes.Unauthenticated, "authorization token is not provided")
    }

    accessToken := values[0]
    claims, err := i.jwtManager.ValidateToken(accessToken)
    if err != nil {
        return nil, status.Errorf(codes.Unauthenticated, "access token is invalid: %v", err)
    }

    return claims, nil
}

func requiresAuth(method string) bool {
    // 配置不需要认证的方法
    noAuthMethods := map[string]bool{
        "/user.UserService/Login":    true,
        "/user.UserService/Register": true,
        "/health.HealthCheck/Check":  true,
    }

    return !noAuthMethods[method]
}

func extractResourceAndAction(method string) (string, string) {
    // 从方法名中提取资源和动作
    // 例如: "/product.ProductService/CreateProduct" -> resource: "product", action: "create"
    parts := strings.Split(method, "/")
    if len(parts) != 3 {
        return "", ""
    }

    serviceParts := strings.Split(parts[1], ".")
    if len(serviceParts) != 2 {
        return "", ""
    }

    resource := strings.ToLower(serviceParts[0])
    action := strings.ToLower(strings.TrimPrefix(parts[2], serviceParts[1]))
    return resource, action
}

// 使用示例
func main() {
    // 初始化JWT管理器
    jwtManager := security.NewJWTManager("your-secret-key", 24*time.Hour)

    // 初始化RBAC
    rbac := security.NewRBAC()
    
    // 添加角色和权限
    rbac.AddRole("admin")
    rbac.AddRole("user")

    // 为管理员添加权限
    rbac.AddPermission("admin", "product", "create")
    rbac.AddPermission("admin", "product", "update")
    rbac.AddPermission("admin", "product", "delete")
    rbac.AddPermission("admin", "order", "view")

    // 为普通用户添加权限
    rbac.AddPermission("user", "product", "view")
    rbac.AddPermission("user", "order", "create")

    // 创建认证拦截器
    authInterceptor := middleware.NewAuthInterceptor(jwtManager, rbac)

    // 创建gRPC服务器,并添加拦截器
    server := grpc.NewServer(
        grpc.UnaryInterceptor(authInterceptor.Unary()),
        grpc.StreamInterceptor(authInterceptor.Stream()),
    )

    // 注册服务
    pb.RegisterProductServiceServer(server, &ProductService{})
    pb.RegisterOrderServiceServer(server, &OrderService{})

    // 启动服务器
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    log.Printf("Server listening at %v", lis.Addr())
    if err := server.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

让我们继续完成微服务架构设计的最后几个重要部分:

11. 分布式事务处理

在微服务架构中,分布式事务的处理是一个重要的挑战,我们来看看如何使用Saga模式处理:

go 复制代码
// saga/saga.go
package saga

import (
    "context"
    "fmt"
    "sync"
)

type TransactionStep struct {
    Execute   func(ctx context.Context) error
    Compensate func(ctx context.Context) error
}

type Saga struct {
    steps    []TransactionStep
    mutex    sync.Mutex
    executed []bool
}

func NewSaga() *Saga {
    return &Saga{
        steps:    make([]TransactionStep, 0),
        executed: make([]bool, 0),
    }
}

func (s *Saga) AddStep(step TransactionStep) {
    s.mutex.Lock()
    defer s.mutex.Unlock()
    s.steps = append(s.steps, step)
    s.executed = append(s.executed, false)
}

func (s *Saga) Execute(ctx context.Context) error {
    for i, step := range s.steps {
        if err := step.Execute(ctx); err != nil {
            // 执行补偿操作
            s.compensate(ctx, i)
            return fmt.Errorf("step %d failed: %w", i, err)
        }
        s.executed[i] = true
    }
    return nil
}

func (s *Saga) compensate(ctx context.Context, failedStep int) {
    // 从失败步骤开始,反向执行补偿操作
    for i := failedStep; i >= 0; i-- {
        if s.executed[i] {
            if err := s.steps[i].Compensate(ctx); err != nil {
                // 记录补偿操作失败
                fmt.Printf("compensation failed for step %d: %v\n", i, err)
            }
        }
    }
}

// order/order.go - 订单服务中使用Saga的示例
type OrderService struct {
    productClient  pb.ProductServiceClient
    inventoryClient pb.InventoryServiceClient
    paymentClient   pb.PaymentServiceClient
}

func (s *OrderService) CreateOrder(ctx context.Context, order *pb.Order) error {
    saga := NewSaga()
    
    // 检查商品库存
    saga.AddStep(TransactionStep{
        Execute: func(ctx context.Context) error {
            req := &pb.CheckInventoryRequest{
                ProductId: order.ProductId,
                Quantity: order.Quantity,
            }
            _, err := s.inventoryClient.CheckInventory(ctx, req)
            return err
        },
        Compensate: func(ctx context.Context) error {
            // 库存检查不需要补偿
            return nil
        },
    })
    
    // 扣减库存
    saga.AddStep(TransactionStep{
        Execute: func(ctx context.Context) error {
            req := &pb.DeductInventoryRequest{
                ProductId: order.ProductId,
                Quantity: order.Quantity,
            }
            _, err := s.inventoryClient.DeductInventory(ctx, req)
            return err
        },
        Compensate: func(ctx context.Context) error {
            req := &pb.RestoreInventoryRequest{
                ProductId: order.ProductId,
                Quantity: order.Quantity,
            }
            _, err := s.inventoryClient.RestoreInventory(ctx, req)
            return err
        },
    })
    
    // 处理支付
    saga.AddStep(TransactionStep{
        Execute: func(ctx context.Context) error {
            req := &pb.ProcessPaymentRequest{
                OrderId: order.Id,
                Amount: order.TotalAmount,
            }
            _, err := s.paymentClient.ProcessPayment(ctx, req)
            return err
        },
        Compensate: func(ctx context.Context) error {
            req := &pb.RefundPaymentRequest{
                OrderId: order.Id,
                Amount: order.TotalAmount,
            }
            _, err := s.paymentClient.RefundPayment(ctx, req)
            return err
        },
    })
    
    return saga.Execute(ctx)
}

12. 日志聚合与分析

在微服务架构中,统一的日志收集和分析系统非常重要:

go 复制代码
// logger/logger.go
package logger

import (
    "context"
    "encoding/json"
    "github.com/olivere/elastic/v7"
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "time"
)

type LogEntry struct {
    Timestamp   time.Time              `json:"timestamp"`
    Level       string                 `json:"level"`
    Service     string                 `json:"service"`
    TraceID     string                 `json:"trace_id"`
    SpanID      string                 `json:"span_id"`
    Message     string                 `json:"message"`
    Fields      map[string]interface{} `json:"fields,omitempty"`
}

type Logger struct {
    zap        *zap.Logger
    elastic    *elastic.Client
    serviceName string
}

func NewLogger(serviceName string, elasticURL string) (*Logger, error) {
    // 配置zap logger
    config := zap.NewProductionConfig()
    config.EncoderConfig.TimeKey = "timestamp"
    config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    
    zapLogger, err := config.Build()
    if err != nil {
        return nil, err
    }
    
    // 配置Elasticsearch客户端
    client, err := elastic.NewClient(
        elastic.SetURL(elasticURL),
        elastic.SetSniff(false),
    )
    if err != nil {
        return nil, err
    }
    
    return &Logger{
        zap:         zapLogger,
        elastic:     client,
        serviceName: serviceName,
    }, nil
}

func (l *Logger) Info(ctx context.Context, msg string, fields ...zap.Field) {
    l.log(ctx, "info", msg, fields...)
}

func (l *Logger) Error(ctx context.Context, msg string, fields ...zap.Field) {
    l.log(ctx, "error", msg, fields...)
}

func (l *Logger) log(ctx context.Context, level, msg string, fields ...zap.Field) {
    // 获取追踪信息
    span := opentracing.SpanFromContext(ctx)
    var traceID, spanID string
    if span != nil {
        spanContext := span.Context().(jaeger.SpanContext)
        traceID = spanContext.TraceID().String()
        spanID = spanContext.SpanID().String()
    }
    
    // 创建日志条目
    entry := LogEntry{
        Timestamp:   time.Now(),
        Level:       level,
        Service:     l.serviceName,
        TraceID:     traceID,
        SpanID:      spanID,
        Message:     msg,
        Fields:      make(map[string]interface{}),
    }
    
    // 添加额外字段
    for _, field := range fields {
        entry.Fields[field.Key] = field.Interface
    }
    
    // 本地日志
    if level == "error" {
        l.zap.Error(msg, fields...)
    } else {
        l.zap.Info(msg, fields...)
    }
    
    // 异步发送到Elasticsearch
    go func() {
        _, err := l.elastic.Index().
            Index(fmt.Sprintf("logs-%s", time.Now().Format("2006-01-02"))).
            Type("_doc").
            BodyJson(entry).
            Do(context.Background())
        
        if err != nil {
            l.zap.Error("Failed to send log to Elasticsearch",
                zap.Error(err),
                zap.Any("entry", entry),
            )
        }
    }()
}

// 日志查询接口
func (l *Logger) QueryLogs(ctx context.Context, params QueryParams) ([]LogEntry, error) {
    query := elastic.NewBoolQuery()
    
    if params.Service != "" {
        query = query.Must(elastic.NewTermQuery("service", params.Service))
    }
    
    if params.Level != "" {
        query = query.Must(elastic.NewTermQuery("level", params.Level))
    }
    
    if params.TraceID != "" {
        query = query.Must(elastic.NewTermQuery("trace_id", params.TraceID))
    }
    
    if !params.StartTime.IsZero() {
        query = query.Must(elastic.NewRangeQuery("timestamp").Gte(params.StartTime))
    }
    
    if !params.EndTime.IsZero() {
        query = query.Must(elastic.NewRangeQuery("timestamp").Lte(params.EndTime))
    }
    
    result, err := l.elastic.Search().
        Index(fmt.Sprintf("logs-%s", time.Now().Format("2006-01-02"))).
        Query(query).
        Sort("timestamp", false).
        Size(params.Limit).
        From(params.Offset).
        Do(ctx)
        
    if err != nil {
        return nil, err
    }
    
    var logs []LogEntry
    for _, hit := range result.Hits.Hits {
        var entry LogEntry
        if err := json.Unmarshal(hit.Source, &entry); err != nil {
            return nil, err
        }
        logs = append(logs, entry)
    }
    
    return logs, nil
}

13. 总结与最佳实践

在本节课中,我们详细探讨了微服务架构设计的核心内容:

  1. 服务拆分

    • 采用DDD方法
    • 明确业务边界
    • 保持适当粒度
  2. 接口设计

    • 使用gRPC和RESTful API
    • 版本控制
    • 接口文档
  3. 服务治理

    • 服务注册与发现
    • 熔断器模式
    • 限流措施
  4. 部署策略

    • 容器化部署
    • Kubernetes编排
    • 灰度发布

14. 实践检查清单

以上就是微服务架构设计的核心内容。在实际项目中,需要根据具体业务场景和技术栈选择合适的实现方案。重要的是要始终遵循微服务的设计原则,保持服务的独立性和可维护性。同时,要重视监控、日志、追踪等基础设施的建设,这些都是保证微服务架构稳定运行的关键因素。


怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
芒果爱编程1 小时前
MCU、ARM体系结构,单片机基础,单片机操作
开发语言·网络·c++·tcp/ip·算法
明明跟你说过1 小时前
【Go语言】从Google实验室走向全球的编程新星
开发语言·后端·go·go1.19
凌盛羽2 小时前
C#对Excel表csv文件的读写操作
开发语言·windows·物联网·microsoft·c#·excel
VBA63372 小时前
VBA高级应用30例应用在Excel中的ListObject对象:向表中添加注释
开发语言
Dontla2 小时前
Rust字节数组(Byte Array)Rust u8、Vec<u8>、数组切片、向量切片、字符串转字节数组转字符串、&[u8]类型:字节数组引用
开发语言·rust
走在考研路上4 小时前
Python错误处理
开发语言·python
数据小爬虫@4 小时前
Python爬虫:如何优雅地“偷窥”商品详情
开发语言·爬虫·python
CV大法好4 小时前
刘铁猛p3 C# 控制台程序引用System.Windows.Forms报错,无法引用程序集 解决方法
开发语言·c#
Days20504 小时前
uniapp小程序增加加载功能
开发语言·前端·javascript
deephub5 小时前
ORCA:基于持续批处理的LLM推理性能优化技术详解
人工智能·深度学习·性能优化·llm