Go Web 编程快速入门 12 - 微服务架构:服务发现、负载均衡与分布式系统

微服务将系统拆分为可独立部署的服务,每个服务围绕业务能力构建、独立扩展与演进。本章以第 04.1 章的"循序渐进+可运行示例"的风格,讲解服务注册与发现、负载均衡、分布式协调与事务的基础落地方式,并完成一个最小可用的"网关调用用户服务"的示例。

1 微服务基础概念

1.1 服务接口与生命周期

go 复制代码
type Service interface {
    Name() string
    Version() string
    Start() error
    Stop() error
    Health() error
}

type BaseService struct {
    name    string
    version string
    srv     *http.Server
}

func NewBaseService(name, version string, handler http.Handler, addr string) *BaseService {
    return &BaseService{ name: name, version: version, srv: &http.Server{Addr: addr, Handler: handler} }
}

func (s *BaseService) Name() string    { return s.name }
func (s *BaseService) Version() string { return s.version }
func (s *BaseService) Start() error    { go s.srv.ListenAndServe(); return nil }
func (s *BaseService) Stop() error     { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second); defer cancel(); return s.srv.Shutdown(ctx) }
func (s *BaseService) Health() error   { return nil }

1.2 健康检查端点

go 复制代码
func HealthHandler(name, version string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        _ = json.NewEncoder(w).Encode(map[string]string{"status": "healthy", "service": name, "version": version})
    }
}

2 服务注册与发现(最小内存注册表)

2.1 注册表与服务信息

go 复制代码
type ServiceInfo struct {
    Name    string
    Version string
    Address string // host
    Port    int
    Tags    []string
}

type Registry struct { store map[string][]ServiceInfo }

func NewRegistry() *Registry { return &Registry{store: make(map[string][]ServiceInfo)} }

func (r *Registry) Register(info ServiceInfo) { r.store[info.Name] = append(r.store[info.Name], info) }
func (r *Registry) Discover(name string) []ServiceInfo { return r.store[name] }

2.2 客户端发现与调用

go 复制代码
type Client struct { reg *Registry; lb LoadBalancer; httpc *http.Client }

func NewClient(reg *Registry, lb LoadBalancer) *Client {
    return &Client{reg: reg, lb: lb, httpc: &http.Client{Timeout: 3 * time.Second}}
}

func (c *Client) CallJSON(ctx context.Context, svcName, path string, req, resp interface{}) error {
    insts := c.reg.Discover(svcName)
    if len(insts) == 0 { return fmt.Errorf("未发现服务: %s", svcName) }
    inst, err := c.lb.Select(insts)
    if err != nil { return err }
    url := fmt.Sprintf("http://%s:%d%s", inst.Address, inst.Port, path)
    var body io.Reader
    if req != nil { b, _ := json.Marshal(req); body = bytes.NewReader(b) }
    httpReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, url, body)
    httpReq.Header.Set("Content-Type", "application/json")
    httpResp, err := c.httpc.Do(httpReq)
    if err != nil { return err }
    defer httpResp.Body.Close()
    if httpResp.StatusCode >= 400 { return fmt.Errorf("HTTP错误: %s", httpResp.Status) }
    if resp != nil { return json.NewDecoder(httpResp.Body).Decode(resp) }
    return nil
}

3 负载均衡实现(轮询)

go 复制代码
type LoadBalancer interface { Select([]ServiceInfo) (ServiceInfo, error) }

type RoundRobin struct { idx int32 }

func (rr *RoundRobin) Select(list []ServiceInfo) (ServiceInfo, error) {
    if len(list) == 0 { return ServiceInfo{}, fmt.Errorf("无可用实例") }
    i := atomic.AddInt32(&rr.idx, 1)
    return list[int(i)%len(list)], nil
}

4 分布式锁与协调(接口与简化实现)

为了讲解思路,我们先定义接口与最小可用实现(内存锁,单实例适用;生产可替换为 Redis/Etcd/ZooKeeper 等)。

go 复制代码
type DistLock interface {
    TryLock(key string, ttl time.Duration) bool
    Unlock(key string)
}

type MemLock struct { m sync.Map }

func (l *MemLock) TryLock(key string, ttl time.Duration) bool {
    _, loaded := l.m.LoadOrStore(key, time.Now().Add(ttl))
    return !loaded
}
func (l *MemLock) Unlock(key string) { l.m.Delete(key) }

5 分布式事务入门(两阶段思想)

接口化事务参与者与事务管理器,结合持久化与消息队列可进一步增强。本章提供概念与最小骨架示例。

go 复制代码
type TxParticipant interface {
    ID() string
    Prepare(ctx context.Context, txID string) error
    Commit(ctx context.Context, txID string) error
    Rollback(ctx context.Context, txID string) error
}

type TxManager struct {}

func (m *TxManager) TwoPhaseCommit(ctx context.Context, txID string, ps []TxParticipant) error {
    for _, p := range ps { if err := p.Prepare(ctx, txID); err != nil { for _, rp := range ps { _ = rp.Rollback(ctx, txID) }; return err } }
    for _, p := range ps { if err := p.Commit(ctx, txID); err != nil { return err } }
    return nil
}

6 实战:网关调用用户服务

6.1 用户服务(提供 /user/info)

go 复制代码
func userHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    _ = json.NewEncoder(w).Encode(map[string]interface{"id": 1, "name": "Alice"})
}

func startUserService(reg *Registry) {
    mux := http.NewServeMux()
    mux.HandleFunc("/user/info", userHandler)
    mux.HandleFunc("/health", HealthHandler("user-service", "v1"))
    svc := NewBaseService("user-service", "v1", mux, ":8081")
    _ = svc.Start()
    reg.Register(ServiceInfo{Name: svc.Name(), Version: svc.Version(), Address: "127.0.0.1", Port: 8081})
}

6.2 网关服务(转发到用户服务)

go 复制代码
func startGateway(reg *Registry) {
    client := NewClient(reg, &RoundRobin{})
    mux := http.NewServeMux()
    mux.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
        var resp map[string]interface{}
        if err := client.CallJSON(r.Context(), "user-service", "/user/info", nil, &resp); err != nil {
            http.Error(w, err.Error(), http.StatusBadGateway); return
        }
        w.Header().Set("Content-Type", "application/json")
        _ = json.NewEncoder(w).Encode(resp)
    })
    mux.HandleFunc("/health", HealthHandler("gateway", "v1"))
    svc := NewBaseService("gateway", "v1", mux, ":8080")
    _ = svc.Start()
}

6.3 启动主函数

go 复制代码
func main() {
    reg := NewRegistry()
    startUserService(reg)
    startGateway(reg)
    log.Println("网关: http://localhost:8080/api/user")
    select{} // 阻塞主协程
}

至此,我们实现了最小可用的注册、发现与负载均衡链路。生产环境中可将注册中心替换为 Consul/Etcd/Kubernetes,加入健康上报、摘除、熔断与重试策略。

相关推荐
旺仔小拳头..3 小时前
HTML——表单与表格
前端·html
xu_duo_i3 小时前
vue2+elementUI后端返回二进制流,前端下载实现
前端·javascript·elementui
慧一居士3 小时前
在Vue项目中平滑地引入HTML文件
前端·vue.js
我的20093 小时前
HTML常用特殊字符
前端·html
开发者如是说3 小时前
我用 Compose 写了一个 i18n 多语言管理工具
前端·后端·架构
微信api接口介绍3 小时前
微信个人发消息api
运维·服务器·开发语言·前端·网络·微信·ipad
阿明Drift3 小时前
从炫酷粒子星云学 Three.js:深度解析一个 15 万粒子的 GPU 动画系统
前端·three.js
凉_橙3 小时前
移动端h5适配方案
前端
久亮哦3 小时前
开发Electron程序
前端·javascript·electron