linux - go1.6 File方法WriteString频繁调用导致系统缓存大

标签 linux go

go1.6 文件方法WriteString频繁调用导致系统缓存很大。

如何解决这个问题。

进入环境: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. 尝试使用bufio.Writer尽可能多地调用file.WriteString ,否则每次写入都将是系统调用,从而影响性能。

  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. 你应该守护accessLogsync.Mutex或者通过 channel 写入它。为什么?因为有不止一个 goroutine 试图与 accessLog 一起工作而且您不希望发生数据竞争。
    通过 channel 进行操作会简化您的 writeLog日志功能。目前很难遵循这个逻辑。我最初以为您没有正确关闭文件。

关于linux - go1.6 File方法WriteString频繁调用导致系统缓存大,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38045375/

相关文章:

linux - 如何杀死Linux中的特定进程?

android - 使用 shell 命令获取应用程序消耗的 Memory-RAM 信息

linux - 如何清除 SSH ProxyCommand 保持打开状态的连接?

function - 通过对象而不是指向它的指针调用带有指针接收器的方法?

go - 在 Golang 中将 int 数组转换为字节数组

Go:制作一个可从其他 Go 应用程序调用的守护进程

linux - 从另一个线程中止 read()

linux - 如何使用 dd Linux 命令从互联网下载文件?

go - 如何检查go文件中是否存在函数

去 float 比较