LINUX.ORG.RU

зачем второй цикл?

 


0

1
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	// Задаем максимальный размер канала
	sem := make(chan bool, 100)
	for i := 1; i <= 1000; i++ {
		sem <- true
		go func(id int) {
			resp, err := http.Get(fmt.Sprintf("http://localhost:5000/request-%d", id))
			if err != nil {
				log.Fatal(err)
			}
			defer resp.Body.Close()
			bytes, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("%s\n", bytes)
			<-sem
		}(i)
	}
        // Вот этот. Все зависает пока канал не будет заполнен?
	for i := 0; i < cap(sem); i++ {
		sem <- true
	}
}

Решил вернуться к изучению go после трех недель перерыва. Общий стаж «изучения» дней пять.


Решил вернуться к изучению go после трех недель перерыва. Общий стаж «изучения» дней пять.

У меня так с вязанием. Общий стаж — 2 часа, период обучения — с 1 класса школы по сей день, последний перерыв лет 20.

Virtuos86 ★★★★★ ()
Ответ на: комментарий от tz4678

А это и было по существу. Если у тебя паузы будут превышать время, которое ты тратишь на изучение, выучишь ты ровным счётом ничего. А сам Go я не знаю, подсказать не могу, хотя код выглядит примитивным, и непонятно, почему ты в нем не можешь разобраться, если целых пять дней уже ковыряешь доки.

Virtuos86 ★★★★★ ()
Последнее исправление: Virtuos86 (всего исправлений: 1)

чего ты пытаешься добиться этим кодом? Если просто параллельного исполнения запросов, то тут нужен sync.WaitGroup, а не каналы (хотя с ними тоже можно решить задачу)

anonymous ()

зачем второй цикл?
способ не завершать main процесс пока не будут отправлены 1000 get запросов.
дойдя до i = 1000 в первом цикле (завершив цикл)
надо время что бы отправленные ответы будут обработаны <-sem
т.к размер канала 100. второй цикл сможет отправлять
только когда отработает очередная go func(id int)
с чтением из <-sem
Канал заполнен второй цикл не сможет отправить пока из него не считают.

NimoLime ()
Ответ на: комментарий от tz4678

если неизвестно общее количество запросов

на каждую созданую горутину добавлять в waitgroup единицу. Внутри горутины вызывать wg.Done(), тогда единица уйдет из ожидания.

Dred ★★★★★ ()
Последнее исправление: Dred (всего исправлений: 1)
Ответ на: комментарий от beastie

В общем, как я понимаю, ты пытаешься добиться что-то подобное:

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"sync"
)

type response struct {
	id   int
	body []byte
	err  error
}

func worker(ids <-chan int, resp chan<- response) {
	for id := range ids {
		body, err := requestId(id)
		resp <- response{id: id, body: body, err: err}
	}
}

func requestId(id int) ([]byte, error) {
	url := fmt.Sprintf("http://localhost:5000/request-%d", id)
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

func feed(ids chan<- int, max int) {
	defer close(ids)
	for i := 0; i < max; i++ {
		ids <- i
	}
}

func report(resp <-chan response) {
	for r := range resp {
		if r.err != nil {
			log.Println("error:", r.err)
			continue
		}
		log.Println(r.id, "→", string(r.body))
	}
}

func main() {
	// define and parse cmd args
	var (
		nWorker   = flag.Int("worker", runtime.NumCPU()*2+1, "number of worker")
		nRequests = flag.Int("n", 1000, "number of requests")
	)
	flag.Parse()

	var (
		in  = make(chan int)
		out = make(chan response)
		wg  sync.WaitGroup
	)

	// spawn N worker
	for i := 0; i < *nWorker; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			worker(in, out)
		}()
	}

	// wait for all worker to finish and close response channel
	go func() {
		wg.Wait()
		close(out)
	}()

	// feed worker
	go feed(in, *nRequests)

	// report responses
	report(out)
}
beastie ★★★★★ ()
Ответ на: комментарий от beastie

Мне просто нужно пару миллионов запросов сделать. Важно ограничить количество одновременных соединений. А этот код неправильный?

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
)

func main() {
	// Задаем максимальный размер канала
	sem := make(chan interface{}, 100)
	wg := new(sync.WaitGroup)
	for i := 1; i <= 1000; i++ {
		wg.Add(1)
		sem <- nil
		go func(id int) {
			resp, err := http.Get(fmt.Sprintf("http://localhost:5000/%d", id))
			if err != nil {
				log.Fatal(err)
			}
			defer resp.Body.Close()
			bytes, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				log.Fatal(err)
			}
			fmt.Printf("%s", bytes)
			wg.Done()
			<-sem
		}(i)
	}
	wg.Wait()
}

Проверял на таком бекенде:

#!/usr/bin/env python
import random
import time

from flask import Flask

app = Flask(__name__)
@app.route('/<int:id>')
def index(id):
  time.sleep(random.uniform(0.1, 0.5))
  return f'Request #{id}\n'
if __name__ == '__main__':
  app.run()

Вывод:

...
Request #1000
Request #917
Request #908
Request #915
Request #949
Request #924
Request #969
Request #930
Request #997
Request #928
Request #991
Request #936
Request #905
Request #914
Request #978
Request #906
Request #953
Request #922
Request #943
Request #976
Request #962
Request #989
Request #944
Request #938
Request #945
Request #937
Request #992
Request #980
Request #964
Request #994
Request #993
Request #929
Request #986
Request #934
Request #948
Request #965
Request #935
Request #959
Request #974
Request #952
Request #983
Request #988
Request #981
Request #977
Request #990
Request #996
Request #999

tz4678 ()
Ответ на: комментарий от tz4678

тот код делает тоже самое. в точности. я не фанат нодовского подхода с адом зависимостей из библиотек по 10 строчек, но спасибо.

На написание этих условных 10 строчек качественно (а это значит с документацией и тестами) иногда уходит целый день. Если твой день не стоит одного go get, то стоит задуматься.

Deleted ()
Ответ на: комментарий от tz4678

Оно вообще не имеет смысла. Всё что ты там настрочил, можно уместить в простой loop, без всяких go-routines и channels (которые там вообще ни к месту). (Что в прочем оно и делает, но более марсиански). См. пример, который я тебе привёл и пляши от туда. Оно как раз создаёт N одновмременных worker и кормит их данными.

beastie ★★★★★ ()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от beastie

вот смотри:

package main

import (
	"crypto/tls"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sync"
)

func fetch(url string, ch chan interface{}, wg *sync.WaitGroup) {
	ch <- nil
	wg.Add(1)
	resp, err := http.Get(url)
	if err != nil {
		log.Println(err)
	} else {
		defer resp.Body.Close()
		server := resp.Header.Get("server")
		fmt.Printf("%s => %s\n", url, server)
	}
	<-ch
	wg.Done()
}

func main() {
	ch := make(chan interface{}, 10)
	wg := new(sync.WaitGroup)
	// Игнорируем SSL ошибки
	http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	csvFile, _ := os.Open("top-1m.csv")
	reader := csv.NewReader(csvFile)
	for i := 0; i < 10; i++ {
		row, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		} else {
			go fetch("http://"+row[1], ch, wg)
			go fetch("https://"+row[1], ch, wg)
		}
	}
	wg.Wait()
}

Есть файл с базой алексы. Это миллион самых популярных сайтов в мире. Я для примера беру 10 сайтов из списка. Там только доменные имена (протоколов нет), поэтому для проверки одного сайта нужно его запросить по хттп и хттпс. Ну тут для примера я вывожу имя серверного ПО, используемого сайтом. У меня цель так-то другая. Я хочу проверить топовые сайты на уязвимости. У меня вызывает сомнения пригоден ли для этой задачи GO, потому как много всяких рекламных заявлений про 100500 запросов в секунду, а на деле... НЕ повторится ли история с Python, его говнолиба aiohttp вообще непригодной оказалась. Я видел сишную утилиту, которая за 5 минут может просканитть весь инет, но можно ли на GO подобное писать?

tz4678 ()
Ответ на: комментарий от Deleted

На написание этих условных 10 строчек качественно (а это значит с документацией и тестами) иногда уходит целый день

Есть гарантия, что go get выдаст правильно работающую библиотеку? Ведь несоответствие документации и кода даже в программах за деньги сплошь и рядом.

monk ★★★★★ ()
Ответ на: комментарий от tz4678

Мне просто нужно пару миллионов запросов сделать. Важно ограничить количество одновременных соединений. А этот код неправильный?

как вариант
https://play.golang.org/p/QD194CtohE_i
rps - регулируется ключом.
завершение генерации запросов по fmt.Scanln()

NimoLime ()
Ответ на: комментарий от beastie

A little copying is better than a little dependency.

Эта фраза про копирование, а не про использование.

Безотносительно того, что буквально воспринимать не стоит.

Deleted ()
Ответ на: комментарий от tz4678

код не ограничивает количество одновременных соединений. Код держит заданный rps. В контексте http сложно говорить о одновременных соединениях т.к протокол предполагает запрос ответ (get - 200ok). Код держит нагрузку на http server с заданным request per second. Т.е говоря о нагрузке послать 1000 и посмотреть результаты - это не говорящая статистика. а создать нагрузку и сказать что сервер держит 200 rps значащая статистика. И добавлю, столько короткие соединения трудно называть одновременными. (Если бы речь шла о телефонных вызовах то да 100 одновременных вызов держит плата обработки голоса. ) но это не точно

NimoLime ()
Ответ на: комментарий от NimoLime

мне не нужно нагрузку держать, мне нужно количество открытых соединений с удаленными серверами ограничить. если миллион одновременных запросов сделать, то вывалится куча ошибок. вот и все.

tz4678 ()

Последний цикл нужен чтобы дождаться выполнения последних 100 запросов. Если его не будет, процесс закончится и те рутины, которые не успели отработать до конца-тоже

anonymous ()