#go
#Вперед
Вопрос:
Описание проблемы
Я хочу отправлять одновременный вызов для каждой итерации в моем цикле for.
Текущий код
Эта программа в основном пытается найти действительный каталог на сервере
package main
import (
"bufio"
"fmt"
"net/http"
"os"
)
func main() {
var site string
if len(os.Args) > 1 {
site = os.Args[1]
} else {
fmt.Println("Need the host. Example > http://www.google.com/")
fmt.Println("wfuzz http://www.google.com/ wordlist.txt")
site = "http://www.google.com"
os.Exit(1)
}
f, err := os.Open(os.Args[2])
if err != nil {
fmt.Println("error opening file!", err)
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
a := site scanner.Text()
get_page(a)
}
if err := scanner.Err(); err != nil {
fmt.Println("error", err)
}
var input string
fmt.Scanln(amp;input)
fmt.Println("done")
}
func get_page(site string) string {
a := "n"
res, err := http.Get(site)
if err != nil {
fmt.Println("error:", err)
}
if res.StatusCode != 404 {
fmt.Println("Page Found!! ::", site, " :: ", res.StatusCode)
}
return a
}
Цель для достижения
Чего я хочу добиться, так это в основном отправлять параллельные вызовы цикла for, чтобы таким образом ускорить процесс и стать конечной целью текущей программы.
Ответ №1:
Используя комбинацию стандартных библиотек sync.WaitGroup
и каналов Go, вы можете выполнить это без необходимости явно отслеживать количество слов в файле, из которого вы хотите прочитать.
Измените свой get_page
(примечание: это не идиоматический переход к подчеркиванию пользователем в именах функций -> https://golang.org/doc/effective_go.html#names ) func, чтобы принять дополнительный канал и группу ожидания, и вы могли бы сделать что-то вроде следующего:
package main
import (
"bufio"
"fmt"
"net/http"
"os"
"sync"
)
func main() {
//your previous code for getting the command line arguments from os.Args, then...
//create a WaitGroup
wg := new(sync.WaitGroup)
//create a channel that you can write your results too
resultChan := make(chan string)
scanner := bufio.NewScanner(f)
for scanner.Scan() {
//increment the waitgroup
wg.Add(1)
a := site scanner.Text()
//call the getPage with the http target, with the result chan, and the waitgroup
go getPage(a, resultChan, wg)
}
if err := scanner.Err(); err != nil {
fmt.Println("error", err)
//if there is an error, close the result chan so progam can exit (for more robust cancellation of potential currently running go routines, take a look at https://blog.golang.org/pipelines)
close(resultChan)
}
//start another go routine to wait for all concurrent calls to get_page to complete, then close the result channel
go func() {
wg.Wait()
close(resultChan)
}()
for result := range resultChan {
//deal with the result in some way
fmt.Println(result)
}
fmt.Println("done")
}
//side note - it is not idiomatic go to use underscores in func name
func getPage(site string, ch chan<- string, wg *sync.WaitGroup) {
//decrement waitgroup when operation completes
defer wg.Done()
res, err := http.Get(site)
if err != nil {
ch <- fmt.Sprintf("error: %s", err)
return
}
if res.StatusCode != 404 {
ch <- fmt.Sprintf("Page Found!! :: %s :: %d", site, res.StatusCode)
}
}
Ответ №2:
Вы можете использовать
sync.WaitGroup
Единственная проблема заключается в том, сколько одновременных вызовов вы собираетесь выполнять.
пример
если wordlist.txt содержание этого
admin.php
admin
administrator
administrator
вы знаете, что будете выполнять 4 одновременных вызова
если содержимое
admin.php
admin/
administrator/
administrator/
administrator/
administrator/
вы знаете, что будете выполнять 6 одновременных вызовов.
Приведенный ниже код был создан с файлом из 3 слов
package main
import (
"fmt"
"sync"
"bufio"
"net/http"
"os"
)
func main(){
var site string
var wg sync.WaitGroup
if len(os.Args) > 1 {
site = os.Args[1]
} else {
fmt.Println("Need the host. Example > http://www.google.com/")
fmt.Println("wfuzz http://www.google.com/ wordlist.txt")
site = "http://www.google.com"
os.Exit(1)
}
f, err := os.Open(os.Args[2])
if err != nil {
fmt.Println("error opening file!", err)
}
wg.Add(3) // TODO check the number of words in the file
scanner := bufio.NewScanner(f)
for scanner.Scan() {
a := site scanner.Text()
go get_page(a,amp;wg)
}
if err := scanner.Err(); err != nil {
fmt.Println("error", err)
}
wg.Wait()
fmt.Println("done")
}
func get_page(site string,wg *sync.WaitGroup) string{
a := "n"
defer wg.Done()
res, err := http.Get(site)
if err != nil {
fmt.Println("error:", err)
}
if res.StatusCode != 404 {
fmt.Println("Page Found!! ::", site, " :: ", res.StatusCode)
}
return a
}
Комментарии:
1. Да, возникла проблема с параллелизмом, которой я раньше не испытывал… когда у нас слишком много подключений к веб-сайту, веб-сервер иногда отказывается от большего количества подключений, чем определенное количество, или даже брандмауэр отключает соединения, предоставляя ложную информацию о доступных ресурсах на сервере, но спасибо, что ваш код работает в моей лаборатории 🙂
Ответ №3:
Вы можете использовать go get_page(a)
, который запускает подпрограмму для каждого слова в списке слов, чтобы установить максимальное количество одновременных вызовов, использующих рабочий пул. (https://gobyexample.com/worker-pools )
Комментарии:
1. проблема в том, что если я использую go get_page(a), что произойдет, так это то, что моя основная функция завершится до завершения цикла, другими словами, вывод только с go get_page (a) равен нулю.
2. вы можете решить это с помощью каналов, пожалуйста, ознакомьтесь с приведенным примером. (Часть с «Наконец, мы собираем все результаты работы». описание ожидает, пока все рабочие не будут выполнены)