Performance 慢日志包
为什么GoPerformance 慢日志包,performance,http,go,logging,Performance,Http,Go,Logging,为什么Gologpackage会让我的http API慢得这么厉害?有那么慢吗 下面是我的路由器示例,使用而不使用日志记录: package main import ( "fmt" "log" "net/http" "time" "github.com/julienschmidt/httprouter" ) func main() { hand
log
package会让我的http API慢得这么厉害?有那么慢吗
下面是我的路由器示例,使用而不使用日志记录:
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
func main() {
handler := httprouter.New()
handler.GET("/hello", f)
http.ListenAndServe(fmt.Sprintf(":%d", 8080), handler)
}
func f(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "world")
}
使用基准测试该端点,我得到以下结果:
$wrk-t1-d1s-c100http://localhost:8080/hello
正在运行1s测试@http://localhost:8080/hello
1个螺纹和100个接头
线程统计平均标准偏差最大+/-标准偏差
延迟1.15ms 197.55us 2.84ms 80.02%
要求/秒84.58k 6.15k 99.01k 80.00%
83904个1.01秒请求,读取9.68MB
请求/秒:83380.37
传输/秒:9.62MB
当我为日志添加中间件时:
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/julienschmidt/httprouter"
)
func main() {
handler := httprouter.New()
handler.GET("/hello", logger(f))
fmt.Println("httprouter")
http.ListenAndServe(fmt.Sprintf(":%d", 8080), handler)
}
func logger(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
start := time.Now()
next(w, r, ps)
elapsed := time.Since(start)
log.Printf("%s | %s | %s | %d\n", time.Now().Format(time.RFC3339), r.Method, r.URL.Path, elapsed)
}
}
func f(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "world")
}
速度降低到4倍:
$ wrk -t1 -d1s -c100 http://localhost:8080/hello
Running 1s test @ http://localhost:8080/hello
1 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.25ms 4.34ms 26.47ms 60.23%
Req/Sec 20.51k 2.19k 24.28k 70.00%
20449 requests in 1.01s, 2.36MB read
Requests/sec: 20330.66
Transfer/sec: 2.35MB
我在本地测试了它:
MacBook Pro 13inches
2 GHz Quad-Core Intel Core i5
Memory 16GB
I use default Go max proc without modifying anything after installed.
log
程序包有那么慢吗?有什么改进的建议吗?这个答案总结了对这个问题的评论
- 使用缓冲io
- 从一个goroutine写入,以减少来自其他日志goroutine的阻塞
type writer chan []byte
func (w writer) Write(p []byte) (int, error) {
w <- append(([]byte)(nil), p...)
return len(p), nil
}
func writePump(w writer) {
bw := bufio.NewWriter(os.Stderr)
for p := range w {
bw.Write(p)
// Slurp up buffered messages in flush. This ensures
// timely output.
n := len(w)
for i := 0; i < n; i++ {
bw.Write(<-w)
}
bw.Flush()
}
}
默认情况下,日志包直接写入stderr。在缓冲写入程序中交换以提高性能:
bw:=buffo.NewWriter(os.Stderr);延迟bw.Flush();log.SetOutput(bw)
@velkor是正确的。使用缓冲写入程序。此外,您编写的中间件还做了大量额外的工作,如日志记录、time.Now
、time.Since
,等等,这些都是性能下降的原因。我认为您应该记录已用时间
而不是time.Now().Format(time.rfc339)
来获取写入响应所用的时间(持续时间)。@shmsr我认为调用time.Now()和time.Since不会使性能降低那么多。显然,它直接登录到os.Stderr。每个日志输出都与互斥锁同步。因此,如果您在任何地方都有相同的互斥,那么您必须在每次登录时获取它。想象一下这对你的工作有什么影响concurrency@WhitePanther我的意思是使用一个go例程来记录在缓冲通道上循环的日志,这样日志函数基本上会将日志推送到通道,并且不会对API响应时间造成瓶颈;这个imo应该有用。你能详细说明为什么我们需要额外的“咕噜…”吗?
w := make(writer, 16) // adjust capacity to meet your needs
go writePump(w)
log.SetOutput(w)