文章

go httpfast

来自官方的简介

https://github.com/valyala/fasthttp

简而言之,fasthttp 服务器比 net/http 快高达 10 倍。 fasthttp 可能不适合你!fasthttp 是为一些高性能边缘情况而设计的。除非您的服务器/客户端需要每秒处理数千个中小型请求,并且需要一致的低毫秒响应时间,否则 fasthttp 可能不适合您。在大多数情况下,net/http它要好得多,因为它(net/http)更易于使用并且可以处理更多情况。在大多数情况下,您甚至不会注意到性能差异。

多核系统的优化技巧

  • 使用“可重用端口”监听器。通过允许多个进程或线程监听相同的端口,提高服务器对并发连接的处理能力,充分利用多核 CPU 的性能。
  • 每个 CPU 核心运行一个单独的服务器实例,设置 GOMAXPROCS=1(避免多个核心之间的锁竞争,提高单个实例的处理效率)。
  • 使用taskset将每个服务器实例固定到单独的 CPU 核心上,确保每个实例在独立的核心上运行,减少上下文切换的开销,提高缓存命中率。
  • 确保多队列网卡的中断在 CPU 核心之间均匀分布。有关详细信息,请参阅这篇文章
  • 使用最新版本的 Go,因为每个版本都包含性能改进。

fasthttp最佳实践

  • 不要分配对象和[]byte缓冲区 —— 尽可能重用它们。Fasthttp API 设计鼓励这样做。
  • sync.Pool是你最好的伙伴。

  • 在生产环境中分析你的程序 go tool pprof --alloc_objects your-program mem.pprof 通常比go tool pprof your-program cpu.pprof提供更好的优化机会洞察。
  • 为热门路径编写测试和基准测试
  • 避免在[]bytestring之间进行转换,因为这可能导致内存分配和复制。Fasthttp API 为[]bytestring都提供了函数 —— 使用这些函数而不是在[]bytestring之间手动转换。有一些例外情况 —— 查看这个维基页面以获取更多详细信息。
  • 定期在race detector下验证你的测试和生产代码。
  • 在Web服务中优先使用quicktemplate而不是html/template

[]byte缓冲区相关的技巧

以下技巧在fasthttp中使用:

  • 标准 Go 函数接收nil缓冲区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var (
	// 两个缓冲区都是未初始化状态
	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

// 这个 for 循环遍历切片 src 的每个元素,如果 src 为 nil,这个循环将不执行任何操作,直接跳过,不会引发程序错误。
for i, ch := range src {
	doSomething(i, ch)
}

因此,去除代码中对[]byte缓冲区的nil检查。例如:

1
2
3
4
srcLen := 0
if src != nil {
	srcLen = len(src)
}

变为

1
srcLen := len(src)
  • 字符串可以使用append追加到[]byte buffer中
1
dst = append(dst, "foobar"...)
  • []byte缓冲区可以扩展到其容量。
1
2
3
buf := make([]byte, 100)
a := buf[:10]  // len(a) == 10, cap(a) == 100.
b := a[:100]  // is valid, since cap(a) == 100.
  • 所有 fasthttp 函数都接受空的[]byte缓冲区
1
2
statusCode, body, err := fasthttp.Get(nil, "https://google.com/")
uintBuf := fasthttp.AppendUint(nil, 1234)
  • string[]byte buffer可以在不进行内存分配的情况下进行转换。
1
2
3
4
5
6
7
8
9
10
11
12
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
}

!!! 这是一种不安全的方式,结果的字符串和字节切片缓冲区共享相同的字节。“确保在字符串仍然存在的情况下不要修改字节切片缓冲区中的字节!

Good blog

fasthttp net/http对比 fasthttp:比net/http快十倍的Go框架(server 篇)

本文由作者按照 CC BY 4.0 进行授权

大伙爱看