Go网络编程基础

Go语言的网络编程模型基于CSP(Communicating Sequential Processes)并发模型,通过net包提供了简洁而强大的网络编程能力。无论是底层的TCP/UDP socket编程,还是高层的HTTP服务构建,Go都提供了优雅的解决方案。

Go网络编程核心优势

  • goroutine-per-connection:每个连接一个goroutine,开发模型简单高效
  • 标准库完备:net、net/http包功能完善,生产级可用
  • 零依赖部署:静态编译,单二进制文件部署
  • 性能卓越:runtime调度器针对网络I/O优化,性能接近C++

网络编程层次结构

层次 包/库 适用场景
应用层 net/http, net/rpc Web服务、REST API、RPC
传输层 net/tcp, net/udp 自定义协议、长连接服务
网络层 golang.org/x/net/ipv4 底层网络编程、组播
WebSocket gorilla/websocket, nhooyr/websocket 实时通信、推送服务
gRPC google.golang.org/grpc 微服务间通信

TCP Socket编程

理解底层TCP编程有助于我们更好地掌握HTTP服务的原理,也是构建自定义协议服务的基础。

TCP服务器实现

package main

import (
    "bufio"
    "fmt"
    "net"
    "time"
)

// TCPServer TCP服务器结构
type TCPServer struct {
    addr     string
    listener net.Listener
    handler  func(net.Conn)
}

func NewTCPServer(addr string) *TCPServer {
    return &TCPServer{addr: addr}
}

func (s *TCPServer) Start() error {
    ln, err := net.Listen("tcp", s.addr)
    if err != nil {
        return err
    }
    s.listener = ln
    
    fmt.Printf("TCP server listening on %s\n", s.addr)
    
    for {
        conn, err := ln.Accept()
        if err != nil {
            if opErr, ok := err.(*net.OpError); ok && opErr.Temporary() {
                time.Sleep(100 * time.Millisecond)
                continue
            }
            return err
        }
        
        // 每个连接一个goroutine
        go s.handleConnection(conn)
    }
}

func (s *TCPServer) handleConnection(conn net.Conn) {
    defer conn.Close()
    
    // 设置读写超时
    conn.SetReadDeadline(time.Now().Add(30 * time.Second))
    conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
    
    reader := bufio.NewReader(conn)
    
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            fmt.Printf("Read error: %v\n", err)
            return
        }
        
        // 处理请求
        response := s.processRequest(line)
        
        _, err = conn.Write([]byte(response))
        if err != nil {
            fmt.Printf("Write error: %v\n", err)
            return
        }
    }
}

func (s *TCPServer) processRequest(req string) string {
    return fmt.Sprintf("Echo: %s", req)
}

TCP客户端实现

// TCPClient TCP客户端
type TCPClient struct {
    conn   net.Conn
    reader *bufio.Reader
    writer *bufio.Writer
}

func DialTCP(addr string) (*TCPClient, error) {
    conn, err := net.DialTimeout("tcp", addr, 10*time.Second)
    if err != nil {
        return nil, err
    }
    
    return &TCPClient{
        conn:   conn,
        reader: bufio.NewReader(conn),
        writer: bufio.NewWriter(conn),
    }, nil
}

func (c *TCPClient) Send(msg string) (string, error) {
    // 发送消息
    if _, err := c.writer.WriteString(msg + "\n"); err != nil {
        return "", err
    }
    if err := c.writer.Flush(); err != nil {
        return "", err
    }
    
    // 读取响应
    response, err := c.reader.ReadString('\n')
    if err != nil {
        return "", err
    }
    
    return response, nil
}

func (c *TCPClient) Close() error {
    return c.conn.Close()
}

连接池实现

// ConnPool 连接池
type ConnPool struct {
    addr      string
    maxConns  int
    conns     chan net.Conn
    mu        sync.Mutex
}

func NewConnPool(addr string, maxConns int) *ConnPool {
    return &ConnPool{
        addr:     addr,
        maxConns: maxConns,
        conns:    make(chan net.Conn, maxConns),
    }
}

func (p *ConnPool) Get() (net.Conn, error) {
    select {
    case conn := <-p.conns:
        // 检查连接是否有效
        if p.isValid(conn) {
            return conn, nil
        }
        conn.Close()
        return p.createConn()
    default:
        return p.createConn()
    }
}

func (p *ConnPool) Put(conn net.Conn) {
    select {
    case p.conns <- conn:
        // 归还成功
    default:
        // 池已满,关闭连接
        conn.Close()
    }
}

func (p *ConnPool) createConn() (net.Conn, error) {
    return net.DialTimeout("tcp", p.addr, 5*time.Second)
}

func (p *ConnPool) isValid(conn net.Conn) bool {
    // 简单检查:设置短超时测试
    conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
    defer conn.SetReadDeadline(time.Time{})
    
    // 尝试读取(预期会超时)
    buf := make([]byte, 1)
    _, err := conn.Read(buf)
    return err == nil || err.(net.Error).Timeout()
}

HTTP服务器构建

net/http包是Go标准库中最成功的包之一,提供了简洁的API和出色的性能,足以支撑生产环境的高并发服务。

基础HTTP服务

package main

import (
    "encoding/json"
    "net/http"
    "time"
)

func main() {
    // 注册路由
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/api/users", usersHandler)
    http.HandleFunc("/health", healthHandler)
    
    // 配置服务器
    server := &http.Server{
        Addr:         ":8080",
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }
    
    if err := server.ListenAndServe(); err != nil {
        panic(err)
    }
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Welcome!"))
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        getUsers(w, r)
    case http.MethodPost:
        createUser(w, r)
    default:
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "status": "healthy",
        "time":   time.Now().Format(time.RFC3339),
    })
}

使用http.ServeMux路由

func setupRoutes() *http.ServeMux {
    mux := http.NewServeMux()
    
    // 精确匹配
    mux.HandleFunc("/api/users", listUsers)
    mux.HandleFunc("/api/users/", getUser) // 带斜杠匹配子路径
    
    // 静态文件服务
    fs := http.FileServer(http.Dir("./static"))
    mux.Handle("/static/", http.StripPrefix("/static/", fs))
    
    return mux
}

// RESTful API示例
func getUser(w http.ResponseWriter, r *http.Request) {
    // 提取路径参数
    id := r.URL.Path[len("/api/users/"):]
    
    user, err := db.GetUser(id)
    if err != nil {
        if err == sql.ErrNoRows {
            http.Error(w, "User not found", http.StatusNotFound)
            return
        }
        http.Error(w, "Internal error", http.StatusInternalServerError)
        return
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

HTTP服务器配置最佳实践

  • ReadTimeout:防止慢读攻击,建议5-10秒
  • WriteTimeout:包括请求处理时间,根据业务调整
  • IdleTimeout:keep-alive连接空闲超时,建议60-120秒
  • MaxHeaderBytes:限制请求头大小,默认1MB

中间件模式

中间件是HTTP服务开发中的核心模式,用于实现横切关注点如日志、认证、限流等。

中间件基础实现

// Middleware 中间件类型
type Middleware func(http.Handler) http.Handler

// Chain 中间件链
func Chain(h http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}

// LoggingMiddleware 日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        
        // 包装ResponseWriter以捕获状态码
        wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
        
        next.ServeHTTP(wrapped, r)
        
        duration := time.Since(start)
        log.Printf("[%s] %s %s %d %v",
            r.Method,
            r.URL.Path,
            r.RemoteAddr,
            wrapped.statusCode,
            duration,
        )
    })
}

// responseWriter 包装http.ResponseWriter以捕获状态码
type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

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

// RecoveryMiddleware 恐慌恢复中间件
func RecoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Panic recovered: %v\n%s", err, debug.Stack())
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

// AuthMiddleware 认证中间件
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        
        user, err := validateToken(token)
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        
        // 将用户信息存入上下文
        ctx := context.WithValue(r.Context(), "user", user)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

// 使用中间件
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/data", dataHandler)
    
    // 应用中间件链
    handler := Chain(mux,
        RecoveryMiddleware,
        LoggingMiddleware,
        AuthMiddleware,
    )
    
    http.ListenAndServe(":8080", handler)
}

限流中间件

import "golang.org/x/time/rate"

// RateLimitMiddleware 令牌桶限流
type RateLimitMiddleware struct {
    limiter *rate.Limiter
}

func NewRateLimitMiddleware(rps int) *RateLimitMiddleware {
    return &RateLimitMiddleware{
        limiter: rate.NewLimiter(rate.Limit(rps), rps*2),
    }
}

func (rl *RateLimitMiddleware) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !rl.limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// 基于IP的限流
func IPRateLimitMiddleware(next http.Handler) http.Handler {
    limiters := make(map[string]*rate.Limiter)
    var mu sync.Mutex
    
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        
        mu.Lock()
        limiter, exists := limiters[ip]
        if !exists {
            limiter = rate.NewLimiter(rate.Limit(10), 20)
            limiters[ip] = limiter
        }
        mu.Unlock()
        
        if !limiter.Allow() {
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

高性能HTTP客户端

构建高性能服务不仅需要优化服务端,客户端配置同样重要。

优化HTTP客户端

// 创建优化的HTTP客户端
func NewOptimizedClient() *http.Client {
    return &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            // 连接池配置
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            MaxConnsPerHost:     100,
            
            // 空闲连接超时
            IdleConnTimeout: 90 * time.Second,
            
            // TLS握手超时
            TLSHandshakeTimeout: 10 * time.Second,
            
            // 继续传输超时
            ExpectContinueTimeout: 1 * time.Second,
            
            // 禁用压缩(如果自行处理)
            DisableCompression: false,
            
            // 连接复用
            DisableKeepAlives: false,
        },
    }
}

// 带重试的HTTP请求
func RequestWithRetry(client *http.Client, req *http.Request, maxRetries int) (*http.Response, error) {
    var resp *http.Response
    var err error
    
    backoff := 100 * time.Millisecond
    
    for i := 0; i <= maxRetries; i++ {
        resp, err = client.Do(req)
        if err == nil && resp.StatusCode < 500 {
            return resp, nil
        }
        
        if resp != nil {
            resp.Body.Close()
        }
        
        if i < maxRetries {
            time.Sleep(backoff)
            backoff *= 2 // 指数退避
        }
    }
    
    return nil, fmt.Errorf("max retries exceeded: %w", err)
}

连接池调优

// 针对高并发场景的客户端配置
func NewHighPerformanceClient() *http.Client {
    return &http.Client{
        Timeout: 10 * time.Second,
        Transport: &http.Transport{
            // 大量空闲连接应对突发流量
            MaxIdleConns:        500,
            MaxIdleConnsPerHost: 100,
            
            // 快速回收空闲连接
            IdleConnTimeout: 30 * time.Second,
            
            // 启用HTTP/2
            ForceAttemptHTTP2: true,
            
            // 自定义Dialer
            DialContext: (&net.Dialer{
                Timeout:   5 * time.Second,
                KeepAlive: 30 * time.Second,
            }).DialContext,
        },
    }
}

性能优化与监控

HTTP服务性能优化清单

优化项 配置/策略 预期效果
连接复用 启用Keep-Alive,合理设置IdleTimeout 减少TCP握手开销
压缩 启用Gzip压缩响应 减少传输数据量50-80%
超时设置 配置Read/Write/Idle Timeout 防止资源泄漏
连接池 调优MaxIdleConnsPerHost 提升客户端性能
请求体限制 限制MaxHeaderBytes和请求体大小 防止DoS攻击
优雅关闭 实现Server.Shutdown 零停机部署

优雅关闭实现

func gracefulShutdown(server *http.Server, timeout time.Duration) {
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    
    <-quit
    log.Println("Shutting down server...")
    
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        log.Printf("Server forced to shutdown: %v", err)
    }
    
    log.Println("Server exited")
}

func main() {
    server := &http.Server{
        Addr:    ":8080",
        Handler: setupRoutes(),
    }
    
    // 在goroutine中启动服务器
    go func() {
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()
    
    // 等待关闭信号
    gracefulShutdown(server, 30*time.Second)
}

生产环境注意事项

  • 不要暴露内部错误:向客户端返回通用错误消息,详细错误记录日志
  • 不要信任用户输入:严格验证和清理所有输入数据
  • 不要阻塞goroutine:长时间操作使用context控制超时
  • 使用结构化日志:便于日志聚合和分析
  • 暴露健康检查端点:便于负载均衡器和服务发现
  • 监控关键指标:QPS、延迟、错误率、资源使用

总结

Go语言为网络编程提供了从底层TCP到高层HTTP的完整解决方案。构建高性能HTTP服务的关键在于:

  • 理解并发模型:善用goroutine-per-connection模型,但要控制并发数量
  • 合理配置超时:各种超时参数是防止资源泄漏的第一道防线
  • 重视连接管理:连接池和Keep-Alive对性能影响巨大
  • 中间件架构:使用中间件模式解耦横切关注点
  • 可观测性:日志、指标、追踪是生产环境必备

Go的net/http包虽然简单,但通过合理的架构设计和配置调优,完全可以支撑大规模生产环境的流量。理解底层原理,遵循最佳实践,你就能构建出既高性能又易维护的网络服务。