Ответы пользователя по тегу Go
  • Почему Go не видит бинарники в GOBIN?

    Го сам никакие пути к бинарникам не ищет, он обращается за этим к операционной системе. Поэтому добавьте вашу папку с бинарниками в переменную окружения PATH вашей ОС.

    GOBIN определяет путь, куда Го будет билдить бинарники, которые устанавливаются через go install. Но он не отвечает за их поиск там.
    Ответ написан
    Комментировать
  • Как в GO написать свою функцию к другим типам?

    Логично, что ругается. Вы объявили метод у типа strType, а переменную создали типа string.

    Нужно делать так:
    package main
    
    func main() {
    	var str strType
    
    	str.Temp()
    }
    
    type strType string
    
    func (s strType) Temp() {
    }
    Ответ написан
    3 комментария
  • Для чего в go.mod добовить зависимости если мы указываем полный путь к ним в коде?

    Для указания версий этих зависимостей. Чтобы бралась не последняя версия, а указанная. Обеспечивает повторяемость релизов и стабильность.
    Ответ написан
    Комментировать
  • Утекает память при использовании горутин и waitGroup, как найти корень проблемы?

    Подскажу важные хаки для вашего решения.
    Когда нужно обработать n каких-то задач в m потоков, в го очень хорошо работает паттерн worker pool.
    Суть в том, что создается канал, в одной горутине в него кладутся задачи и как только все задачи будут положены, канал обязательно закрывается.
    Тем временем, запускается m воркеров. Воркер представляет из себя функцию, которая в цикле for читает задачи из канала и выполняет их. Удобство тут в том, что как только канал будет закрыт (и в нем не останется ни одной задачи, если он буферизированный), то все циклы for во всех воркерах завершатся. Поэтому не надо делать отдельный канал и селект для завершения.
    WaitGroup при этом следит, чтобы все воркеры успели доработать до конца (потому что когда канал закрывается, какие-то воркеры еще могут завершать свои последние задачи).

    Шаблон в простом виде прикладываю тут, можете его натянуть на свой случай
    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    type Task struct {
    	URL string
    	// сюда можно еще добавить нужные данные для задачи
    }
    
    type Result struct {
    	// если нам нужно что-то получать как результат обработки задачи
    	// то можно результат класть сюда
    }
    
    const (
    	workersCount = 5
    )
    
    func main() {
    	tasks := make(chan Task)
    	results := make(chan Result)
    
    	go generateTasks(tasks)
    
    	wg := &sync.WaitGroup{}
    	for i := 0; i < workersCount; i++ {
    		wg.Add(1) // обязательно инкрементим вне воркера, потому что воркер стартует не сразу
    		go worker(tasks, results, wg)
    	}
    
    	go func() {
    		wg.Wait()
    		// wg.Wait пустит нас сюда только когда все воркеры завершатся
    		// а значит, нам можно закрывать канал результатов, потому что результатов больше точно не будет
    		close(results)
    	}()
    
    	for result := range results {
    		// обрабатываем результаты, если нужно
    		// если нам не нужно централизованное место для обработки результатов,
    		// канал results и все, что с ним связано, можно убрать
    		fmt.Println("got result:", result)
    	}
    	// программа завершится как только канал results будет закрыт
    }
    
    func generateTasks(tasks chan<- Task) { // тип chan<- значит, что в переданный сюда канал можно только писать, нельзя читать
    	// тут генерируем наши задачи, читаем их из файла, например
    	for i := 0; i < 20; i++ {
    		tasks <- Task{
    			URL: fmt.Sprintf("http://test.com?i=%d", i),
    		}
    	}
    
    	// как только задачи закончились, закрываем канал
    	// этим мы сигнализируем воркерам, что работы больше нет, можно завершаться
    	close(tasks)
    }
    
    func worker(tasks <-chan Task, results chan<- Result, wg *sync.WaitGroup) {
    	defer wg.Done() // defer позволяет точно не забыть сделать Done() при завершении функции
    	for task := range tasks {
    		fmt.Println(task.URL)
    		var res Result
    		// здесь пишем код, который обрабатывает задачу
    		// генерируем на выходе какой-то результат (если результат нам не нужен, можем это опустить)
    		results <- res
    	}
    }
    Ответ написан
    1 комментарий
  • Почему env GOPATH больше не имеет значения?

    Раньше все зависимости лежали в GOPATH, но это было неудобно тем, что не было никакого управления этими зависимостями. Нельзя было зафиксировать их конкретные версии и нельзя было для разных проектов сделать разные зависимости, потому что все было в одной центральной папке.
    Потом в го ввели систему модулей:
    https://go.dev/blog/using-go-modules
    https://habr.com/ru/company/otus/blog/503918/

    Теперь в корневой папке каждого проекта есть файл go.mod, который перечисляет зависимости этого проекта и их конкретные версии. Поэтому необходимость в GOPATH отпала, теперь Go при сборке использует модули, указанные в go.mod и не ходит в GOPATH вообще, это намного удобнее.
    Ответ написан
    4 комментария
  • Как зашифровать http запрос?

    Использовать https, тогда можно будет зашифровать в том числе урл.
    Если делать свое шифрование поверх http, то во-первых, это будет сильно сложнее сделать правильно и надежно, во-вторых, не получится зашифровать урл, потому что по стандарту http он должен быть в открытом виде.
    Ответ написан
    Комментировать
  • Как понять от первого элемента в слайсе(capacity)?

    Каждый раз, когда у слайса кончается капасити, го делает переаллокацию данных в памяти, увеличивая капасити и копируя старые данные в новую аллоцированную область. Причем, капасити растет с запасом, чтобы свести количество переаллокаций к минимуму (ибо это дорогая операция). Поэтому это нормальная ситуация, когда капасити становится больше, чем длина. Го вам делает запас на будущее.
    Ответ написан
    2 комментария
  • Как правильно взаимодействовать с каналами?

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

    Лучше сделать так
    package main
    
    import (
    	"bufio"
    	"fmt"
    	"os"
    	"sync"
    )
    
    func main() {
    	urls := make(chan string)
    	go fillChannel(urls)
    
    	// создаем группу для ожидания, того, что все воркеры завершены
    	wg := &sync.WaitGroup{}
    
    	for i := 0; i < 5; i++ {
    		// при запуске каждого воркера, увеличиваем счетчик в группе на 1
    		wg.Add(1)
    		go requestWorker(urls, wg)
    	}
    
    	// ждем, пока счетчик в группе не будет равен 0
    	wg.Wait()
    }
    
    func requestWorker(channel <-chan string, wg *sync.WaitGroup) {
    	// По завершении воркера счетчик в группе будет уменьшен на 1
    	defer wg.Done()
    	// Заодно пишем сообщение о завершении воркера
    	defer println("Worker stopped")
    
    	// Постоянно читаем из канала новые сообщения
    	// цикл автоматически завершится, когда канал закроется и буфер будет пуст
    	for url := range channel {
    		println(url)
    	}
    }
    
    func fillChannel(channel chan<- string) {
    	file, err := os.Open("data.txt")
    	defer file.Close()
    
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    
    	fileScanner := bufio.NewScanner(file)
    	fileScanner.Split(bufio.ScanLines)
    
    	for fileScanner.Scan() {
    		channel <- fileScanner.Text()
    	}
    
    	// закрываем канал, когда данные кончились
    	// в го принято, чтобы канал закрывал только тот, кто в него пишет
    	close(channel)
    }



    Этот паттерн называется worker pool. Мы пишем в канал все нужные данные и закрываем канал, когда данные кончились. Благодаря тому, что воркеры читают из канала через range, цикл просто выходит, когда канал закрыт и воркеры завершаются.
    WaitGroup используется для того, чтобы подождать, пока воркеры доработают последние данные.
    Ответ написан
    Комментировать
  • Как в процессе выполнения программы считывать вывод?

    Все гораздо проще. Вы можете сразу в stdout подложить буфер и смотреть в него при необходимости
    cmd := exec.Command("python", "script.py")
    stdoutBuf := bytes.NewBuffer(nil)
    cmd.Stdout = stdoutBuf
    if err := cmd.Run(); err != nil {
    	log.Println(err.Error())
    }
    
    fmt.Println(stdoutBuf.String())


    Или еще проще, если вам нужны и stdout и stderr вместе:
    cmd := exec.Command("python", "script.py")
    res, err := cmd.CombinedOutput()
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Println(string(res))
    Ответ написан
    3 комментария
  • Что не так с потоками?

    Рантайм го расчитан на то, что большинство горутин будут иметь точки блокировок, что логично для веб-сервисов. Когда сервис блокируется на чтении/записи сети/файла, планировщик переключается на другую горутину. Поэтому нет никаких гарантий того, что нагрузка равномерно распределиться, если все горутины будут заняты просто числомолотилками.
    После какой-то версии го (точно не помню номер), завезли вытесняющую многозадачность и теперь планировщик может переключать горутины даже на числомолотилках, но делает он это обычно когда запускается сборка мусора или срабатывает случайный таймер. Поэтому равномерного распределения на таких задачах не ждите, язык просто делался не для этого.
    Ответ написан
    Комментировать
  • Как преобразовать interface в string?

    https://go.dev/play/p/-aB5SS1amav
    package main
    
    import (
    	"fmt"
    	"log"
    )
    
    func main() {
    	code := map[string]interface{}{
    		"test": "test",
    	}
    
    	// извлекаем interface{} из мапы внутри которого строка
    	ifaceStr := code["test"]
    
    	// извлекаем строку из interface{}
    	str, ok := ifaceStr.(string)
    	if !ok {
    		log.Fatal("it's not a string")
    	}
    	fmt.Println(str)
    }


    Но почему бы вам не делать анмаршал в map[string]string сразу? Чтобы не конвертить лишний раз?
    Ответ написан
  • Можно ли использовать значение переменной как название структуры?

    Так делать не стоит, в го это решается другими способами. Вы пытаетесь переносить подход из скриптовых языков в го, так это не работает.
    Ответ написан
  • Как отдавать статику через julienschmidt/httprouter?

    r.Handler("GET", "/", fServer)
    Этот шаблон значит полное совпадение с корнем, поэтому у вас 404 на все вложенные страницы. Чтобы этого избежать сделайте так:

    r.Handler("GET", "/*filepath", fServer)
    Ответ написан
    3 комментария
  • Golang выдает ошибку, что делать?

    Вам компилятор четко пишет
    main redeclared in this block {строка 8 столбец 6 }
    previous declaration at ./hello.go:18:6

    Перевожу: функция main написана несколько раз, второй раз она встречается в файле hello.go на 18 строчке.
    Видимо, у вас в одной папке несколько файлов и в нескольких из них есть функция main. Так делать нельзя, все, что находится в одной папке, го считает одним пакетом.

    Что касается второй ошибки, отправляйтесь изучать основы синтаксиса языка, иначе далеко так не уедете. У вас лишние запятые в конце списка параметров и в конце списка переменных.
    Ответ написан
    7 комментариев
  • Запутался с установкой golang, что делать?

    потом я хотел настроить перменную GOPATH, а там как я понял надо указать путь до ГО и там выбрать папку scr.


    Нет, вы пользуетесь каким-то странный старым руководством. Если из пакетов ставите, то у вас уже всё нужное есть. Никакой GOPATH уже давно прописывать не надо.
    Ответ написан
    Комментировать
  • Как через os/exec запустить сторонний exe файл в GoLang?

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

    Несколько примеров:
    path := `c:\Program files\some program\program.exe`
    // аналогично вызову в консоли start "c:\Program files\some data"
    cmd := exec.Command("start", path)
    ......
    
    // аналогично вызову в консоли "c:\Program files\some program\program.exe"
    cmd := exec.Command(path)
    ......
    
    // аналогично вызову в консоли some_prog.exe -flag1 value1 -flag2 value2
    cmd := exec.Command("some_prog.exe", "-flag1", "value1", "-flag2", "value2")
    ......


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

    Кстати, если вам просто нужен полный ответ команды stdout+stderr, можете использовать метод cmd.CombinedOutput(). Он выполнил программу, дождется ее завершения и вернет полностью то, что она писала в консоль.
    Ответ написан
    7 комментариев
  • Как вы решаете проблему циклических импортов?

    orders и payments должны лежать внутри одной модели.
    Если кода много, то можете вынести эти структуры в отдельный пакет, посвященный только внутренним структурам данных, это и будет та самая одна модель.
    пакет structures: типы orders и payments
    пакет orders: импортирует structures
    пакет payments: импортирует structures

    А в целом, в го связанность кода (и кольцевые импорты вместе с ней) принято разрушать через использование интерфейсов.
    У вас в идеале должны быть пакеты с моделями, в которых должны лежать внутренние типы данных и объявлены интерфейсы к внешним источникам типа баз данных, итд.
    Внутри пакета базы данных у вас должны импортиться структуры данных из модели и производиться конверсия сырых данных в структуры модели. А сама база данных в модели объявлена через интерфейс, поэтому пакет базы не приходится импортить.
    Такая схема дает еще плюсы, что легко тестить модель, подменяя базу моками.
    Ответ написан
    Комментировать
  • Как получить базовый массив слайса или ссылку на него?

    Никак не получить, это и не нужно в нормальной ситуации
    Если очень хочется, то можно это сделать через unsafe. Но используя пакет unsafe вы лишаетесь безопасности по памяти.
    Ответ написан
    Комментировать
  • Как провести итерацию функций в map?

    Старайтесь никогда не использовать пустой интерфейс (interface{}), только в самых крайних случаях.
    Тут вам нужно просто изменить тип мапы на map[string][]func(string, interface{})

    Не знаю, почему shortName у вас тоже имеет тип interface{}, его бы изменить на конкретный тип или конкретный интерфейс. Тогда код станет еще понятнее и удобнее.
    Ответ написан
    Комментировать
  • Как ускорить работу кода?

    Для таких целей отлично подходит паттерн "worker pool"
    https://gobyexample.com/worker-pools

    Создаете воркеров столько, сколько параллельно урлов хотите обрабатывать. Отправляете в канал не слайс урлов, а урлы по-одному. Каждый воркер берет из канала свой урл и все они параллельно обрабатывают разные урлы.
    Ответ написан
    1 комментарий