Maison  >  Article  >  développement back-end  >  fuite de mémoire de la fonction de rendu HTML

fuite de mémoire de la fonction de rendu HTML

王林
王林avant
2024-02-06 10:39:11671parcourir

html 渲染函数内存泄漏

Contenu de la question

Le problème auquel je suis confronté est que même en essayant seulement 200 requêtes, le programme occupe 6 Go de la mémoire du conteneur et finit par être tué par oom. Mon idée est d'extraire tous les nœuds de texte présents dans le HTML puis de les traiter pour extraire leur nom, le HTML et le texte de cette balise. Ainsi, pour générer du HTML pour une balise spécifique, j'utilise la fonction de rendu de golang.org/x/net/html. Où je fournis strings.builder en tant que io.writer pour écrire le code HTML généré. Mais pour une raison quelconque, le constructeur prend trop de mémoire.

package main

import (
    "encoding/csv"
    "io"
    "log"
    "net/http"
    "strings"
    "golang.org/x/net/html"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/data", GetData)
    if err := http.ListenAndServe(":8001", mux); err != nil {
        log.Println(err)
    }
}

type TagInfo struct {
    Tag  string
    Name string
    Text string
}

// http.handler
func GetData(w http.ResponseWriter, r *http.Request) {
    u := r.URL.Query().Get("url")
    doc, err := GetDoc(u)
    if err != nil {
        log.Println(err)
        w.WriteHeader(500)
        return
    }
    var buf strings.Builder
    data := Extract(doc, &buf)
    csvw := csv.NewWriter(io.Discard)
    for _, d := range data {
        csvw.Write([]string{d.Name, d.Tag, d.Text})
    }
}

// fires request and get text/html
func GetDoc(u string) (*html.Node, error) {
    res, err := http.Get(u)
    if err != nil {
        return nil, err
    }
    defer res.Body.Close()
    return html.Parse(res.Body)
}

func Extract(doc *html.Node, buf *strings.Builder) []TagInfo {
    var (
        tags = make([]TagInfo, 0, 100)
        f    func(*html.Node)
    )

    f = func(n *html.Node) {
        if n.Type == html.TextNode {
            text := strings.TrimSpace(n.Data)
            if text != "" {
                parent := n.Parent
                tag := Render(parent, buf)
                tagInfo := TagInfo{
                    Tag:  tag,
                    Name: parent.Data,
                    Text: n.Data,
                }
                tags = append(tags, tagInfo)
            }
        }
        for child := n.FirstChild; child != nil; child = child.NextSibling {
            f(child)
        }
    }
    f(doc)
    return tags
}

// Render the html around the tag
// if node is text then pass the
// parent node paramter in function
func Render(n *html.Node, buf *strings.Builder) string {
    defer buf.Reset()
    if err := html.Render(buf, n); err != nil {
        log.Println(err)
        return ""
    }
    return buf.String()
}

Si vous souhaitez une liste spécifique d'URL, la voici. J'ai fait environ 60 demandes à la fois.

J'ai essayé d'utiliser bytes.buffer bytes.buffer et sync.pool mais les deux ont le même problème. En utilisant pprof, j'ai remarqué que la méthode writestring de strings.builder provoquait une utilisation importante de la mémoire. <code>bytes.buffersync.pool 但两者都有相同的问题。使用 pprof 我注意到 strings.builder 的 writestring 方法导致大量内存使用。


正确答案


所以这里的基本问题是接受任何 content-type ,这在抓取方面是不可接受的,大多数网站都需要发送 text/html

Réponse correcte

Le problème fondamental ici est donc d'accepter tout type de contenu qui n'est pas acceptable en termes d'exploration, la plupart des sites Web ont tous besoin pour envoyer du text/html. golang.org/x/net/htmlLe problème est que même si l'

url envoie

tout ce qui ne représente pas des données HTML application/pdf ,然后正文将包含 html.Parse, elle l'accepte toujours sans générer d'erreur.

Prenons un exemple où les données binaires du pdf analysé sont renvoyées et aucune erreur n'est renvoyée, il s'agit d'une bibliothèque de pensée au comportement étrange pour le scraping/crawl acceptant les données binaires.

🎜La solution est la suivante : 🎜Vérifiez les en-têtes de réponse, si seules les données sont en HTML, puis continuez, sinon il y aura une ambiguïté ou une utilisation de la mémoire plus élevée (peut-être inférieure), mais nous ne pouvons pas prédire ce qui se passera. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer