VGrabko
@VGrabko
Golang, Php, Js

Почему так много памяти сьедает словарь?

У меня была простая задача. На страницах парсить емейлы. Для того что бы повторно не добавлять емейлы в базу я сделал два мапа

var storageUrl map[string]int
var storageEmail map[string]int

в них ключём выступает url или емейл, а значение всегда 0. Сейчас мап с урл имеет 2204 елемента, а с эмейлами 2289 елементов. Общая память 1075мб. Что можно сделать?

ОСТОРОЖНО! От моего кода из глаз течёт кровь.

main
spoiler
package main

import (
	"bufio"
	//"bytes"
	"fmt"
	//"log"
	"os"
	"parser/parse"
	"time"
)

var storageUrl map[string]int
var storageEmail map[string]int

func main() {
	storageUrl = make(map[string]int, 10240)
	storageEmail = make(map[string]int)
	queueLoadUrl := make(chan string)
	queueParseUrlHtml := make(chan string)
	//queueParseEmailHtml := make(chan string)

	queueStorageUrl := make(chan string)
	queueStorageEmail := make(chan string)

	defer func() {
		if r := recover(); r != nil {
			ReplacationFileSystem()
			os.Exit(1)
		}
	}()
	for i := 0; i < 10; i++ {
		go func() {
			for {
				msg := <-queueLoadUrl
				_, ok := storageUrl[msg]
				if !ok {
					queueStorageUrl <- msg
					queueParseUrlHtml <- parse.LoadUrl(msg)
				}
			}
		}()

		//эта рутина занята парсингом
		go func() {
			for {
				msg := <-queueParseUrlHtml
				go func() {
					//поиск емейлов.
					for _, value := range parse.EmailHtml(msg) {
						queueStorageEmail <- value
					}
					//парсим все ссылки и отдаём на загрузку.
					//что то вроде рекурсии
					for _, value := range parse.UrlHtml(msg) {
						queueLoadUrl <- value
					}
				}()
			}
		}()
	}

	//эта рутина обновляет данные в Storage
	go func() {
		for {
			select {
			case msg1 := <-queueStorageUrl:
				storageUrl[msg1] = 0
			case msg2 := <-queueStorageEmail:
				_, ok := storageEmail[msg2]
				if !ok {
					storageEmail[msg2] = 0
				}
			}
		}
	}()

	//рутина занята репликацией.

	reader := bufio.NewReader(os.Stdin)
	fmt.Print("Фраза или url: ")
	text, _ := reader.ReadString('\n')
	fmt.Println("Начал парсинг")
	queueLoadUrl <- text

	for {
		time.Sleep(25000 * time.Millisecond)
		ReplacationFileSystem()
	}

}

func ReplacationFileSystem() {
	file, _ := os.Create("email.txt")
	defer file.Close()
	var buf string
	for key, _ := range storageEmail {
		buf += key + "\n"
	}
	fmt.Println("Распарсенно страниц: ", len(storageUrl), "\nВсего найдено адресов: ", len(storageEmail))
	file.WriteString(buf)
}



parse
spoiler
package parse

import (
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"sync"

	"golang.org/x/net/html"
)

var Host string

func LoadUrl(urls string) string {
	u, err := url.Parse(strings.TrimSpace(urls))
	if err != nil {
		return ""
	}
	rw := &sync.RWMutex{}
	if u.Host == "" {
		rw.RLock()
		u.Host = Host
		rw.RUnlock()
	} else {
		rw.Lock()
		Host = u.Host
		rw.Unlock()
	}

	if u.Scheme == "" {
		u.Scheme = "http"
	}

	res, err := http.Get(u.String())
	if err != nil {
		return ""
	}
	d, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()
	if err != nil {
		return ""
	}
	return string(d)
}

func UrlHtml(s string) map[int]string {
	urls := map[int]string{}
	count := 0

	doc, err := html.Parse(strings.NewReader(s))
	if err != nil {
		log.Fatal(err)
	}
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key == "href" {
					urls[count] = a.Val
					count++
					break
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	return urls
}

func EmailHtml(str string) []string {
	r := regexp.MustCompile("([a-z0-9_\\.\\-]+)\\@(([a-z0-9\\-])+\\.)+([a-z]{2,6})")
	return r.FindAllString(str, -1)
}

  • Вопрос задан
  • 328 просмотров
Решения вопроса 3
bitver
@bitver
Возможно это оффтоп, а может и причина болезни, но Это просто жесть)
//parse
rw := &sync.RWMutex{}
  if u.Host == "" {
    rw.RLock()
    u.Host = Host
    rw.RUnlock()


А это нормально? С трудом понимаю что здесь, но кажется внутри замыкание лишнее.
go func() {
      for {
        msg := <-queueParseUrlHtml
        go func() { // <--------
Ответ написан
@arctblake
А зачем int использовать как value? Он ведь 4 или 8 байтов съедает в зависимости от системы. Используйте struct{} вместо int
Ответ написан
VGrabko
@VGrabko Автор вопроса
Golang, Php, Js
Переписал всё на мьютексах. Вместо каналов сделал очереди (из мапов) Не течёт) Я доволен как кот)
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
откройте для себя memcached или redis
оно будет, конечно, помедленней, но избавит от необходимости хранить кучу хрени в рентайме и потери данных при креше приложения
redis сохранит данные и в случае перезапуска сервака
Ответ написан
fastpars
@fastpars
Предполагаю что вы не закрыли Body в http.Response.

На вопрос "Почему не работает код?" предлагаю вам не стесняться и показывать этот самый код.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы