• Как сделать POST запрос используя сертификат?

    @ghostiam
    На Go писатель, серверов пинатель.
    Вам нужен клиентский сертификат и приватный ключ от него, а так же сертификат CA.
    Далее нужно загрузить ваши сертификаты и настроить http.Client.
    ...
    	transport := &http.Transport{TLSClientConfig: tlsConfig}
    	client := &http.Client{Transport: transport}
    
    	resp, err := client.Post(...)
    ...


    Полный код смотрите тут(нагуглил по запросу "golang http request client certificate"):
    https://gist.github.com/michaljemala/d6f4e01c4834b...
    Ответ написан
    Комментировать
  • Как правильно настроить канал в Golang?

    @ghostiam
    На Go писатель, серверов пинатель.
    Вам нужно добавить sync.WaitGroup во внутрь функции httpRequest

    Получиться приблизительно так:
    func httpRequest(waitChan chan struct{}, inputData inputDataStruct) {
        //url := <- urlChan
        defer close(waitChan)
        urlChan := make(chan string)
        count := inputData.count
        j := 0
        var wg sync.WaitGroup
        for i := range inputData.url {
            for j < count {
                wg.Add(1)
                go func() {
    getResponse(urlChan, inputData)
    wg.Done()
    }
                urlChan <- inputData.url[i]
                j++
            }
            j = 0
        }
    wg.Wait()
    }


    Я конечно не уверен, что ваш код станет после этого рабочим, так как у вас там дальше запускаются горутины, например appendResponse(), только не понятно зачем.
    Ответ написан
  • Что нужно писать в файлах proto в option go_package?

    @ghostiam
    На Go писатель, серверов пинатель.
    Нужно указать полный путь до пакета, где будут лежать сгенерированные файлы.
    В вашем случае (если будете держать сгенерированные и прото файлы в /protos/fizz)
    option go_package = "example.com/project/protos/fizz";

    далее запускаете генерацию с опцией
    --go_opt=paths=source_relative

    If the paths=source_relative flag is specified, the output file is placed in the same relative directory as the input file. For example, an input file protos/buzz.proto results in an output file at protos/buzz.pb.go.

    https://developers.google.com/protocol-buffers/doc...

    Загружать код в репозиторий не нужно.
    Ответ написан
    4 комментария
  • Стоит ли брать Go в качестве первого ЯП?

    @ghostiam
    На Go писатель, серверов пинатель.
    Как бы я не любил go (он мой основной язык на протяжении 8 лет или даже более), всё таки я бы советовал начать с другого, так как go больше серверный язык.

    Вы уже определились, что больше нравится? Игры, win/mac/linux софт, веб, серверное, фронт, мобайл программирование?

    В качестве первого языка, советую начать с C#, так как на нём можно делать всё что перечислено выше(не всегда конечно эффективно).
    Он даст возможность изучить программирование под разные типы задач, а строгая типизация C#(go тоже строго типизированный) позволит обойти кучу больных мест, на которые можно наткнуться например в JS или Python.

    А вот когда определитесь в направлении своего развития, стоит выбрать язык более предназначенный под задачу.
    Ответ написан
    2 комментария
  • Лучший способ избежать race condition, в данном случае?

    @ghostiam
    На Go писатель, серверов пинатель.
    Примеры с каналом
    Когда знаем кол-во данных:
    https://play.golang.org/p/7pNGCebvqlo
    Когда не знаем:
    https://play.golang.org/p/7WtVnXwjtuo

    Подробности в комментариях:
    https://qna.habr.com/answer?answer_id=1901263#comm...
    Ответ написан
    Комментировать
  • Можно ли в Go собрать проект с объявленными, но не используемыми переменными?

    @ghostiam
    На Go писатель, серверов пинатель.
    Нет, собрать нельзя.
    Можно сделать финт ушами(но это дурной тон)
    var _ = fmt.Println
    var _ = myvar

    То есть, воспользоваться нужно в любом случае.

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

    @ghostiam
    На Go писатель, серверов пинатель.
    go test -v .
    Ответ написан
    Комментировать
  • Какая рекомендуемая минимальная конфигурация vps для rails, django, node/express, go, rust, elixir, aps.net core?

    @ghostiam
    На Go писатель, серверов пинатель.
    Могу ответить по чистому GO, так как не использую фреймворки:
    У меня достаточно крупные сервисы, но они редко едят больше 50МБ памяти, в среднем от 15 до 30МБ.
    Но конечно всё это "среднее" по больнице, так как высоконагруженные сервисы, со слоями кеша вполне едят у меня и 0.5ГБ и более, но это всё из за кеша и огромного количества горутин(да они легковесные, но если запустить несколько десятков-сотен тысяч...).

    По поводу длительности, размер занимаемой памяти не будет расти, если вы не допустите грубых ошибок, например:
    - У вас будет вечная глобальная переменная/структура, в которую данные будут постоянно писаться, но не удаляться, тут GC не сможет помочь(или тот же кеш без TTL).
    - Утечки горутин.
    Ответ написан
    Комментировать
  • Есть ли смысл в том что в go убирают пакет ioutil?

    @ghostiam
    На Go писатель, серверов пинатель.
    Меньше зависимостей, как в примере в статье:
    Now that ioutil is no longer needed, and os was already imported, we have one less dependency

    Да и зачем нужен пакет `*util`, когда то же самое может быть в основном пакете, новичкам проще будет запомнить где лежат нужные методы(не все же пользуются Goland, который сам подставит нужный пакет).

    Но я думаю, это подготовка к пакету io/fs(который они упоминают в статье) и они просто стандартизируют методы.

    Но лучше почитайте proposal, на которые есть ссылки в статье, там указаны причины изменений и более подробное обсуждение.
    Ответ написан
    Комментировать
  • Как пользоваться check/handle в Golang?

    @ghostiam
    На Go писатель, серверов пинатель.
    А у вас го версии 2?

    Подсказка: версии 2 не существует :), а в статье описаны идеи, которые, возможно, когда нибудь будут добавлены в го в будущем, которое пока не наступило.

    в статье ведь написано:
    Черновики дизайна это даже не предложения (proposals), с которых начинается любое изменение в библиотеке, тулинге или языке Go. Это начальная точка для обсуждения дизайна, предложенная командой Go после нескольких лет работы над данными вопросами. Всё, что описано в черновиках с большой долей вероятности будет изменено, и, при наилучших раскладах, воплотится в реальность только через несколько релизов (я даю ~2 года).


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

    Да, так и обрабатывать, это го, тут так принято... Можно вынести это в отдельную функцию, которая будет возвращать `start, end, err`.
    Ответ написан
  • Как создать новый контекст от старого игнорируя прерывание контекста в go?

    @ghostiam
    На Go писатель, серверов пинатель.
    Оберните контекст в неотменяемый.
    // WithoutCancel returns a context that is never canceled.
    func WithoutCancel(ctx context.Context) context.Context {
    	return noCancel{ctx: ctx}
    }
    
    type noCancel struct {
    	ctx context.Context
    }
    
    func (c noCancel) Deadline() (time.Time, bool)       { return time.Time{}, false }
    func (c noCancel) Done() <-chan struct{}             { return nil }
    func (c noCancel) Err() error                        { return nil }
    func (c noCancel) Value(key interface{}) interface{} { return c.ctx.Value(key) }


    Вызывать как-то так:
    package main
    
    import (
    	"context"
    	"time"
    )
    
    func main() {
    	ctx := context.Background()
    	ctx, c := context.WithTimeout(ctx, 100*time.Millisecond)
    	defer c()
    
    	ctxNo := WithoutCancel(ctx)
    	...
    }


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

    @ghostiam
    На Go писатель, серверов пинатель.
    вот фикс
    https://play.golang.org/p/TR38meeQAH7
    или
    https://play.golang.org/p/WdGTLqGaoAD

    это происходит, потому что так работает range, он копирует элемент из массива и перезаписывает переменную i при каждой итерации, поэтому нужно или скопировать её(как в примере 1), или передавать её через замыкание(как в 2 и в случае с горутинами).
    Ответ написан
    1 комментарий
  • Чем запуск горутины отличается от запуска функции?

    @ghostiam
    На Go писатель, серверов пинатель.
    Потому-что горутина запускается в новом "потоке" приложения и главный поток не блокируется.
    Чтобы дождаться завершения работы всех горутин, нужно использовать sync.WaitGroup
    https://gobyexample.com/waitgroups
    https://golang.org/pkg/sync/#example_WaitGroup
    Ответ написан
    Комментировать
  • [Updated] Жива ли связка protobuf + go + grpc?

    @ghostiam
    На Go писатель, серверов пинатель.
    Отлично связка живёт и развивается.
    Не знаю какие уроки/статьи вы смотрите, из офф доки всё работает, пользуюсь постоянно.
    https://grpc.io/docs/languages/go/

    Вот как выглядит сгенерированный файл
    https://github.com/ghostiam/gosandbox/blob/master/...
    Совсем другие импорты, нежели у вас.
    Ответ написан
  • Как получить доступ к LTE-модему через VPN?

    @ghostiam
    На Go писатель, серверов пинатель.
    Роут на 192.168.8.1 через VPN пробовали?
    Главное чтобы настройки на микротике позволяли ходить через VPN на данный адрес.

    Я использую OpenVPN сервер(но думаю логика будет одна и та же), у меня 2 подсети:
    192.168.88.0/24 и 10.10.10.0/24 - Основные.
    10.8.8.0/24 - подсеть VPN клиентов.

    В файерволе разрешён forward в основную сеть.

    В конфиге на клиенте прописано:
    route 10.10.10.0 255.255.255.0 10.8.8.1
    route 192.168.88.0 255.255.255.0 10.8.8.1

    Таким образом я могу получить к этим подсетям доступ через VPN.

    В вашем случае, предполагаю, должно быть что-то типа(на сервере):
    route 192.168.8.1 255.255.255.255 192.168.42.10
    Ответ написан
    Комментировать
  • Как правильно стерилизовать такой запрос Golang + Postgres?

    @ghostiam
    На Go писатель, серверов пинатель.
    На самом деле, здесь несколько решений задачи и все они реализуются в момент скана таблицы.

    Сначала вам нужно будет вытащить все данные, либо в какие нибудь переменные, либо в промежуточную структуру. Если полей мало, то можно в переменные, если много, то удобнее автомапинг на структуру (sqlx со сканом по строке или sqlstruct)

    Я сделаю примеры на основе ручного сканирования, чтобы было понятнее.

    Наша основа:
    spoiler
    type Post struct {
    	ID int64
    	Title string
    	Text string
    }
    
    type UserPosts struct {
    	Username string
    	Posts    []Post
    }
    
    func qna864719(db *sql.DB) ([]UserPosts, error) {
    	query := `select u.username
         , p.post_id
         , p.post_title
         , p.post_text
    from posts p
             join users u on u.user_id = p.post_author
    group by u.username, p.post_id;`
    
    	rows, err := db.Query(query)
    	if err != nil {
    		return nil, fmt.Errorf("query: %w", err)
    	}
    	defer rows.Close()
    
    	var result []UserPosts
    	for rows.Next() {
    		var username string
    		var post Post
    
    		err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
    		if err != nil {
    			return nil, fmt.Errorf("scan: %w", err)
    		}
    
    		// Здесь логика преобразования результата в массив UserPosts
    	}
    
    	err = rows.Err()
    	if err != nil {
    		return nil, fmt.Errorf("after scan: %w", err)
    	}
    
    	return result, nil
    }


    Вариант 1. Более эффективный. Необходима сортировка по Username (order by u.username)
    spoiler
    func qna864719_1(db *sql.DB) ([]UserPosts, error) {
    	query := `select u.username
         , p.post_id
         , p.post_title
         , p.post_text
    from posts p
             join users u on u.user_id = p.post_author
    group by u.username, p.post_id
    order by u.username` // <-------- ОБЯЗАТЕЛЬНАЯ СОРТИРОВКА
    
    	rows, err := db.Query(query)
    	if err != nil {
    		return nil, fmt.Errorf("query: %w", err)
    	}
    	defer rows.Close()
    
    	var result []UserPosts
    
    	// Храним промежуточный результат по юзеру в переменной,
    	// перед добавлением в основной массив
    	var lastResult *UserPosts // <--------
    	for rows.Next() {
    		var username string
    		var post Post
    
    		err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
    		if err != nil {
    			return nil, fmt.Errorf("scan: %w", err)
    		}
    
    		// Здесь логика преобразования результата в массив UserPosts
    
    		// Если промежуточный результат существует, но username отличается
    		// от текущего, то добавляем промежуточный результат в основной массив
    		// обнуляя промежуточный результат
    		if lastResult != nil && lastResult.Username != username {
    			result = append(result, *lastResult)
    			lastResult = nil
    		}
    
    		// Если промежуточного результата нет, иницилизируем его
    		if lastResult == nil {
    			lastResult = &UserPosts{Username: username}
    		}
    
    		// Добавляем посты
    		lastResult.Posts = append(lastResult.Posts, post)
    	}
    
    	err = rows.Err()
    	if err != nil {
    		return nil, fmt.Errorf("after scan: %w", err)
    	}
    
    	// После выхода из сканирования, у нас может остаться промежуточный результат
    	// который необходимо добавить в основной массив
    	if lastResult != nil {
    		result = append(result, *lastResult)
    	}
    
    	return result, nil
    }


    Вариант 2. Более затратный по памяти и тактам процессора, но более простой в написании.
    spoiler
    func qna864719_2(db *sql.DB) ([]UserPosts, error) {
    	query := `select u.username
         , p.post_id
         , p.post_title
         , p.post_text
    from posts p
             join users u on u.user_id = p.post_author
    group by u.username, p.post_id;`
    
    	rows, err := db.Query(query)
    	if err != nil {
    		return nil, fmt.Errorf("query: %w", err)
    	}
    	defer rows.Close()
    
    	var result []UserPosts
    
    	// Иницилизируем хеш-мап который будет содержать посты по username
    	postsByUsername := make(map[string][]Post)
    	for rows.Next() {
    		var username string
    		var post Post
    
    		err = rows.Scan(&username, &post.ID, &post.Title, &post.Text)
    		if err != nil {
    			return nil, fmt.Errorf("scan: %w", err)
    		}
    
    		// Здесь логика преобразования результата в массив UserPosts
    
    		// Добавляем посты в мап
    		postsByUsername[username] = append(postsByUsername[username], post)
    	}
    
    	err = rows.Err()
    	if err != nil {
    		return nil, fmt.Errorf("after scan: %w", err)
    	}
    
    	// Преобразовываем мап в массив
    	for username, posts := range postsByUsername {
    		result = append(result, UserPosts{
    			Username: username,
    			Posts:    posts,
    		})
    	}
    
    	// Сортируем, так как в мапе записи хранятся в "случайном" порядке
    	sort.Slice(result, func(i, j int) bool {
    		return result[i].Username < result[j].Username
    	})
    
    	return result, nil
    }
    Ответ написан
    Комментировать
  • На чем написать rest api для kanban доски?

    @ghostiam
    На Go писатель, серверов пинатель.
    На GO, "православный" стек, это стандартный пакет net/http + какой нибудь внешний роутер, например chi, либо вообще не http/rest, а gRPC (если мы говорим об API).
    Для БД чистый SQL, максимум какой нибудь маппер на структуру, типа sqlx(можно ещё использовать sql builder, но в IDE Goland хорошая поддержка чистого sql).

    ORM очень не советую, есть конечно gorm(генерирует запросы не эффективно, N+1 при связях), для мелких проектов хватит, но вот со сложными запросами любая ORM не справляется.
    Я категорически против ORM, так как за всё время моей работы в вебе, в любом проекте, самым слабым звеном всегда была ORM(долбит кучей запросов БД, когда на чистом sql это 1-2 запроса), либо программист, вместо того, чтобы написать запрос вида "SELECT SUM(amount) FROM book WHERE author = 'Petya'", достаёт все записи и суммирует это в коде(делает работу за БД + тратит ресурсы БД на чтение данных с диска и передачу их по сети).

    Не нужно использовать фреймворки, это не даст опыта написания на go(как и в большинстве языков).
    Go используют, где производительности других решений не достаточно, или нужна многопоточность, что фреймворки так себе обеспечивают.
    Шаблоны в go практически не использую, пишу фронт на vuejs и обращаюсь к go api.
    Есть генераторы документаций, но я тут не посоветую, их нужно тестировать отдельно, у каждого есть свои плюсы и минусы, сам использую другое решение(опишу в конце).

    Самое главное, побыстрее понять, что на go нужно писать как на go, а не так как привыкли в других языках, потому-что, в нём нет привычных классов, строгая типизация, процесс живёт долго и могут быть race condition(гонка состояний) при многопотоке(веб сервер многопоточный), поэтому о подходах из php советую поскорее забыть.

    В своих проектах(в проде), использую go только как сервис предоставляющий апи. Недавно начал использовать grpc-gateway(так как использую gRPC, но так же необходимо делать API доступное через web), проект позволяет описать всё что необходимо в proto файлах (см. gRPC), сгенерировать модели и обёртки под большенство языков + документацию в OpenAPI 2.0. Это не фреймворк, это обёртка над стандартным net/http, которая сразу валидирует и маппит данные на структуру.

    Коротко:
    Для новичка, советую забыть про фреймворки(они не помогут в изучении) и ORM(не эффективно).

    Использовать для веб сервера:
    net/http - встроен в го
    chi(проще) или gorilla/mux - роутер

    Для взаимодействия с БД:
    sqlx - обёртка над стандартным пакетом sql, но позволяет сразу считывать данные в структуру, без ручного сканирования.
    Ответ написан
    3 комментария
  • Как в GO вывести в template struct?

    @ghostiam
    На Go писатель, серверов пинатель.
    Поля структуры должны быть экспортируемые (Начинаться с заглавной буквы), тогда будет доступ к этим полям из шаблона.
    Ответ написан
    1 комментарий