Ответы пользователя по тегу Go
  • Как маппить результат запроса бд в структуры со вложенностями Golang?

    - Я считаю, что свой кастомный маппер - это отличное решение. Чтобы быть уверенней, обязательно напишите тесты. Как unit, так и интеграционные, например, с помощью testcontainers.

    - Можете вместо pgx посмотреть в сторону sqlc. У него совершенно другой подход. Вы пишете запрос со всеми джойнами, а он вам автоматически генерирует код со всеми структурами и функциями. Правда, там по джойнам практически отсутствует документация, но уж погуглите.

    - Ну, и если совсем сложно... то... может одним глазком глянуть в сторону ORM? Я никому не расскажу...
    Ответ написан
    Комментировать
  • Как устранить утечку памяти при множественных соединениях в net/http Golang?

    Скорей всего, проблема в том, что вы возвращаете ошибку, не закрывая http соединение.
    Для этого была специально введена одна из самых привлекательных конструкций языка - defer.

    var client = &http.Client{
        Timeout: time.Millisecond * 100,
    }
    
    func scan(ip string) (error, string, string, string) {
        resp, err := client.Get("http://" + ip)
        // Здесь сразу желательно обработать ошибку ...
        
        // Закрываем соединение в конце выполнения
        // функции в любом случае,
        // даже если где-то возникнет ошибка
        defer resp.Body.Close()
    
        // Закрытие неиспользуемых соединений
        // Нужно ли это теперь?
        // client.CloseIdleConnections()
    
        // Проблема могла быть тут
        // if err != nil {
        //     return err, "", "", ""
        // }
    
        headers := buildHeaders(resp.Header)
        body, _ := io.ReadAll(resp.Body)
    
        // и это нам уже особо не нужно
        // resp.Body.Close()
    
        return err, headers, string(body), resp.Status
    }
    Ответ написан
    3 комментария
  • Почему я получаю ошибку invalid operation: err (variable of type Error) is not an interface при проверке типа переменной?

    Я полностью присоединюсь к ответу выше, просто хочу дополнить.

    Замечу, что вы проверяете не то, что нужно, шиворот-навыворот. Проверять при приведении типов лучше конкретные типы. Т.е. мы получаем из функции ошибку в виде интерфейса error, а уже в проверке проверяем её на наш кастомный тип MyError. Поэтому желательно (не обязательно) не проверять переменную ошибки в той же функции, где вы её создали, а возвращать откуда-то и уже тогда проверять на ошибку.

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

    package main
    
    import (
    	"fmt"
    )
    
    type MyError struct {
    	Message string
    }
    
    func (e MyError) Error() string {
    	return e.Message
    }
    
    func main() {
    	// Возвращаем из функции нашу кастомную ошибку, но в виде интерфейса error
    	err := foo()
    
    	if err == nil {
    		fmt.Println("Нет ошибки")
    	// И теперь тут приводим error к нашему типу MyError и проверяем
    	} else if myErr, ok := err.(MyError); ok {
    		fmt.Printf("Ура! Нужный нам тип ошибки: %v\n", myErr.Message)
    	} else {
    		fmt.Println("Какой-то другой тип ошибки:", err)
    	}
    
    	// Проверка одной из "подстав" Go
    
    	err = bad()
    	if err != nil {
    		fmt.Println("Упс... Как так... Не nil...")
    	} else {
    		fmt.Println("Должно вывестись это, но не выводится...")
    	}
    
    	err = good()
    	if err != nil {
    		fmt.Println("Это не должно выводиться, всё верно.")
    	} else {
    		fmt.Println("Ошибки нет, всё верно.")
    	}
    }
    
    func foo() error {
    	err := MyError{"Ой! Ошибка MyError!"}
    	// err := fmt.Errorf("Ой! Обычная ошибка!")
    	// var err error = nil
    	return err
    }
    
    func bad() error {
    	var p *MyError = nil // Вроде же nil, но не работает....
    	// p = &MyError{"Ой!"} // Пробуем создать ошибку, и всё работает.
    
    	if p == nil {
    		fmt.Println("Ну nil же-ж... Должно же-ж работать", p)
    	}
    
    	return p
    }
    
    func good() error {
    	// return MyError{"Ой!"}
    
            // Буквально пишем "nil", никаких указателей, которые равны nil, это прямой выстрел в ногу
    	return nil
    }


    https://go.dev/play/p/2YcWcH9oqel
    Ответ написан
    Комментировать
  • Насколько актуальна книга Марка Саммерфильда?

    Не читал, но не осуждаю)

    Я считаю, что если многие советуют, то можно брать, если хотите.

    Go - язык немного особенный в плане устаревания информации. Если, например, купить книгу по JavaScript того года, то это будет супер-вредно, потому что в те времена это был чуть ли не другой язык.

    А создатели Go с самого начала заявили, что обратная совместимость будет одной из основных характеристик языка. Роб Пайк вообще говорил, что Go 2 никогда не будет.

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

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

    P. S. Совет лично от меня: читайте книги по программированию на английском. Очень часто переводчики просто бредят как ChatGPT, и некоторые книги просто невозможно читать.
    Ответ написан
    2 комментария
  • Как в goшном SOA сервисе работать с базой данных?

    Не знаю, какая у вас архитектура, но когда какая-то библиотека обрастает огромным количеством методов, то Go - это один из редких языков программирования, который, благодаря своей утиной типизации позволяет использовать шаблон проектирования "consumer interfaces", т.е. объявлять интерфейсы в месте, где они используются и включать в эти интерфейсы только те методы из огромной структуры базы данных, которые именно тут, на месте, и используются. Таким образом ваши интерфейсы будут минимальными, понятными, и искать их не придётся. Ну, и в добавок, старый добрый Dependency Injection решит массу наших проблем

    База данных:
    package db
    
    import "fmt"
    
    // Стуктура базы данных. Здесь нам даже и не нужно объявлять никаких интерфейсов
    type Service struct {
    	// ...
    }
    
    func NewDbService() (*Service, error) {
    	return &Service{}, nil
    }
    
    func (d *Service) CreateUser(username string, email string) {
    	fmt.Println(fmt.Sprintf("user %s with email %s is created", username, email))
    }
    
    func (d *Service) CreateProduct(name string) {
    	fmt.Println(fmt.Sprintf("product  %s is created", name))
    }


    Пользователь:
    package user
    
    // Тот самый интерфейс, который нам позволяет выбрать из структуры базы данных
    // только нужные нам здесь методы
    type dbUser interface {
    	CreateUser(username string, email string)
    }
    
    type Service struct {
    	db dbUser
    	// ...
    }
    
    func NewService(db dbUser) (*Service, error) {
    	return &Service{db}, nil
    }
    
    func (c *Service) New(username string, email string) {
    	c.db.CreateUser(username, email)
    }


    Товар:
    package product
    
    // Тот самый интерфейс, который нам позволяет выбрать из структуры базы данных
    // только нужные нам здесь методы
    type dbProduct interface {
    	CreateProduct(name string)
    }
    
    type Service struct {
    	db dbProduct
    	// ...
    }
    
    func NewService(db dbProduct) (*Service, error) {
    	return &Service{db}, nil
    }
    
    func (p *Service) New(name string) {
    	p.db.CreateProduct(name)
    }


    main.go
    package main
    
    import (
    	"test2/db"
    	"test2/product"
    	"test2/user"
    )
    
    func main() {
            // Инициализируем базу данных
    	dbService, _ := db.NewDbService()
            // Структура базы данных реализует интерфейс dbProduct, инжектим её
    	productService, _ := product.NewService(dbService)
            // Структура базы данных реализует интерфейс dbUser, инжектим её
    	userService, _ := user.NewService(dbService)
    
            // Пользуемся на здоровье...
    	userService.New("user1", "uswr1@example.com")
    	productService.New("product1")
    }
    Ответ написан
    Комментировать
  • Как организовать структуру проекта с несколькими модулями Golang?

    Делаем три разных файла main.go под каждый сервис. Кладём их каждый в свою подпапку в директории, например "cmd".
    А для общих библиотек используем отдельную директорию, например "pkg", и будем импортировать отсюда функционал во все три сервиса.

    Т.е. у нас получается структура файлов:
    cmd/
          repeater/
                main.go
          controller/
                main.go
          executor/
                main.go
    pkg/
          c-library/
                clibrary.go
    go.mod


    И потом запускаем компиляцию:

    go build ./cmd/repeater
    go build ./cmd/controller
    go build ./cmd/executor


    Пример main.go
    package main
    
    import c_library "test/pkg/c-library"
    
    func main() {
    	c_library.HelloWorld()
    }


    И общая библиотека:
    package c_library
    
    import "fmt"
    
    func HelloWorld() {
    	fmt.Println("HELLO WORLD")
    }


    Весь проект у меня лежит в папочке test, и в файле go.mod надо бы указать следующее

    module test
    Ответ написан
    5 комментариев
  • Как предотвратить бесконечную загрузку страницы при отправке post запроса?

    Использовать SSE (Server Sent Events). Это на порядок проще, чем вебсокеты, но мощь практически такая же.
    Вот неплохая статья на английском.
    https://blog.stackademic.com/real-time-communicati...

    Кстати, библиотека HTMX позволяет это обрабатывать и без написания Javascript
    https://htmx.org/extensions/server-sent-events/
    Ответ написан
    Комментировать