fasthttp 号称比 net/http 要快 10 倍左右,之前使用 fasthttp 写过也写过一些项目,这里试图刨析一下 fasthttp 高性能的原因
fasthttp介绍
github: https://github.com/valyala/fasthttp
这里先翻译一下官网的介绍
FastHTTP最佳实践
- 不要分配 object 和 []byte 缓冲区, 要尽可能地重用它们。FastHTTP API设计鼓励这样做。
- sync.Pool 使用对象池
- 在项目中开启 go tool pprof –alloc_objects your-program mem.ppro 通常会比 go tool pprof your-program cpu.pprof 更好
- 写 tests 和 benchmarks
- 避免在 []byte 和 string 之间进行转换,因为这可能导致 内存分配+复制 。FastHTTP API同时为[]byte 和 string 提供函数,使用这些函数,而不是在[]byte 和 string 之间手动转换。wiki: https://github.com/golang/go/wiki/CompilerOptimizations#string-and-byte
- 在并发的环境下测试代码
合理使用 []byte 字节缓冲区
为什么还要创建另一个 HTTP 包而不是优化 net/http?
因为 net/http api 限制了许多优化机会。例如:
- net/http 请求对象生存期不受请求处理程序执行时间的限制。因此,服务器必须为每个请求创建一个新的请求对象,而不是像 fasthttp 那样重用现有对象。
- net/http api 要求每个请求创建一个新的响应对象。
- net/http 头存储在 map[string][] 字符串中。因此,在调用用户提供的请求处理程序之前,服务器必须解析所有头,将它们从 []byte 转换为 string,并将它们放入映射中。这都需要 fasthttp 避免不必要的内存分配。
为什么 fasthttp api 与 net/http 不兼容?
因为 net/http API 限制了许多优化机会。请参阅上面的答案了解更多详细信息。此外,某些 net/http API部件的使用还不理想:
比较 net/http connection hijacking 与 fasthttp connection hijacking.
比较 net/http Request.Body reading t与 fasthttp request body reading.
与 fasthttp 相比,net/http有什么优势?
- net/http 支持从 GO1.6 开始支持 HTTP/2.0
- net/http API是稳定的,而 FastHTTP API 不断发展
- net/http 处理更多的 http 情况
- net/http 应该包含更少的bug,因为它被更广泛的受众使用和测试。
- net/http 支持 go1.5 的旧版本
为什么 fastHttp API 返回 []byte 而不是 string?
因为 []byte 到 string 的转换需要内存重分配和复制
fasthttp 支持哪些 go 版本?
go1.5+
优势总结
根据作者的解释,fasthttp 对性能提高的优化点主要在于:
- 协程池
- 对象的复用
- 减少 []byte 到 string 的转化
源码分析
接下来我们从源码来分析一下 fasthttp 究竟做了哪些优化
协程池
go 官方原生的 server.go1
2
3
4
5
6
7
8
9l := listen()
func (srv *Server) Serve(l net.Listener) error {
for {
rw, err := l.accept()
....
// 处理请求, 每次都开启一个 gorotine
go c.serve(ctx)
}
}
fasthttp 的 Serve1
2
3
4
5
6
7
8
9
10
11
12func (s *Server) Serve(ln net.Listener) error {
for {
if c, err = acceptConn(s, ln, &lastPerIPErrorTime); err != nil {
......
}
// 对应 go c.serve(ctx)
if !wp.Serve(c) {
//......
}
//......
}
}
很明显,go 原生的 net/http 中,当接受到新的请求后就启动一个新的斜程,而 fasthttp 使用斜程池处理