Рефакторинг кода привел к проблемам с взаимодействием структур и скоростью выполнения поставленных задач
я начал переписывать свой код из-за того, что в функциональном виде он начал терять читаемость. Основная цель моего кода заключается в следующем:
Есть процессор, у него есть отправитель адресов страниц, воркеры этого процессора читают эти адреса и стягивают с них необходимую информацию.
Код:
import (
"sync"
"path"
"github.com/PuerkitoBio/goquery"
"fmt"
"log"
)
type Shape struct {
Name, PathToImage string
}
type Champion struct {
Name, PathToDefaultSkin, Complexity, Info, Role string
Shapes []Shape
}
func collectChampion(page *goquery.Document) Champion {
var champion Champion
champion.Name = page.Find(".iLTyui span").Text()
return champion
}
type Worker struct {}
func (w *Worker) Work(tasks chan string, championsCh chan Champion) {
for requestURL := range tasks {
page, err := getGoqueryDocumentByURL(requestURL)
if err != nil { log.Fatal(err) }
championsCh <- collectChampion(page)
}
}
type Handler struct {
pendingChampions chan Champion
}
func (h *Handler) Handle() {
for champion := range h.pendingChampions {
fmt.Println(champion.Name)
}
}
type Processor struct {
Tasks chan string
Workers []Worker
Handler Handler
Sender func()
WG *sync.WaitGroup
}
func (p *Processor) init() {
p.Tasks = make(chan string)
p.Sender = func() {
document, err := getGoqueryDocumentByURL(MonitoringPage)
if err != nil { log.Fatal(err) }
document.Find(ChampionsSelector).Each(func(
index int, item *goquery.Selection) {
championPageURL, _ := item.Attr("href")
p.Tasks <- MonitoringPage + path.Base(championPageURL)
},
)
close(p.Tasks)
}
for i := 0; i < WORKERS; i++ {
p.Workers = append(p.Workers,
Worker{})
}
p.Handler = Handler{ pendingChampions: make(chan Champion) }
}
func (p *Processor) InformationProcessor() {
go p.Sender()
for _, worker := range p.Workers {
go worker.Work(p.Tasks,
p.Handler.pendingChampions)
}
go p.Handler.Handle()
for {}
}
На for {} в конце метода процессора обращать внимания не стоит, я знаю, что так поступать нельзя, просто еще WG не задействовал.
Вот чем меня смущает мое "нерукотворное произведение":
- Скорость получения данных уменьшилась несмотря на то, что читаемость слегка улучшилась.
- Структура воркера пуста из-за того, что если в ней объявить поле tasks, которое вместо процессора будет получать урлы, код будет работать некорректно.(вместо 150 необходимых объектов он может вернуть под 400, видимо это из-за того, что получение урлов происходит для каждого воркера. Скорость кода падает еще больше)
Вот о чем я:
import (
"sync"
"path"
"github.com/PuerkitoBio/goquery"
"fmt"
"log"
)
type Shape struct {
Name, PathToImage string
}
type Champion struct {
Name, PathToDefaultSkin, Complexity, Info, Role string
Shapes []Shape
}
func collectChampion(page *goquery.Document) Champion {
var champion Champion
champion.Name = page.Find(".iLTyui span").Text()
return champion
}
type Worker struct {
pendingTasks chan string // добавили поле
}
func (w *Worker) Work(championsCh chan Champion) {
for requestURL := range w.pendingTasks { // проходка по полученным таскам ВОРКЕРА, а не ПРОЦЕССОРА
page, err := getGoqueryDocumentByURL(requestURL)
if err != nil { log.Fatal(err) }
championsCh <- collectChampion(page)
}
}
type Handler struct {
pendingChampions chan Champion
}
func (h *Handler) Handle() {
for champion := range h.pendingChampions {
fmt.Println(champion.Name)
}
}
type Processor struct {
Workers []Worker
Handler Handler
Sender func(chan string)
WG *sync.WaitGroup
}
func (p *Processor) init() {
p.Sender = func(ch chan string) {
document, err := getGoqueryDocumentByURL(MonitoringPage)
if err != nil { log.Fatal(err) }
document.Find(ChampionsSelector).Each(func(
index int, item *goquery.Selection) {
championPageURL, _ := item.Attr("href")
ch <- MonitoringPage + path.Base(championPageURL) // возвращаем в канал полученный при вызове
},
)
close(ch)
}
for i := 0; i < WORKERS; i++ {
p.Workers = append(p.Workers,
Worker{ pendingTasks: make(chan string) })
}
p.Handler = Handler{ pendingChampions: make(chan Champion) }
}
func (p *Processor) InformationProcessor() {
for _, worker := range p.Workers {
go p.Sender(worker.pendingTasks) // вот скорее всего основная проблема,
go worker.Work(p.Handler.pendingChampions) // из-за которой падает корректность и производительность
}
// мы для каждого воркера повторно получаем адреса из сендера
go p.Handler.Handle()
for {}
}
Зная проблему я мог бы ее и сам пофиксить, но вот я не знаю как. Меня волнует, чтобы каждый воркер был задействован для скорости выполнения кода, но если так поступать то приходится для каждого вызывать сендер, а это приводит к некорректности. В другом случае структура воркера остается пустая, что тоже, как я полагаю, не круто, а таски записываются в процессор.