go1.6文件方法WriteStringfrequent调用导致大型系统缓存

时间:2016-06-27 03:14:27

标签: linux go

go1.6文件方法WriteStringfrequent调用导致大型系统缓存。

如何解决这个问题。

go env:linux amd64。

这是Linux系统的问题吗?

代码:

package main

import (
    "fmt"
    "net/http"
    "os"
    "time"
)

var logCtxCh chan *http.Request
var accessLogFile *os.File

type HandlerHttp struct{}

func (this *HandlerHttp) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    sendAccessLog(req) 
    w.Write([]byte("Hello Word"))
}

func main() {
    s := &http.Server{
        Addr:    ":8012",
        Handler: &HandlerHttp{},
    }
    logCtxCh = make(chan *http.Request, 500)
    go startAcessLog()

   err:= s.ListenAndServe()
   fmt.Println(err.Error())
}

func startAcessLog() {
    for {
        select {
        case ctx := <-logCtxCh:
            handleAccessLog(ctx)
        }
    }
}

func sendAccessLog(req *http.Request) {
    logCtxCh <- req
}

func handleAccessLog(req *http.Request) {
    uri := req.RequestURI
    ip := req.RemoteAddr
    agent := req.UserAgent()
    refer := req.Referer()
    method := req.Method
    now := time.Now().Format("2006-01-02 15:04:05")

    logText := fmt.Sprintf("%s %s %s %s %s %s\n",
        now,
        ip,
        method,
        uri,
        agent,
        refer,
    )

    fileName := fmt.Sprintf("/data/logs/zyapi/access_zyapi%s.log",
        time.Now().Format("2006010215"),
    )
    writeLog(fileName, logText)
}

func writeLog(fileName, logText string) {
    var err error
    var exist = true

    if _, err = os.Stat(fileName); os.IsNotExist(err) {
        exist = false
    }

    if exist == false {
        if accessLogFile != nil {
            accessLogFile.Sync()
            accessLogFile.Close()
        }

        accessLogFile, err = os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
        if err == nil {
            _, err = accessLogFile.WriteString(logText)
        }
        if err != nil {
            fmt.Errorf(err.Error())
        }
    } else {
        if accessLogFile == nil {
            accessLogFile, err = os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND, 0666)
            if err != nil {
                fmt.Errorf(err.Error())
                return
            }
        }
        _, err = accessLogFile.WriteString(logText)
        if err != nil {
            fmt.Errorf(err.Error())
        }
    }
}

试验:

ab -n100000 -c10 -k "http://127.0.0.1:8012/"
ab -n100000 -c10 -k "http://127.0.0.1:8012/"
ab -n100000 -c10 -k "http://127.0.0.1:8012/"
ab -n100000 -c10 -k "http://127.0.0.1:8012/"
ab -n100000 -c10 -k "http://127.0.0.1:8012/"

运行几次后,系统文件缓存变得非常大

CONTAINER    CPU %   MEM USAGE/LIMIT   MEM %     NET I/O   BLOCK I/O

api_8011  38.47%    6.442GB/6.442GB   100.00%    0B/0B     0B/115.4MB 

api_8012  36.90%    6.442GB/6.442GB   99.99%     0B/0B     0B/115.6 MB

1 个答案:

答案 0 :(得分:0)

还有很多事情要发生,我无法立即发现错误,但这些事情会有所帮助:

  1. 如果您正在调用file.WriteString,请尝试尽可能多地使用bufio.Writer,否则每次写入都会成为系统调用,会影响性能。

  2. 您不需要在select函数中使用startAccessLog

    func startAcessLog() { for ctx := <-logCtxCh { handleAccessLog(ctx) } }

  3. 从以下位置更改错误检查:

    if err != nil { fmt.Errorf(err.Error()) }

    为:

    if err != nil { fmt.Println(err) }

    否则您不会打印错误。 fmt.Errorf格式化字符串,如fmt.Sprintf,并将其作为错误返回。它根本不打印任何东西。

  4. 您应该使用sync.Mutex保护accessLog或通过频道写信给它。为什么?因为有多个goroutine试图与accessLog合作,并且您不希望数据竞争发生。
    通过通道执行此操作会简化writeLog功能日志。目前很难遵循逻辑。我最初认为你没有正确关闭文件。