valyala/fasthttp

GitHub: valyala/fasthttp

一个为 Go 语言提供高性能 HTTP 服务端与客户端实现的库,通过零内存分配在热路径中实现远超标准库 net/http 的吞吐能力。

Stars: 23417 | Forks: 1840

# fasthttp [![Go Reference](https://pkg.go.dev/badge/github.com/valyala/fasthttp)](https://pkg.go.dev/github.com/valyala/fasthttp) [![Go Report](https://goreportcard.com/badge/github.com/valyala/fasthttp)](https://goreportcard.com/report/github.com/valyala/fasthttp) ![FastHTTP – Go 中最快且最可靠的 HTTP 实现](https://static.pigsec.cn/wp-content/uploads/repos/cas/70/70c371ce205db410d84ac374d4c25e04db3f29eab06293fe18a8fed3afe17db3.png) Go 的快速 HTTP 实现。 ## fasthttp 可能不适合你! fasthttp 是为一些高性能的边缘情况设计的。**除非**你的服务器/客户端需要处理**每秒数千次中小型请求**,并且需要稳定的低毫秒级响应时间,否则 fasthttp 可能不适合你。**对于大多数情况,`net/http` 要好得多**,因为它更易于使用并且能处理更多的情况。对于大多数情况,你甚至不会注意到性能差异。 ## 常规信息与链接 目前 fasthttp 已被 [VertaMedia](https://vertamedia.com/) 成功使用 在生产环境中,每台物理服务器从超过 150 万个并发 keep-alive 连接中提供高达 200K rps 的服务。 [TechEmpower 第 23 轮基准测试结果](https://www.techempower.com/benchmarks/#section=data-r23&hw=ph&test=plaintext) [服务器基准测试](#http-server-performance-comparison-with-nethttp) [客户端基准测试](#http-client-comparison-with-nethttp) [安装](#install) [文档](https://pkg.go.dev/github.com/valyala/fasthttp) [文档中的示例](https://pkg.go.dev/github.com/valyala/fasthttp#pkg-examples) [代码示例](examples) [优秀的 fasthttp 工具](https://github.com/fasthttp) [从 net/http 切换到 fasthttp](#switching-from-nethttp-to-fasthttp) [Fasthttp 最佳实践](#fasthttp-best-practices) [相关项目](#related-projects) [常见问题解答](#faq) ## HTTP 服务器性能与 [net/http](https://pkg.go.dev/net/http) 的比较 简而言之,fasthttp 服务器比 net/http 快达 6 倍。 以下是基准测试结果。 _GOMAXPROCS=1_ net/http 服务器: ``` $ GOMAXPROCS=1 go test -bench=NetHTTPServerGet -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkNetHTTPServerGet1ReqPerConn 722565 15327 ns/op 3258 B/op 36 allocs/op BenchmarkNetHTTPServerGet2ReqPerConn 990067 11533 ns/op 2817 B/op 28 allocs/op BenchmarkNetHTTPServerGet10ReqPerConn 1376821 8734 ns/op 2483 B/op 23 allocs/op BenchmarkNetHTTPServerGet10KReqPerConn 1691265 7151 ns/op 2385 B/op 21 allocs/op BenchmarkNetHTTPServerGet1ReqPerConn10KClients 643940 17152 ns/op 3529 B/op 36 allocs/op BenchmarkNetHTTPServerGet2ReqPerConn10KClients 868576 14010 ns/op 2826 B/op 28 allocs/op BenchmarkNetHTTPServerGet10ReqPerConn10KClients 1297398 9329 ns/op 2611 B/op 23 allocs/op BenchmarkNetHTTPServerGet100ReqPerConn10KClients 1467963 7902 ns/op 2450 B/op 21 allocs/op ``` fasthttp 服务器: ``` $ GOMAXPROCS=1 go test -bench=kServerGet -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkServerGet1ReqPerConn 4304683 2733 ns/op 0 B/op 0 allocs/op BenchmarkServerGet2ReqPerConn 5685157 2140 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10ReqPerConn 7659729 1550 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10KReqPerConn 8580660 1422 ns/op 0 B/op 0 allocs/op BenchmarkServerGet1ReqPerConn10KClients 4092148 3009 ns/op 0 B/op 0 allocs/op BenchmarkServerGet2ReqPerConn10KClients 5272755 2208 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10ReqPerConn10KClients 7566351 1546 ns/op 0 B/op 0 allocs/op BenchmarkServerGet100ReqPerConn10KClients 8369295 1418 ns/op 0 B/op 0 allocs/op ``` _GOMAXPROCS=4_ net/http 服务器: ``` $ GOMAXPROCS=4 go test -bench=NetHTTPServerGet -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkNetHTTPServerGet1ReqPerConn-4 2670654 4542 ns/op 3263 B/op 36 allocs/op BenchmarkNetHTTPServerGet2ReqPerConn-4 3376021 3559 ns/op 2823 B/op 28 allocs/op BenchmarkNetHTTPServerGet10ReqPerConn-4 4387959 2707 ns/op 2489 B/op 23 allocs/op BenchmarkNetHTTPServerGet10KReqPerConn-4 5412049 2179 ns/op 2386 B/op 21 allocs/op BenchmarkNetHTTPServerGet1ReqPerConn10KClients-4 2226048 5216 ns/op 3289 B/op 36 allocs/op BenchmarkNetHTTPServerGet2ReqPerConn10KClients-4 2989957 3982 ns/op 2839 B/op 28 allocs/op BenchmarkNetHTTPServerGet10ReqPerConn10KClients-4 4383570 2834 ns/op 2514 B/op 23 allocs/op BenchmarkNetHTTPServerGet100ReqPerConn10KClients-4 5315100 2394 ns/op 2419 B/op 21 allocs/op ``` fasthttp 服务器: ``` $ GOMAXPROCS=4 go test -bench=kServerGet -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkServerGet1ReqPerConn-4 7797037 1494 ns/op 0 B/op 0 allocs/op BenchmarkServerGet2ReqPerConn-4 13004892 963.7 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10ReqPerConn-4 22479348 522.6 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10KReqPerConn-4 25899390 451.4 ns/op 0 B/op 0 allocs/op BenchmarkServerGet1ReqPerConn10KClients-4 8421531 1469 ns/op 0 B/op 0 allocs/op BenchmarkServerGet2ReqPerConn10KClients-4 13426772 903.7 ns/op 0 B/op 0 allocs/op BenchmarkServerGet10ReqPerConn10KClients-4 21899584 513.5 ns/op 0 B/op 0 allocs/op BenchmarkServerGet100ReqPerConn10KClients-4 25291686 439.4 ns/op 0 B/op 0 allocs/op ``` ## HTTP 客户端与 net/http 的比较 简而言之,fasthttp 客户端比 net/http 快达 4 倍。 以下是基准测试结果。 _GOMAXPROCS=1_ net/http 客户端: ``` $ GOMAXPROCS=1 go test -bench='HTTPClient(Do|GetEndToEnd)' -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkNetHTTPClientDoFastServer 885637 13883 ns/op 3384 B/op 44 allocs/op BenchmarkNetHTTPClientGetEndToEnd1TCP 203875 55619 ns/op 6296 B/op 70 allocs/op BenchmarkNetHTTPClientGetEndToEnd10TCP 231290 54618 ns/op 6299 B/op 70 allocs/op BenchmarkNetHTTPClientGetEndToEnd100TCP 202879 58278 ns/op 6304 B/op 69 allocs/op BenchmarkNetHTTPClientGetEndToEnd1Inmemory 396764 26878 ns/op 6216 B/op 69 allocs/op BenchmarkNetHTTPClientGetEndToEnd10Inmemory 396422 28373 ns/op 6209 B/op 68 allocs/op BenchmarkNetHTTPClientGetEndToEnd100Inmemory 363976 33101 ns/op 6326 B/op 68 allocs/op BenchmarkNetHTTPClientGetEndToEnd1000Inmemory 208881 51725 ns/op 8298 B/op 84 allocs/op BenchmarkNetHTTPClientGetEndToEndWaitConn1Inmemory 237 50451765 ns/op 7474 B/op 79 allocs/op BenchmarkNetHTTPClientGetEndToEndWaitConn10Inmemory 237 50447244 ns/op 7434 B/op 77 allocs/op BenchmarkNetHTTPClientGetEndToEndWaitConn100Inmemory 238 50067993 ns/op 8639 B/op 82 allocs/op BenchmarkNetHTTPClientGetEndToEndWaitConn1000Inmemory 1366 7324990 ns/op 4064 B/op 44 allocs/op ``` fasthttp 客户端: ``` $ GOMAXPROCS=1 go test -bench='kClient(Do|GetEndToEnd)' -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkClientGetEndToEnd1TCP 406376 26558 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd10TCP 517425 23595 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd100TCP 474800 25153 ns/op 3 B/op 0 allocs/op BenchmarkClientGetEndToEnd1Inmemory 2563800 4827 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd10Inmemory 2460135 4805 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd100Inmemory 2520543 4846 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd1000Inmemory 2437015 4914 ns/op 2 B/op 0 allocs/op BenchmarkClientGetEndToEnd10KInmemory 2481050 5049 ns/op 9 B/op 0 allocs/op ``` _GOMAXPROCS=4_ net/http 客户端: ``` $ GOMAXPROCS=4 go test -bench='HTTPClient(Do|GetEndToEnd)' -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkNetHTTPClientGetEndToEnd1TCP-4 767133 16175 ns/op 6304 B/op 69 allocs/op BenchmarkNetHTTPClientGetEndToEnd10TCP-4 785198 15276 ns/op 6295 B/op 69 allocs/op BenchmarkNetHTTPClientGetEndToEnd100TCP-4 780464 15605 ns/op 6305 B/op 69 allocs/op BenchmarkNetHTTPClientGetEndToEnd1Inmemory-4 1356932 8772 ns/op 6220 B/op 68 allocs/op BenchmarkNetHTTPClientGetEndToEnd10Inmemory-4 1379245 8726 ns/op 6213 B/op 68 allocs/op BenchmarkNetHTTPClientGetEndToEnd100Inmemory-4 1119213 10294 ns/op 6418 B/op 68 allocs/op BenchmarkNetHTTPClientGetEndToEnd1000Inmemory-4 504194 31010 ns/op 17668 B/op 102 allocs/op ``` fasthttp 客户端: ``` $ GOMAXPROCS=4 go test -bench='kClient(Do|GetEndToEnd)' -benchmem -benchtime=10s cpu: Intel(R) Xeon(R) CPU @ 2.20GHz BenchmarkClientGetEndToEnd1TCP-4 1474552 8143 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd10TCP-4 1710270 7186 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd100TCP-4 1701672 6892 ns/op 4 B/op 0 allocs/op BenchmarkClientGetEndToEnd1Inmemory-4 6797713 1590 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd10Inmemory-4 6663642 1782 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd100Inmemory-4 6608209 1867 ns/op 0 B/op 0 allocs/op BenchmarkClientGetEndToEnd1000Inmemory-4 6254452 2645 ns/op 8 B/op 0 allocs/op BenchmarkClientGetEndToEnd10KInmemory-4 6944584 1966 ns/op 17 B/op 0 allocs/op ``` ## 安装 ``` go get -u github.com/valyala/fasthttp ``` ## 从 net/http 切换到 fasthttp 遗憾的是,fasthttp 没有提供与 net/http 完全相同的 API。 详情请参阅 [常见问题解答](#faq)。 虽然有 [net/http -> fasthttp 处理程序转换器](https://pkg.go.dev/github.com/valyala/fasthttp/fasthttpadaptor), 但最好还是手动编写 fasthttp 请求处理程序,以便利用 fasthttp 的所有优势(尤其是高性能 :) )。 重要事项: - Fasthttp 使用 [RequestHandler 函数](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 而不是实现了 [Handler 接口](https://pkg.go.dev/net/http#Handler)的对象。 幸运的是,很容易将绑定的结构体方法传递给 fasthttp: type MyHandler struct { foobar string } // net/http 风格的 request handler,即绑定到 MyHandler 结构体的方法。 func (h *MyHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) { // 注意,我们可以在这里访问 MyHandler 的属性 - 参见 h.foobar。 fmt.Fprintf(ctx, "Hello, world! Requested path is %q. Foobar is %q", ctx.Path(), h.foobar) } // fasthttp 风格的 request handler,即普通的函数。 func fastHTTPHandler(ctx *fasthttp.RequestCtx) { fmt.Fprintf(ctx, "Hi there! RequestURI is %q", ctx.RequestURI()) } // 将绑定的结构体方法传递给 fasthttp myHandler := &MyHandler{ foobar: "foobar", } fasthttp.ListenAndServe(":8080", myHandler.HandleFastHTTP) // 将普通函数传递给 fasthttp fasthttp.ListenAndServe(":8081", fastHTTPHandler) - [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 只接受一个参数 - [RequestCtx](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx)。 它包含了 HTTP 请求处理和响应写入所需的所有功能。下面是一个简单的请求处理程序从 net/http 转换为 fasthttp 的示例。 // net/http request handler requestHandler := func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/foo": fooHandler(w, r) case "/bar": barHandler(w, r) default: http.Error(w, "Unsupported path", http.StatusNotFound) } } // 对应的 fasthttp request handler requestHandler := func(ctx *fasthttp.RequestCtx) { switch string(ctx.Path()) { case "/foo": fooHandler(ctx) case "/bar": barHandler(ctx) default: ctx.Error("Unsupported path", fasthttp.StatusNotFound) } } - Fasthttp 允许以任意顺序设置响应头和写入响应体。 没有像 net/http 那样的“先写头,后写体”的限制。以下代码对 fasthttp 是有效的: requestHandler := func(ctx *fasthttp.RequestCtx) { // 首先设置一些 headers 和状态码 ctx.SetContentType("foo/bar") ctx.SetStatusCode(fasthttp.StatusOK) // 然后写入 body 的第一部分 fmt.Fprintf(ctx, "this is the first part of body\n") // 然后设置更多的 headers ctx.Response.Header.Set("Foo-Bar", "baz") // 然后写入更多的 body fmt.Fprintf(ctx, "this is the second part of body\n") // 然后覆盖已经写入的 body ctx.SetBody([]byte("this is completely new body contents")) // 然后更新状态码 ctx.SetStatusCode(fasthttp.StatusNotFound) // 基本上,在从 RequestHandler 返回之前,任何内容都可以被多次更新。 // // 与 net/http 不同,fasthttp 在从 RequestHandler 返回之前 // 不会将响应发送到网络上。 } - Fasthttp 没有提供 [ServeMux](https://pkg.go.dev/net/http#ServeMux), 但有支持 fasthttp 的更强大的第三方路由器和 Web 框架: - [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing) - [router](https://github.com/fasthttp/router) - [lu](https://github.com/vincentLiuxiang/lu) - [atreugo](https://github.com/savsgio/atreugo) - [Fiber](https://github.com/gofiber/fiber) - [Gearbox](https://github.com/gogearbox/gearbox) 带有简单 ServeMux 的 Net/http 代码可以轻松转换为 fasthttp 代码: // net/http 代码 m := &http.ServeMux{} m.HandleFunc("/foo", fooHandlerFunc) m.HandleFunc("/bar", barHandlerFunc) m.Handle("/baz", bazHandler) http.ListenAndServe(":80", m) // 对应的 fasthttp 代码 m := func(ctx *fasthttp.RequestCtx) { switch string(ctx.Path()) { case "/foo": fooHandlerFunc(ctx) case "/bar": barHandlerFunc(ctx) case "/baz": bazHandler.HandlerFunc(ctx) default: ctx.Error("not found", fasthttp.StatusNotFound) } } fasthttp.ListenAndServe(":80", m) - 因为为每个请求创建一个新的 channel 实在是太昂贵了,所以 RequestCtx.Done() 返回的 channel 只有在服务器关闭时才会被关闭。 func main() { fasthttp.ListenAndServe(":8080", fasthttp.TimeoutHandler(func(ctx *fasthttp.RequestCtx) { select { case <-ctx.Done(): // ctx.Done() 只有在服务器关闭时才会被关闭。 log.Println("context cancelled") return case <-time.After(10 * time.Second): log.Println("process finished ok") } }, time.Second*2, "timeout")) } - net/http -> fasthttp 转换表: - 以下所有伪代码假设 w、r 和 ctx 具有以下类型: var ( w http.ResponseWriter r *http.Request ctx *fasthttp.RequestCtx ) - [r.Body](https://pkg.go.dev/net/http#Request) **➜** [ctx.PostBody()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.PostBody) - [r.URL.Path](https://pkg.go.dev/net/url#URL) **➜** [ctx.Path()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Path) - [r.URL](https://pkg.go.dev/net/http#Request) **➜** [ctx.URI()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.URI) - [r.Method](https://pkg.go.dev/net/http#Request) **➜** [ctx.Method()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Method) - [r.Header](https://pkg.go.dev/net/http#Request) **➜** [ctx.Request.Header](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHeader) - [r.Header.Get()](https://pkg.go.dev/net/http#Header.Get) **➜** [ctx.Request.Header.Peek()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHeader.Peek) - [r.Host](https://pkg.go.dev/net/http#Request) **➜** [ctx.Host()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Host) - [r.Form](https://pkg.go.dev/net/http#Request) **➜** [ctx.QueryArgs()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.QueryArgs) + [ctx.PostArgs()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.PostArgs) - [r.PostForm](https://pkg.go.dev/net/http#Request) **➜** [ctx.PostArgs()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.PostArgs) - [r.FormValue()](https://pkg.go.dev/net/http#Request.FormValue) **➜** [ctx.FormValue()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.FormValue) - [r.FormFile()](https://pkg.go.dev/net/http#Request.FormFile) **➜** [ctx.FormFile()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.FormFile) - [r.MultipartForm](https://pkg.go.dev/net/http#Request) **➜** [ctx.MultipartForm()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.MultipartForm) 对于不受信任的 multipart 输入,请使用 [ctx.MultipartFormWithLimit()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.MultipartFormWithLimit)(或自定义的 [Server.FormValueFunc](https://pkg.go.dev/github.com/valyala/fasthttp#Server))来强制执行解析大小限制。 - [r.RemoteAddr](https://pkg.go.dev/net/http#Request) **➜** [ctx.RemoteAddr()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.RemoteAddr) - [r.RequestURI](https://pkg.go.dev/net/http#Request) **➜** [ctx.RequestURI()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.RequestURI) - [r.TLS](https://pkg.go.dev/net/http#Request) **➜** [ctx.IsTLS()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.IsTLS) - [r.Cookie()](https://pkg.go.dev/net/http#Request.Cookie) **➜** [ctx.Request.Header.Cookie()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHeader.Cookie) - [r.Referer()](https://pkg.go.dev/net/http#Request.Referer) **➜** [ctx.Referer()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Referer) - [r.UserAgent()](https://pkg.go.dev/net/http#Request.UserAgent) **➜** [ctx.UserAgent()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.UserAgent) - [w.Header()](https://pkg.go.dev/net/http#ResponseWriter) **➜** [ctx.Response.Header](https://pkg.go.dev/github.com/valyala/fasthttp#ResponseHeader) - [w.Header().Set()](https://pkg.go.dev/net/http#Header.Set) **➜** [ctx.Response.Header.Set()](https://pkg.go.dev/github.com/valyala/fasthttp#ResponseHeader.Set) - [w.Header().Set("Content-Type")](https://pkg.go.dev/net/http#Header.Set) **➜** [ctx.SetContentType()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.SetContentType) - [w.Header().Set("Set-Cookie")](https://pkg.go.dev/net/http#Header.Set) **➜** [ctx.Response.Header.SetCookie()](https://pkg.go.dev/github.com/valyala/fasthttp#ResponseHeader.SetCookie) - [w.Write()](https://pkg.go.dev/net/http#ResponseWriter) **➜** [ctx.Write()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Write), [ctx.SetBody()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.SetBody), [ctx.SetBodyStream()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.SetBodyStream), [ctx.SetBodyStreamWriter()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.SetBodyStreamWriter) - [w.WriteHeader()](https://pkg.go.dev/net/http#ResponseWriter) **➜** [ctx.SetStatusCode()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.SetStatusCode) - [w.(http.Hijacker).Hijack()](https://pkg.go.dev/net/http#Hijacker) **➜** [ctx.Hijack()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Hijack) - [http.Error()](https://pkg.go.dev/net/http#Error) **➜** [ctx.Error()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Error) - [http.FileServer()](https://pkg.go.dev/net/http#FileServer) **➜** [fasthttp.FSHandler()](https://pkg.go.dev/github.com/valyala/fasthttp#FSHandler), [fasthttp.FS](https://pkg.go.dev/github.com/valyala/fasthttp#FS) - [http.ServeFile()](https://pkg.go.dev/net/http#ServeFile) **➜** [fasthttp.ServeFile()](https://pkg.go.dev/github.com/valyala/fasthttp#ServeFile) - [http.Redirect()](https://pkg.go.dev/net/http#Redirect) **➜** [ctx.Redirect()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Redirect) - [http.NotFound()](https://pkg.go.dev/net/http#NotFound) **➜** [ctx.NotFound()](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.NotFound) - [http.StripPrefix()](https://pkg.go.dev/net/http#StripPrefix) **➜** [fasthttp.PathRewriteFunc](https://pkg.go.dev/github.com/valyala/fasthttp#PathRewriteFunc) - _非常重要!_ Fasthttp 不允许在从 [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 返回后保留对 [RequestCtx](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) 或其成员的引用。 否则必然会导致[数据竞争](http://go.dev/blog/race-detector)。 仔细检查所有从 net/http 转换为 fasthttp 的请求处理程序,看它们是否在返回后保留了对 RequestCtx 或其成员的引用。 RequestCtx 针对这种情况提供了以下_应急方案_: - 将 RequestHandler 包装到 [TimeoutHandler]() 中。 - 如果存在对 RequestCtx 或其成员的引用,在从 RequestHandler 返回之前调用 [TimeoutError](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.TimeoutError)。 有关更多详细信息,请参阅[示例](https://pkg.go.dev/github.com/valyala/fasthttp#example-RequestCtx-TimeoutError)。 使用这个出色的工具 - [竞争检测器](http://go.dev/blog/race-detector) - 来检测和消除程序中的数据竞争。如果你在程序中检测到与 fasthttp 相关的数据竞争,那么很有可能 你忘记在从 [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 返回之前调用 [TimeoutError](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.TimeoutError)。 - 盲目地从 net/http 切换到 fasthttp 不会给你带来性能提升。 虽然 fasthttp 针对速度进行了优化,但其性能可能很容易被缓慢的 [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 所饱和。 因此在切换到 fasthttp 之后,请[分析](http://go.dev/blog/pprof)并优化你的 代码。例如,使用 [quicktemplate](https://github.com/valyala/quicktemplate) 代替 [html/template](https://pkg.go.dev/html/template)。 - 另请参阅 [fasthttputil](https://pkg.go.dev/github.com/valyala/fasthttp/fasthttputil)、 [fasthttpadaptor](https://pkg.go.dev/github.com/valyala/fasthttp/fasthttpadaptor) 和 [expvarhandler](https://pkg.go.dev/github.com/valyala/fasthttp/expvarhandler)。 ## 针对多核系统的性能优化技巧 - 使用 [reuseport](https://pkg.go.dev/github.com/valyala/fasthttp/reuseport) 监听器。 - 每个 CPU 核心运行一个单独的服务器实例,设置 GOMAXPROCS=1。 - 使用 [taskset](http://linux.die.net/man/1/taskset) 将每个服务器实例绑定到单独的 CPU 核心。 - 确保多队列网卡的中断均匀分布在各个 CPU 核心之间。 详情请参阅[这篇文章](https://blog.cloudflare.com/how-to-achieve-low-latency/)。 - 使用最新版本的 Go,因为每个版本都包含性能改进。 ## Fasthttp 最佳实践 - 不要分配对象和 `[]byte` 缓冲区 - 尽可能地重用它们。Fasthttp API 设计鼓励这样做。 - [sync.Pool](https://pkg.go.dev/sync#Pool) 是你最好的朋友。 - 在生产环境中[分析你的程序](http://go.dev/blog/pprof)。 `go tool pprof --alloc_objects your-program mem.pprof` 通常比 `go tool pprof your-program cpu.pprof` 能提供更好的优化机会洞察。 - 为热点路径编写[测试和基准测试](https://pkg.go.dev/testing)。 - 避免在 `[]byte` 和 `string` 之间进行转换,因为这可能会导致内存 分配+拷贝 - 有关更多详细信息,请参阅[此维基页面](https://github.com/golang/go/wiki/CompilerOptimizations#string-and-byte)。 - 定期在[竞争检测器](https://go.dev/doc/articles/race_detector.html)下验证你的测试和生产代码。 - 在你的 Web 服务器中,优先使用 [quicktemplate](https://github.com/valyala/quicktemplate) 而不是 [html/template](https://pkg.go.dev/html/template)。 ## 不安全的零分配转换 在对性能要求极高的代码中,使用标准 Go 分配在 `[]byte` 和 `string` 之间进行转换可能效率低下。为了解决这个问题,`fasthttp` 使用了 **unsafe** 的零分配辅助函数: ### `UnsafeString(b []byte) string` 将 `[]byte` 转换为 `string`,**无需内存分配**。 ``` // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { // #nosec G103 return *(*string)(unsafe.Pointer(&b)) } ``` ### `UnsafeBytes(s string) []byte` 将 `string` 转换为 `[]byte`,**无需内存分配**。 ``` // UnsafeBytes returns a byte pointer without allocation. func UnsafeBytes(s string) []byte { // #nosec G103 return unsafe.Slice(unsafe.StringData(s), len(s)) } ``` ### 用例与注意事项 - 这些函数非常适合对性能敏感且必须避免分配的场景(例如,请求/响应处理循环)。 - 如果原始字符串仍在使用中,**请勿**修改从 `UnsafeBytes(s string)` 返回的 `[]byte`,因为字符串在 Go 中是不可变的,并且可能会在运行时被共享。 - 使用带有 `#nosec G103` 注释保护的示例代码,以抑制有关不安全操作的静态分析警告。 ## `[]byte` 缓冲区使用技巧 fasthttp 使用了以下技巧。在你的代码中也使用它们吧。 - 标准 Go 函数接受 nil 缓冲区 ``` var ( // both buffers are uninitialized dst []byte src []byte ) dst = append(dst, src...) // is legal if dst is nil and/or src is nil copy(dst, src) // is legal if dst is nil and/or src is nil (string(src) == "") // is true if src is nil (len(src) == 0) // is true if src is nil src = src[:0] // works like a charm with nil src // this for loop doesn't panic if src is nil for i, ch := range src { doSomething(i, ch) } ``` 所以请从你的代码中抛弃针对 `[]byte` 缓冲区的 nil 检查。例如, ``` srcLen := 0 if src != nil { srcLen = len(src) } ``` 变成了 ``` srcLen := len(src) ``` - 字符串可以通过 `append` 追加到 `[]byte` 缓冲区中 ``` dst = append(dst, "foobar"...) ``` - `[]byte` 缓冲区可以扩展到其容量大小。 ``` buf := make([]byte, 100) a := buf[:10] // len(a) == 10, cap(a) == 100. b := a[:100] // is valid, since cap(a) == 100. ``` - 所有 fasthttp 函数都接受 nil `[]byte` 缓冲区 ``` statusCode, body, err := fasthttp.Get(nil, "http://google.com/") uintBuf := fasthttp.AppendUint(nil, 1234) ``` - string 和 `[]byte` 缓冲区可以无需内存分配进行转换 ``` func b2s(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } func s2b(s string) (b []byte) { bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) bh.Data = sh.Data bh.Cap = sh.Len bh.Len = sh.Len return b } ``` ### 警告: 这是一种 **unsafe** 的方式,生成的 string 和 `[]byte` 缓冲区共享相同的字节。 **如果 string 仍然存活,请确保不要修改 `[]byte` 缓冲区中的字节!** ## 相关项目 - [fasthttp](https://github.com/fasthttp) - 为基于 fasthttp 的项目提供的各种有用的 辅助工具。 - [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing) - 适用于 fasthttp 服务器的快速且 功能强大的路由包。 - [http2](https://github.com/dgrr/http2) - fasthttp 的 HTTP/2 实现。 - [router](https://github.com/fasthttp/router) - 一个高 性能的 fasthttp 请求路由器,具有很好的扩展性。 - [fasthttp-auth](https://github.com/casbin/fasthttp-auth) - 使用 Casbin 的 fasthttp 授权中间件。 - [fastws](https://github.com/fasthttp/fastws) - 为 fasthttp 制作的精简 WebSocket 包, 用于并发处理读/写操作。 - [gramework](https://github.com/gramework/gramework) - 由 fasthttp 维护者之一开发的 Web 框架。 - [lu](https://github.com/vincentLiuxiang/lu) - 一个基于 fasthttp 的高性能 Go 中间件 Web 框架。 - [websocket](https://github.com/fasthttp/websocket) - 基于 Gorilla 的 fasthttp WebSocket 实现。 - [websocket](https://github.com/dgrr/websocket) - 基于事件的高性能 WebSocket 库,适用于零分配的 WebSocket 服务器和客户端。 - [fasthttpsession](https://github.com/phachon/fasthttpsession) - 用于 fasthttp 服务器的快速而强大的会话包。 - [atreugo](https://github.com/savsgio/atreugo) - 高性能且可扩展的微型 Web 框架,在热点路径中实现零内存分配。 - [kratgo](https://github.com/savsgio/kratgo) - 简单、轻量且超快的 HTTP Cache,可加速你的网站。 - [kit-plugins](https://github.com/wencan/kit-plugins/tree/master/transport/fasthttp) - fasthttp 的 go-kit transport 实现。 - [Fiber](https://github.com/gofiber/fiber) - 受 Expressjs 启发的、运行在 Fasthttp 之上的 Web 框架。 - [Gearbox](https://github.com/gogearbox/gearbox) - :gear: gearbox 是一个用 Go 编写的 Web 框架,专注于高性能和内存优化。 - [http2curl](https://github.com/li-jin-gou/http2curl) - 将 fasthttp 请求转换为 curl 命令行的工具。 - [OpenTelemetry Golang Compile Time Instrumentation](https://github.com/alibaba/opentelemetry-go-auto-instrumentation) - 无需更改任何代码即可使用 OpenTelemetry API 监控 fasthttp 应用的工具。 - [protoc-gen-httpgo](https://github.com/MUlt1mate/protoc-gen-httpgo) - 生成 fasthttp 服务器和客户端代码的 protoc 插件。 ## 常见问题解答 - _为什么要创建另一个 HTTP 包而不是优化 net/http?_ 因为 net/http API 限制了许多优化机会。 例如: - net/http Request 对象的生命周期不受请求处理程序执行 时间的限制。因此服务器必须为每个请求创建一个新的 Request 对象,而 不是像 fasthttp 那样重用现有对象。 - net/http headers 存储在 `map[string][]string` 中。因此服务器 必须解析所有的 headers,将它们从 `[]byte` 转换为 `string` 并在调用用户提供的请求处理程序之前将 它们放入 map 中。 所有这些都需要不必要的内存分配,而 fasthttp 避免了这些分配。 - net/http 客户端 API 要求为每个请求创建一个新的响应对象。 - _为什么 fasthttp API 与 net/http 不兼容?_ 因为 net/http API 限制了许多优化机会。请参阅 上面的答案了解更多详情。此外,某些 net/http API 部分在使用上 并不理想: - 比较 [net/http 连接劫持](https://pkg.go.dev/net/http#Hijacker) 和 [fasthttp 连接劫持](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Hijack)。 - 比较 [net/http Request.Body 读取](https://pkg.go.dev/net/http#Request) 和 [fasthttp request body 读取](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.PostBody)。 - _为什么 fasthttp 不支持 HTTP/2.0 和 WebSockets?_ [HTTP/2.0 支持](https://github.com/fasthttp/http2) 正在开发中。[WebSockets](https://github.com/fasthttp/websockets) 已经完成了。 第三方也可以使用 [RequestCtx.Hijack](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.Hijack) 来实现这些好东西。 - _与 fasthttp 相比,net/http 是否有已知的优势?_ 是的: - net/http 支持 [从 go1.6 开始的 HTTP/2.0](https://pkg.go.dev/golang.org/x/net/http2)。 - net/http API 是稳定的,而 fasthttp API 在不断演进。 - net/http 处理了更多的 HTTP 边界情况。 - net/http 可以流式传输请求和响应体 - net/http 可以处理更大的 body,因为它不需要将整个 body 读入内存 - net/http 应该包含更少的错误,因为它的使用和测试受众 要广泛得多。 - _为什么 fasthttp API 更倾向于返回 `[]byte` 而不是 `string`?_ 因为 `[]byte` 到 `string` 的转换并不是无开销的 - 它需要内存 分配和拷贝。如果你更喜欢使用字符串而不是字节切片,可以随意将返回的 `[]byte` 结果包装进 `string()`。 但请注意,这具有非零开销。 - _fasthttp 支持哪些 GO 版本?_ 我们支持 Go 团队支持的相同版本。 目前是 Go 1.25.x 及更新版本。 旧版本可能可以运行,但不会得到官方支持。 - _请提供真实的基准测试数据和服务器信息_ 请参阅[此 issue](https://github.com/valyala/fasthttp/issues/4)。 - _有计划向 fasthttp 添加请求路由吗?_ 没有计划向 fasthttp 添加请求路由。 请使用支持 fasthttp 的第三方路由器和 Web 框架: - [fasthttp-routing](https://github.com/qiangxue/fasthttp-routing) - [router](https://github.com/fasthttp/router) - [gramework](https://github.com/gramework/gramework) - [lu](https://github.com/vincentLiuxiang/lu) - [atreugo](https://github.com/savsgio/atreugo) - [Fiber](https://github.com/gofiber/fiber) - [Gearbox](https://github.com/gogearbox/gearbox) - _我在 fasthttp 中检测到了数据竞争!_ 太棒了![提交一个 bug](https://github.com/valyala/fasthttp/issues/new)。但在此之前, 请检查你的代码中是否存在以下问题: - 确保在从 [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 返回后没有对 [RequestCtx](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) 或其成员的引用。 - 确保在从 [RequestHandler](https://pkg.go.dev/github.com/valyala/fasthttp#RequestHandler) 返回之前调用了 [TimeoutError](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx.TimeoutError) 如果存在对 [RequestCtx](https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx) 或其成员的引用,且这些引用可能被其他 goroutine 访问。 - _我在这里没有找到我的问题的答案_ 尝试探索[这些问题](https://github.com/valyala/fasthttp/issues?q=label%3Aquestion)。
标签:Go, Ruby工具, SOC Prime, Syscall, Web开发, 开发工具, 日志审计, 网络库