Go 语言网络编程实战
Go 语言网络编程实战
Go 很适合写网络程序。标准库已经提供 TCP、UDP、HTTP、超时、连接池等能力,很多服务不需要一上来就引入复杂框架。
这篇文章从 TCP echo server 开始,讲几个真实项目里必须注意的点。
一、最小 TCP 服务
1 | listener, err := net.Listen("tcp", "127.0.0.1:9000") |
每个连接用一个 goroutine 处理,这是 Go 网络编程最常见的写法。
二、处理连接
1 | func handleConn(conn net.Conn) { |
这个服务会把客户端发来的内容原样写回去。看起来简单,但已经包含网络服务的核心:连接、读、写、关闭、错误处理。
三、TCP 是字节流,不是消息流
不要假设一次 Read 就是一条完整消息。TCP 可能半包,也可能粘包。
如果你的协议是一行一条消息,可以用 bufio.Reader:
1 | reader := bufio.NewReader(conn) |
如果是二进制协议,通常会用固定长度头部记录 body 长度。核心原则是:协议必须有明确边界。
四、必须设置超时
没有超时的连接很容易被慢客户端拖住。可以设置读写 deadline:
1 | conn.SetReadDeadline(time.Now().Add(5 * time.Second)) |
写入也可以设置:
1 | conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) |
超时不是可选项。只要面对外部网络,就应该默认考虑。
五、HTTP 客户端不要裸用默认配置
很多人会直接写:
1 | resp, err := http.Get(url) |
这在 demo 里可以,生产代码最好显式配置超时:
1 | client := &http.Client{ |
如果是服务端调用下游接口,应该优先使用带 context 的请求:
1 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) |
这样请求取消、服务关闭、链路超时都能传递下去。
六、限制并发连接
goroutine 很轻量,但不是免费。连接过多时,要限制并发或使用连接池。
一个简单的限制方式:
1 | sem := make(chan struct{}, 1000) |
真实服务还需要配合监控,观察连接数、错误率、请求耗时和超时数量。
七、实践建议
Go 网络编程的重点不在“能不能连上”,而在连接生命周期是否可控。
协议边界、超时、取消、并发限制、错误日志,这几个点处理好,服务就不容易被异常网络拖垮。标准库已经足够强,先把这些基础写稳,比盲目堆框架更重要。