当我尝试使用Echo(也包括Gin)发送大型数组时,我遇到了内存问题。 请求后,内存将不可用。
package main
import (
"net/http"
"strconv"
"github.com/labstack/echo"
)
type User struct {
Username string
Password string
Lastname string
Firstname string
}
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
var user User
users := make([]User, 0)
for i := 0; i < 100000; i++ {
user = User{
Username: "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),
Password: "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhftd",
Lastname: "njuftydfhgjkjlkjlkjlkhjkhu",
Firstname: "jkggkjkl,,lm,kljkvgf"}
users = append(users, user)
}
defer func() {
users = nil
}()
return c.JSON(http.StatusOK, users)
})
e.Logger.Fatal(e.Start(":1323"))
}
要测试,我并行运行请求,结果如下:
如何减少内存消耗?
答案 0 :(得分:1)
分配的内存不会立即返回操作系统,请参见Cannot free memory once occupied by bytes.Buffer;和Freeing unused memory?
您的答案没有什么改善,因为您仍在内存中构建(Go)数组(或更确切地说是切片),一旦完成,就只能将其编组到响应中。您还为每个项目创建一个新的编码器,将一个项目编组,然后将其丢弃。您可以使用json.Encoder
封送多个项目。您还需要在每个项目之后刷新响应,这也非常低效。这违反了所有内部缓冲的目的...
相反,您可以在项目(User
)准备好后立即封送它们,因此您不必将所有内容都保留在内存中。并且不要在每个用户之后都刷新一次,这足以在最后执行一次,这是不必要的,因为从处理程序返回后,服务器将刷新所有缓冲的数据。
执行以下操作:
e.GET("/", func(c echo.Context) error {
c.Response().WriteHeader(http.StatusOK)
enc := json.NewEncoder(c.Response())
for i := 0; i < 100000; i++ {
user := User{
Username: "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),
Password: "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhfd",
Lastname: "njuftydfhgjkjlkjlkjlkhjkhu",
Firstname: "jkggkjkl,,lm,kljkvgf",
}
if err := enc.Encode(user); err != nil {
return err
}
}
return nil
})
这里要注意的一件事:上面的代码没有将JSON数组发送到输出,而是发送了一系列JSON对象。如果这不适合您,并且您确实需要发送单个JSON数组,则只需对数据进行“框架化”,然后在项目之间插入逗号:
e.GET("/", func(c echo.Context) error {
resp := c.Response()
resp.WriteHeader(http.StatusOK)
if _, err := io.WriteString(resp, "["); err != nil {
return err
}
enc := json.NewEncoder(resp)
for i := 0; i < 100000; i++ {
if i > 0 {
if _, err := io.WriteString(resp, ","); err != nil {
return err
}
}
user := User{
Username: "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),
Password: "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhft",
Lastname: "njuftydfhgjkjlkjlkjlkhjkhu",
Firstname: "jkggkjkl,,lm,kljkvgf",
}
if err := enc.Encode(user); err != nil {
return err
}
}
if _, err := io.WriteString(resp, "]"); err != nil {
return err
}
return nil
})
答案 1 :(得分:0)
我更改为使用@icza的解决方案。
代码在这里:
package main
import (
"encoding/json"
"net/http"
"strconv"
"github.com/labstack/echo"
)
type User struct {
Username string
Password string
Lastname string
Firstname string
}
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
var user User
users := make([]User, 0)
for i := 0; i < 100000; i++ {
user = User{
Username: "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),
Password: "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhftd",
Lastname: "njuftydfhgjkjlkjlkjlkhjkhu",
Firstname: "jkggkjkl,,lm,kljkvgf"}
users = append(users, user)
}
c.Response().WriteHeader(http.StatusOK)
for _, user := range users {
if err := json.NewEncoder(c.Response()).Encode(user); err != nil {
return err
}
c.Response().Flush()
}
return nil
})
e.Logger.Fatal(e.Start(":1323"))
}
这样做,我的进步很小。我从3.5GB转到2GB。但是每次发送请求时,使用的内存都会增加。
为什么在请求处理后GC无法释放内存?