Go 微服务基础

微服务架构是将单体应用拆分成多个小型、独立的服务,每个服务负责特定的业务功能。

微服务架构特点

  1. 服务独立:每个服务可以独立开发、部署、扩展
  2. 技术异构:不同服务可以使用不同的技术栈
  3. 数据独立:每个服务有自己的数据库
  4. 通信:服务之间通过 API 通信(HTTP、gRPC 等)

简单的微服务示例

项目结构

microservices/
├── go.mod
├── user-service/
│   ├── main.go
│   └── handler.go
├── order-service/
│   ├── main.go
│   └── handler.go
└── gateway/
    └── main.go

用户服务(User Service)

// user-service/main.go
package main
 
import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"
)
 
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}
 
var users = []User{
    {ID: 1, Name: "Alice", Email: "alice@example.com"},
    {ID: 2, Name: "Bob", Email: "bob@example.com"},
}
 
func getUserHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "只支持 GET 方法", http.StatusMethodNotAllowed)
        return
    }
    
    // 获取用户 ID
    idStr := r.URL.Query().Get("id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "无效的用户 ID", http.StatusBadRequest)
        return
    }
    
    // 查找用户
    var user *User
    for _, u := range users {
        if u.ID == id {
            user = &u
            break
        }
    }
    
    if user == nil {
        http.Error(w, "用户不存在", http.StatusNotFound)
        return
    }
    
    // 返回 JSON
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}
 
func listUsersHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}
 
func main() {
    http.HandleFunc("/user", getUserHandler)
    http.HandleFunc("/users", listUsersHandler)
    
    port := ":8001"
    log.Printf("用户服务启动在 http://localhost%s", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

订单服务(Order Service)

// order-service/main.go
package main
 
import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"
)
 
type Order struct {
    ID     int    `json:"id"`
    UserID int    `json:"user_id"`
    Item   string `json:"item"`
    Amount float64 `json:"amount"`
}
 
var orders = []Order{
    {ID: 1, UserID: 1, Item: "商品A", Amount: 100.0},
    {ID: 2, UserID: 1, Item: "商品B", Amount: 200.0},
    {ID: 3, UserID: 2, Item: "商品C", Amount: 150.0},
}
 
func getOrderHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        http.Error(w, "只支持 GET 方法", http.StatusMethodNotAllowed)
        return
    }
    
    idStr := r.URL.Query().Get("id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "无效的订单 ID", http.StatusBadRequest)
        return
    }
    
    var order *Order
    for _, o := range orders {
        if o.ID == id {
            order = &o
            break
        }
    }
    
    if order == nil {
        http.Error(w, "订单不存在", http.StatusNotFound)
        return
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(order)
}
 
func getUserOrdersHandler(w http.ResponseWriter, r *http.Request) {
    userIDStr := r.URL.Query().Get("user_id")
    userID, err := strconv.Atoi(userIDStr)
    if err != nil {
        http.Error(w, "无效的用户 ID", http.StatusBadRequest)
        return
    }
    
    var userOrders []Order
    for _, o := range orders {
        if o.UserID == userID {
            userOrders = append(userOrders, o)
        }
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(userOrders)
}
 
func main() {
    http.HandleFunc("/order", getOrderHandler)
    http.HandleFunc("/orders", getUserOrdersHandler)
    
    port := ":8002"
    log.Printf("订单服务启动在 http://localhost%s", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

API 网关(Gateway)

// gateway/main.go
package main
 
import (
    "io"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
)
 
// 反向代理到用户服务
func userProxy(w http.ResponseWriter, r *http.Request) {
    target, _ := url.Parse("http://localhost:8001")
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.ServeHTTP(w, r)
}
 
// 反向代理到订单服务
func orderProxy(w http.ResponseWriter, r *http.Request) {
    target, _ := url.Parse("http://localhost:8002")
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.ServeHTTP(w, r)
}
 
// 聚合服务:获取用户及其订单
func userWithOrdersHandler(w http.ResponseWriter, r *http.Request) {
    userID := r.URL.Query().Get("user_id")
    
    // 调用用户服务
    userResp, err := http.Get("http://localhost:8001/user?id=" + userID)
    if err != nil {
        http.Error(w, "用户服务不可用", http.StatusInternalServerError)
        return
    }
    defer userResp.Body.Close()
    
    // 调用订单服务
    ordersResp, err := http.Get("http://localhost:8002/orders?user_id=" + userID)
    if err != nil {
        http.Error(w, "订单服务不可用", http.StatusInternalServerError)
        return
    }
    defer ordersResp.Body.Close()
    
    // 聚合响应(简化版)
    w.Header().Set("Content-Type", "application/json")
    w.Write([]byte("{\"user\": "))
    io.Copy(w, userResp.Body)
    w.Write([]byte(", \"orders\": "))
    io.Copy(w, ordersResp.Body)
    w.Write([]byte("}"))
}
 
func main() {
    // 路由
    http.HandleFunc("/api/user/", userProxy)
    http.HandleFunc("/api/order/", orderProxy)
    http.HandleFunc("/api/user-with-orders", userWithOrdersHandler)
    
    port := ":8080"
    log.Printf("API 网关启动在 http://localhost%s", port)
    log.Println("可用路由:")
    log.Println("  - http://localhost:8080/api/user/?id=1")
    log.Println("  - http://localhost:8080/api/order/?id=1")
    log.Println("  - http://localhost:8080/api/user-with-orders?user_id=1")
    log.Fatal(http.ListenAndServe(port, nil))
}

服务发现

简单的服务注册

// service-registry/main.go
package main
 
import (
    "encoding/json"
    "log"
    "net/http"
    "sync"
    "time"
)
 
type Service struct {
    Name    string    `json:"name"`
    Address string    `json:"address"`
    Port    int       `json:"port"`
    LastSeen time.Time `json:"last_seen"`
}
 
type Registry struct {
    services map[string]*Service
    mutex    sync.RWMutex
}
 
func NewRegistry() *Registry {
    r := &Registry{
        services: make(map[string]*Service),
    }
    
    // 启动清理过期服务的 goroutine
    go r.cleanup()
    
    return r
}
 
func (r *Registry) Register(name, address string, port int) {
    r.mutex.Lock()
    defer r.mutex.Unlock()
    
    r.services[name] = &Service{
        Name:     name,
        Address:  address,
        Port:     port,
        LastSeen: time.Now(),
    }
    
    log.Printf("服务注册: %s -> %s:%d", name, address, port)
}
 
func (r *Registry) Get(name string) (*Service, bool) {
    r.mutex.RLock()
    defer r.mutex.RUnlock()
    
    service, exists := r.services[name]
    if exists {
        service.LastSeen = time.Now()  // 更新最后访问时间
    }
    return service, exists
}
 
func (r *Registry) List() []*Service {
    r.mutex.RLock()
    defer r.mutex.RUnlock()
    
    services := make([]*Service, 0, len(r.services))
    for _, s := range r.services {
        services = append(services, s)
    }
    return services
}
 
func (r *Registry) cleanup() {
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()
    
    for range ticker.C {
        r.mutex.Lock()
        now := time.Now()
        for name, service := range r.services {
            if now.Sub(service.LastSeen) > 60*time.Second {
                delete(r.services, name)
                log.Printf("服务过期,已移除: %s", name)
            }
        }
        r.mutex.Unlock()
    }
}
 
func main() {
    registry := NewRegistry()
    
    // 注册服务
    http.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {
        if r.Method != http.MethodPost {
            http.Error(w, "只支持 POST 方法", http.StatusMethodNotAllowed)
            return
        }
        
        var service Service
        if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
            http.Error(w, "无效的请求", http.StatusBadRequest)
            return
        }
        
        registry.Register(service.Name, service.Address, service.Port)
        w.WriteHeader(http.StatusOK)
    })
    
    // 获取服务
    http.HandleFunc("/service/", func(w http.ResponseWriter, r *http.Request) {
        name := r.URL.Path[len("/service/"):]
        service, exists := registry.Get(name)
        if !exists {
            http.Error(w, "服务不存在", http.StatusNotFound)
            return
        }
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(service)
    })
    
    // 列出所有服务
    http.HandleFunc("/services", func(w http.ResponseWriter, r *http.Request) {
        services := registry.List()
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(services)
    })
    
    port := ":8500"
    log.Printf("服务注册中心启动在 http://localhost%s", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

配置管理

环境变量配置

// config/config.go
package config
 
import (
    "os"
    "strconv"
)
 
type Config struct {
    ServerPort    string
    DatabaseURL   string
    RedisURL      string
    LogLevel      string
    MaxWorkers    int
}
 
func Load() *Config {
    return &Config{
        ServerPort:  getEnv("SERVER_PORT", "8080"),
        DatabaseURL: getEnv("DATABASE_URL", "localhost:3306"),
        RedisURL:    getEnv("REDIS_URL", "localhost:6379"),
        LogLevel:    getEnv("LOG_LEVEL", "info"),
        MaxWorkers:  getEnvAsInt("MAX_WORKERS", 10),
    }
}
 
func getEnv(key, defaultValue string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return defaultValue
}
 
func getEnvAsInt(key string, defaultValue int) int {
    if value := os.Getenv(key); value != "" {
        if intValue, err := strconv.Atoi(value); err == nil {
            return intValue
        }
    }
    return defaultValue
}

健康检查

// health/health.go
package health
 
import (
    "encoding/json"
    "net/http"
    "time"
)
 
type HealthStatus struct {
    Status    string            `json:"status"`
    Timestamp time.Time         `json:"timestamp"`
    Services  map[string]string `json:"services,omitempty"`
}
 
func HealthHandler(w http.ResponseWriter, r *http.Request) {
    status := HealthStatus{
        Status:    "healthy",
        Timestamp: time.Now(),
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(status)
}
 
func ReadinessHandler(w http.ResponseWriter, r *http.Request) {
    // 检查依赖服务是否就绪
    status := HealthStatus{
        Status:    "ready",
        Timestamp: time.Now(),
        Services: map[string]string{
            "database": "ok",
            "redis":    "ok",
        },
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(status)
}

日志管理

// logger/logger.go
package logger
 
import (
    "log"
    "os"
)
 
var (
    InfoLogger  *log.Logger
    ErrorLogger *log.Logger
)
 
func Init() {
    InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
    ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
}
 
func Info(format string, v ...interface{}) {
    InfoLogger.Printf(format, v...)
}
 
func Error(format string, v ...interface{}) {
    ErrorLogger.Printf(format, v...)
}

总结

微服务架构的关键点:

  1. 服务拆分:按业务功能拆分服务
  2. API 网关:统一入口,路由和聚合
  3. 服务发现:服务注册与发现机制
  4. 配置管理:集中管理配置
  5. 健康检查:监控服务状态
  6. 日志管理:统一日志格式

这些是微服务的基础,实际项目中可以使用:

  • gRPC:高性能 RPC 框架
  • Consul/Eureka:服务发现
  • Kubernetes:容器编排
  • Istio:服务网格

go 微服务 架构 服务发现