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