valyala/fasthttp
GitHub: valyala/fasthttp
一个为 Go 语言提供高性能 HTTP 服务端与客户端实现的库,通过零内存分配在热路径中实现远超标准库 net/http 的吞吐能力。
Stars: 23417 | Forks: 1840
# fasthttp
[](https://pkg.go.dev/github.com/valyala/fasthttp) [](https://goreportcard.com/report/github.com/valyala/fasthttp)

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 中检测到了数据竞争!_
太棒了。但在此之前,
请检查你的代码中是否存在以下问题:
- 确保在从 [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开发, 开发工具, 日志审计, 网络库