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

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

    Тесты есть разные, Го примерно идет в ногу с Джавой и Шарпом, особенно в случае многопоточных нагруженных серверов. А если посмотреть сколько он потребляет при этом памяти, то даже выходит вперед по эффективности.

    На Го очень просто писать многопоточность и асинхронность, не нужно думать об await-ах, каждая горутина имеет свой стек, что снимает с программиста много головной боли. Вся стандартная библиотека и большой набор библиотек с гитхаба из коробки поддерживают асинхронность и потоковую обработку данных, работать с этим сильно проще, чем в других языках. Соответственно, сложнее накосячить.

    Но абстракций на Го очень мало, по сравнению с той же Джавой, он довольно бедно выглядит (что и дает простоту чтения кода). Это является минусом в определенных ситуациях, поэтому на Го стараются писать небольшие сервисы.

    В итоге, легковесность горутин, легкость работы с ними и асинхронная модель из коробки (не создается тред на каждую рутину, а наоборот, рутины обрабатываются разными тредами по необходимости) привели к тому, что ниша Го это сервисы, которые упираются в ожидание ресурсов от каких-то внешних систем по сети. То есть, идеальный кейс для веб-сервера, который собирает под капотом инфу с БД и других сервисов. По сути такой сервис большую часть времени ждет ресурсов по сети, в Го это ожидание сделано очень эффективно.
    Ответ написан
    Комментировать
  • Импортирование доменных моделей другого сервиса?

    Импорт grpc-контрактов будет корректным.

    Импорт доменных моделей — нет. Потому что доменные модели должны быть независимыми внутри самого сервиса.
    Ответ написан
    Комментировать
  • Opentelemtry +?

    Prometheus это про метрики

    Grafana это тоже про метрики

    Судя по коду, вы сделали поддержку трейсинга в вашем приложении, а чтобы смотреть трейсы, нужно развернуть интерфейс Jaeger. https://www.jaegertracing.io/

    Есть статья на эту тему https://habr.com/ru/articles/710644/
    Ответ написан
  • Докер ограничивает размер загружаемого на бекенд файла?

    Докер ничего не знает про http, он только пробрасывает tcp-коннекты до вашего приложения. Так что проблема точно где-то в вашем коде. Скорее всего какой-то из конфигов локально у вас подключается, а в докер не пробрасывается, поэтому какие-то из ограничений внутри приложения не снимаются.

    502 Bad Gateway обычно вылезает, когда ваше приложение запрос куда-то перенаправляет. Вы с файлом что делаете, сохраняете на диск или куда-то отправляете?
    Ответ написан
  • Как сохранить нули после запятой в типе float64?

    Если тип поля сделать json.RawMessage, то оно не будет меняться. Удобно, когда вам не нужно само число, но надо чтобы оно просто сохранилось при приеме-передаче структур.

    https://go.dev/play/p/egy2GXVB-5p

    код
    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type testStruct struct {
    	ID    string          `json:"id"`
    	Type  string          `json:"type"`
    	Value json.RawMessage `json:"value,omitempty"`
    }
    
    func main() {
    	var msg testStruct
    
    	json.Unmarshal([]byte(`{"type": "test", "id": "testID", "value": 123.00}`), &msg)
    
    	output, _ := json.Marshal(msg)
    
    	fmt.Println(string(output))
    }
    Ответ написан
    Комментировать
  • Как конвертировать .ogg в другой аудио-формат (.mp3, .wav) на Go?

    func main() {
    	cmd := exec.Command("ffmpeg", "-i", "input.ogg", "output.mp3")
    	res, err := cmd.CombinedOutput()
    	if err != nil {
    		fmt.Println(string(res))
    		fmt.Println("error executing command:", err)
    		os.Exit(1)
    	}
    }


    Естественно, на системе должен быть установлен ffmpeg, потому что тут вызывается внешняя программа.
    Ответ написан
    Комментировать
  • Как можно регулярным выражением получить три вхождения в многострочном тексте?

    Тут лучше без регулярок, будет эффективнее: https://go.dev/play/p/7Ae-T6O0YmD

    text := `Мы оба влюблены в один и тот же сон,
    Нас вынесла волна — и укатилась с шумом.
    Ты ль жарче влюблена, иль я страстней влюблен,
    Какое дело нам! Мы не поверим думам! 15 июня 1903
    Информация сайта: https://domain.tdl/?name=asdasdasd
    `
    
    parts := strings.Split(text, "Информация сайта: ")
    
    fmt.Println(parts[0])
    fmt.Println(parts[1])


    Если очень хочется регуляркой, тогда так: https://go.dev/play/p/pJ0mtx4gWPC

    re := regexp.MustCompile(`(?s)^(.+)Информация сайта: (.+)$`)
    
    matches := re.FindStringSubmatch(text)
    
    fmt.Println(matches[1])
    fmt.Println(matches[2])
    Ответ написан
    1 комментарий
  • Как писать микросервисную архитектуру?

    Вы не сможете себе в портфолио добавить микросервисную архитектуру просто написав ее в пет-проекте. Настоящие микросервисы можно пощупать только на масштабах больших компаний и их инфраструктуре, так что устраивайтесь джуном/стажером в какой-нибудь Авито или Озон.

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

    Сделайте лучше хороший проект-монолит, отработайте навыки языка, БД, развертывания, гита, итд... на одном проекте. Будет гораздо полезнее.
    Ответ написан
    2 комментария
  • Как вытащить поля из запроса в pgx?

    https://pkg.go.dev/github.com/jackc/pgx
    Самый первый пример из верха документации
    var name string
    var weight int64
    err := conn.QueryRow("select name, weight from widgets where id=$1", 42).Scan(&name, &weight)
    if err != nil {
        return err
    }


    Только поля замените на свои

    В пятой версии добавились еще другие способы https://pkg.go.dev/github.com/jackc/pgx/v5#hdr-Que...
    Ответ написан
    Комментировать
  • Если в glang перемнная передана в func, то она там живёт сомостоятельно или в пямяти 1 адрес?

    В Го по-умолчанию переменные передаются по значению (то есть, копируются на стеке), но есть нюансы.

    1. Мапы и каналы всегда передаются по указателю.
    2. У слайса на стеке только дескриптор, так что он копируется, но данные, на которые слайс указывал, остаются там же в памяти, где и были.
    3. Если вы явно передаете указатель, то тут все очевидно.
    4. У интерфейсов копируется дескриптор, но т.к. в дескрипторе указатель, то тут все как со слайсами.
    Ответ написан
    Комментировать
  • Почему не работает код Go?

    Код же выводит на экран те строки, которые были написаны вами более 1 раза. А в приведенном скриншоте вы каждое число по одному разу только ввели.

    Проверил у себя, все работает:
    $ go run main.go
    hello
    Hi
    John
    hello
    John
    Paul
    John
    ^D
    2	hello
    3	John


    hello я написал два раза, поэтому там цифра 2, а John написал 3 раза. Остальные слова только один раз, поэтому их нет в выводе
    Ответ написан
    5 комментариев
  • В стандартной библиотеке go есть ли инструменты для работы с многомерными срезами/массивами?

    В стандартной библиотеке — нет. Но есть сторонние библиотеки вроде https://pkg.go.dev/gonum.org/v1/gonum/mat или https://github.com/james-bowman/sparse

    Если вы решаете задачи на алгоритмы, то вам лучше как раз без таких библиотек, которые в одну строчку делают задачу (потому что в библиотеке кто-то до вас уже написал агоритмы).

    Если же по работе нужно для решения проблемы, то берите одну из библиотек, которые я предложил выше.

    Ведь без инструментов, тут нужен iq минимум 150


    Это вам так кажется, потому что первый язык и вы только начинаете разбираться. На самом деле такие задачи без проблем решаются на чистом го.
    Ответ написан
    Комментировать
  • Почему я получаю ошибку invalid operation: err (variable of type Error) is not an interface при проверке типа переменной?

    https://go.dev/play/p/eDi3J3Zzcdg

    package main
    
    import "fmt"
    
    type MyError struct {
    	Message string
    }
    
    func (e MyError) Error() string {
    	return e.Message
    }
    
    func (e MyError) Smth() {
    }
    
    type SomeInterface interface {
    	Smth()
    }
    
    func main() {
    	var err SomeInterface
    	err = MyError{"Something went wrong"}
    
    	// Проверяем, реализует ли err интерфейс error
    	if _, ok := err.(error); ok {
    		fmt.Println("err реализует интерфейс error")
    	} else {
    		fmt.Println("err НЕ реализует интерфейс error")
    	}
    }


    У вас получилось, что тип переменной err это структура, но нужно чтобы тип был каким-нибудь интерфейсом.

    В моем примере я создал интерфейс SomeInterface и добавил вашему типу метод, чтобы он этому интерфейсу удовлетворял. Теперь данный ассершн имеет смысл.

    P.S. С пустым интерфейсом тоже работать будет https://go.dev/play/p/Yz0M1Wzopua
    Ответ написан
    Комментировать
  • Какой фреймворк для Go изучать в 2024 году?

    Если http:
    Взять какой-нибудь хороший роутер, например https://github.com/julienschmidt/httprouter
    И на его основе сделать врапперы и мидлвари под себя по необходимости.
    Если работаете в какой-то конторе, то там уже должен быть фреймворк для микросервисов, заточенный под инфраструктуру компании. Потому что если сервисы делаются все по-разному, это беда.

    Если grpc:
    Особых вариантов нет, нужно использовать protoc.
    При необходимости подключить плагины, чтобы был сваггер и гейтвей в http
    github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
    github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2

    Почему не предлагаю какой-то большой конкретный фреймфорк? Если бы вы сказали, что веб-приложения будете писать, можно было бы посоветовать что-то типа gin, но вы сказали про микросервисы. С ними такая специфика, что надо как конструктор собирать себе стек под задачу, иначе будет неудобно.
    Ответ написан
    3 комментария
  • Как правильно настроить локальное окружение для веб-разработки на Go?

    Есть достаточно много утилит, которые могут следить за папкой с исходниками и пересобирать приложение.
    Например https://github.com/cosmtrek/air

    Ну и для разработки будет гораздо быстрее и удобнее запускать не в докере, билдя каждый раз контейтер, а использовать go run
    Ответ написан
    Комментировать
  • Как добавить метаданные в изображение?

    https://github.com/dsoprea/go-exif

    Есть такая библиотека, которая умеет править или создавать новые exif-блоки
    Ответ написан
    Комментировать
  • Кто нибудь применял или видел generics в серьезных Go проектах?

    Одно из самых полезных применений дженериков, которое у меня было — это написание врапперов, которые добавляют строгую типизацию в обработку чего-либо и уменьшают бойлерплейт. Например, обработка http-запросов. Обычно используется роутер и в него регистрируются хендлеры для разных путей, например:
    router := httprouter.New()
    
    router.POST("/api/products", productsHandler.Handle)
    router.POST("/api/get_free_slots", getFreeTimeSlotsHandler.Handle)
    router.POST("/api/create_visit", createVisitHandler.Handle)


    При этом, функция Handle у каждого хендлера это обычно что-то типа func(w http.ResponseWriter, r *http.Request)
    Хендлер уже внутри себя сам читает тело, парсит его из json, потом формирует ответ, итд.

    Хотелось добавить сюда типизацию и какую-то структуру, поэтому я написал
    простой враппер

    type Validatable interface {
    	Validate() error
    }
    
    func Wrap[Req Validatable, Res any](fn func(ctx context.Context, req Req) (Res, error)) httprouter.Handle {
    	handler := func(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) {
    		writeError := func(statusCode int, err error) {
    			writer.WriteHeader(statusCode)
    			_, _ = writer.Write([]byte(err.Error()))
    		}
    
    		var req Req
    
    		bytes, err := io.ReadAll(request.Body)
    		if err != nil {
    			writeError(http.StatusBadRequest, err)
    			return
    		}
    
    		if err := json.Unmarshal(bytes, &req); err != nil {
    			writeError(http.StatusBadRequest, err)
    			return
    		}
    
    		if err := req.Validate(); err != nil {
    			writeError(http.StatusBadRequest, err)
    			return
    		}
    
    		resp, err := fn(request.Context(), req)
    		if err != nil {
    			writeError(http.StatusInternalServerError, err)
    			return
    		}
    
    		respBytes, err := json.Marshal(resp)
    		if err != nil {
    			writeError(http.StatusInternalServerError, err)
    			return
    		}
    		writer.WriteHeader(http.StatusOK)
    		_, _ = writer.Write(respBytes)
    	}
    
    	return handler
    }



    Он берет на себя весь бойлерплейт по парсингу и валидации запроса, формированию ответа, работы с кодами, итд. Причем, через стандартный errors.Is иногда добавлял сюда еще возможность из хэндлера указать врапперу, какой http-код ответа отдавать. Обычно все функции враппера делал под конкретную задачу в конкретном проекте (можно, например, не только json тела парсить, но и урл и хедеры, итд).

    Использование враппера выглядит так.
    Мы пишем хэндлер, используя конкретные типы

    type Handler struct {
    	userService *usersvc.Service
    }
    
    type Request struct {
    	UserID int64 `json:"user_id"`
    }
    
    func (c Request) Validate() error {
    	if c.UserID == 0 {
    		return errors.New("empty user id")
    	}
    	return nil
    }
    
    type Response struct {
    	Name string `json:"name"`
    	Age  int    `json:"age"`
    }
    
    func New(userService *usersvc.Service) *Handler {
    	return &Handler{userService: userService}
    }
    
    func (h Handler) Handle(ctx context.Context, req *Request) (*Response, error) {
    	user, err := h.userService.UserByID(ctx, req.UserID)
    	if err != nil {
    		return nil, err
    	}
    
    	return &Response{
    		Name: user.Name,
    		Age:  user.Age,
    	}, nil
    }



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

    В итоге использование такого хэндлера+враппер выглядит так:
    getUserHandler := getuser.New(userSvc)
    
    router := httprouter.New()
    
    router.POST("/api/get_user", wrapper.Wrap(productsHandler.getUserHandler))


    Причем, тут даже не видно, что Wrap() имеет дженерик-параметры, потому что гошка самостоятельно выводит эти параметры из типов Request и Response из хэндлера и проверяет, что у Request есть метод Validate.

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

    Потому что вы должны хотя бы раз прочитать значение переменной. Во всех случаях, которые вы приводите, где ошибки нет — значение переменной где-то читается.
    Это сделано, чтобы исключить ряд багов, которые может допустить программист (например, зашедоуить переменную из скоупа выше и присвоить значение во временную переменную вместо оригинальной переменной).
    Ответ написан
    3 комментария
  • Почему так работают интерфесы в Го?

    Потому что в Го сигнатура метода в интерфейсе должна совпадать полностью. Таким образом на рантайме они быстро матчатся.

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

    Кстати, не рекомендую называть интерфейсы ISomething, это не принято в Го.
    Ответ написан
    Комментировать
  • Как исправить ошибку "cannot download, $GOPATH must not be set to $GOROOT"?

    1. Убедиться, что го установлен не в ~/go и что $GOPATH не смотрит на место его установки
    2. Убедиться, что код проекта находится не в ~/go или вложенных в него папках и не в $GOPATH и вложенных в него папках.
    3. Убедиться, что зависимости в проекте управляются через модули (что сделан go mod init)
    Ответ написан
    Комментировать