• Как структурировать JSON объект?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Так как вы делаете не получится :)

    Вы получаете данные, которые нужно преобразовать из JSON в структуру данных Go).
    и дальше с ними работать, а вы преобразовываете JSON (слайс байтов) в слайс байтов.

    Вот рабочий пример того, что вы хотите сделать
    package main
    
    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    )
    
    func main() {
        artist := "Madonna"
        token := "5936fb55e90cdd9938f8e7086c783c40"
        url := fmt.Sprintf(
            "http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=%s&api_key=%s&format=json", artist, token)
        res, err := http.Get(url)
        if err != nil {
            log.Fatal(err)
        }
        defer res.Body.Close()
    
        body, _ := ioutil.ReadAll(res.Body)
    
        var data map[string]interface{}
        // десериализуете JSON
        err = json.Unmarshal(body, &data)
        if err != nil {
            log.Fatal(err)
        }
    
        // сериализуете данные в JSON
        marshaledDataBytes, err := json.MarshalIndent(data, "", "\t")
        fmt.Println(string(marshaledDataBytes))
    }
    Ответ написан
    8 комментариев
  • Ошибка: package ....... is not in GOROOT. Как решить?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Лучше установить пакет так, как написано в его инструкции.
    Выполните в командной строке
    go get github.com/adshao/go-binance/v2

    Ну и импорт потом вот так
    import (
        "github.com/adshao/go-binance/v2"
    )
    Ответ написан
    2 комментария
  • Как установить pkg-config?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Скачайте бинарники + зависимости тут ftp.gnome.org/pub/gnome/binaries/win32/dependencies
    pkg-config_0.26-1_win32.zip
    glib_2.28.8-1_win32.zip
    gettext-runtime_0.18.1.1-2_win32.zip

    Распакуйте их в одну из папок, которая у вас $PATH прописана, или в отдельную папку и пропишите к ней путь в $PATH

    А лучше поставьте Cygwin https://cygwin.com/install.html, у вас отпадёт еще куча разных проблем
    Ответ написан
    Комментировать
  • Как сделать в Go, чтобы какая-то goroutine выполнялась циклично до тех пор, пока state не станет false?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    В вашей реализации надпись "I am doing this!" будет показана только один раз, т.к. вы пишите всего 1 раз в канал, а потом выполнение заблокируется на чтении данных из канала, т.к. там данных не будет.

    То, что вы хотите сделать можно сделать вот так:
    package main
    
    import (
        "log"
        "time"
    )
    
    func doSomethingEvery5Seconds(ch chan bool) {
        log.Println("Started!")
        for {
            select {
            case <-ch:
                return
            default:
                log.Println("I am doing this!")
                time.Sleep(1 * time.Second)
            }
        }
    }
    
    func main() {
        ch := make(chan bool, 1)
        go doSomethingEvery5Seconds(ch)
        time.Sleep(15 * time.Second)
        close(ch) // Останавливаем спустя 15 секунд
    }
    Ответ написан
    2 комментария
  • Почему db.Find сьедает одно из значений таблицы?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    99%, что дело в названиях полей
    Если из базы вы извлекаете данные в структуру Post - допишите там db:"userId", чтобы получилось примерно так
    UserId int `json:"userId" db:"userId"`
    или для gorm
    UserId int `json:"userId" gorm:"column:userId"`
    Ответ написан
    2 комментария
  • Можно ли как-то перехватить команды, которые исполняются через терминал в ubuntu?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Там храниться только история команд, которые были введены в bash.
    Если у вас будет другая shell - список команд в .bash_history вы не увидите.

    Например у меня может запускаться bash сразу после входа в систему, но я потом запущу, например, zsh.
    bash.history будет пустым.
    Это я к тому, что даже если вы сделаете свою shell, то её можно будет легко обойти.

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

    Я делал нечто подобное для Unix FreeBSD лет 20 назад :))
    Ответ написан
    Комментировать
  • Почему Возвращает букву?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Потому, что 65 - это код английской буквы "A".

    Если вы хотите чтобы отобразило 65, нужно использовать либо модуль strconv, либо fmt.
    package main
    
    import "fmt"
    import "strconv"
    
    func main() {
      var i int64 = 65
      fmt.Println(strconv.FormatInt(i, 10))
      // или так
      fmt.Printf("%d\n", i)
    }
    Ответ написан
    2 комментария
  • Как записать данные полученные в формате JSON в БД?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Обратите внимание, что вы запрашиваете один пост, но в ответ приходит массив.
    Это видно по скобкам вначале и конце ответа "[" .... "]",
    [
      {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
      }
    ]

    Т.е. декодировать ответ нужно как массив, а не отдельный пост

    Исправил ошибки и отрефакторил ваш код.
    Глобальные переменные тоже лучше не использовать.
    Надеюсь так станет немного понятнее :)
    Если будет что-то не понятно - пишите, постараюсь помочь.

    В общем вот так будет работать.
    package main
    
    import (
        "database/sql"
        "encoding/json"
        "log"
        "net/http"
    
        _ "github.com/go-sql-driver/mysql"
    )
    
    type Post struct {
        Userid int    `json:"userId"`
        ID     int    `json:"id"`
        Title  string `json:"title"`
        Body   string `json:"body"`
    }
    
    func main() {
        posts, err := fetchPosts("https://jsonplaceholder.typicode.com/posts?id=1")
        if err != nil {
            log.Fatal(err)
        }
    
        db, err := sql.Open("mysql", "root:password@/Posts")
        if err != nil {
            log.Fatal(err)
        }
    
        err = savePosts(db, posts)
        if err != nil {
            log.Fatal(err)
        }
    }
    
    func fetchPosts(url string) ([]Post, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }
    
        var posts []Post
        err = json.NewDecoder(resp.Body).Decode(&posts)
        return posts, err
    }
    
    func savePosts(db *sql.DB, posts []Post) error {
        for _, post := range posts {
            _, err := db.Exec("insert into post (id, user_id, title, body) values(?, ?, ?, ?)",
                post.ID, post.Userid, post.Title, post.Body)
            if err != nil {
                return err
            }
        }
    
        return nil
    }
    Ответ написан
    22 комментария
  • Где можно использовать горутины в Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Самый простой пример - это HTTP сервер.
    Когда к вам приходит HTTP запрос - создаётся горутина и каждый запрос спокойно обрабатывается, никто никого не ждёт. Т.е. сервер может обрабатывать параллельно несколько запросов как раз за счёт горутин.

    Фактически, почти во всех сетевых сервисах есть смысл использовать горутины для распараллеливания обработки запросов (кроме epoll, kqueue и т.п.)

    Еще хороший пример.
    Вам нужно сделать парсер Авито :)) или индесатор сайтов.
    Т.е. задача сводится к тому, что нужно будет постоянно качать страницы с сайтов.
    Если вы будете качать одну страницу за другой - вы будете очень долго качать.
    А так вы можете сделать пул из нескольких горутин (как минимум по 1 на поток процессора) и получите параллельное скачивание страниц.

    Для ускорения работы вычислений (параллельные вычисления) есть смысл использовать горутины только по 1 на поток процессора). Т.е. в случаях, когда у вас огромный объём данных и нужно максимально ускорить его обработку. Например у вас файл 100Гб, и 8 ядер в процессоре - вы можете разбить файл на 8 частей и параллельно эти части обработать (в самом простом варианте, в реальной жизни нужно будет просто по частям считывать новые данные и скармливать свободной горутине)

    Еще когда нужно сделать чтобы какая нибудь задача выполнялась в "фоне".
    Например периодическая очистка кеша/устаревший записей в базе.

    Реальных примеров намного больше :)
    В целом их есть смысл использовать там, где нужно параллельное выполнение какой либо задачи или разных задач параллельно :)
    Ответ написан
    2 комментария
  • Как разбить GO проект на отдельные файлы, и потом подключать их в main?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно сделать через `go mod`, примерно вот так:

    1. Создаёте папку yourproject.com/ там, где вам удобно
    2. cd yourproject.com/ ; go mod init yourproject.com
    3. Создаёте пакет pkg1 внутри yourproject.com, т.е. yourproject.com/pkg1
    4. Создаёте main.go
    5. Запускаете командой go run .

    Структура проекта
    yourproject.com/main.go
    yourproject.com/go.mod (появится после выполнения команды go mod init)
    yourproject.com/pkg1/pkg1.go


    yourproject.com/main.go
    package main
    
    import "yourproject.com/pkg1"
    
    func main() {
        pkg1.SomeFunc()
    }


    yourproject.com/pkg1/pkg1.go
    package pkg1
    
    import "fmt"
    
    func SomeFunc() {
        fmt.Println(`SomeFunc called`)
    }
    Ответ написан
    6 комментариев
  • Как организовать тесты в пакетах вида адаптер?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    У меня в некоторых проектах схема очень похожа на вашу.
    Я выношу тесты и инициализацию в папку repo.
    Структура папок выглядит таким образом (беру пакет users для примера)
    users/repo.go - описание интерфейса Repo
    users/repo/repo.go - тут инициализация любого типа реализации в зависимости от конфига
    users/repo/repo_test.go - тут тесты
      (создаю все типы репозиториев (mongo, redis...)) и одними и теми же тестами прогоняю каждый тип. 
      Тут и инициализация репозиториев для тестов и зачистка после тестов.)
    users/repo/mongodb - реализация (импортирует пакет users при необходимости)
    users/repo/redis
    users/repo/sqlite

    В приложении (там, где идёт инициализация) импортируется только пакет "users/repo", он (на основе конфига) подключается базе нужного типа.
    А все остальные пакеты (если кому нужен интерфейс users.Repo) - импортируют пакет users
    Ответ написан
    5 комментариев
  • В каких приложениях Go существенно эффективнее чем Node.js и PHP?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Бенчмарки - это хорошо, но очень важно понимать что именно там меряли и почему результаты именно такие.

    Несколько лет назад я тоже делал бенчмарки Python, PHP, Node, Go.
    Для меня были важны две вещи:
    1 - скорость ответа сервера/кол-во запросов в секунду
    2 - объём сервиса в памяти, т.к. от этого зависит стоимость ресурсов

    На тесте, где сервисы не делали запросы в базу - из всех четверых лучше всего отработал Go с приличным отрывом, цифры, к сожалению, уже не помню.

    Но вся эта разница сошла на нет, как только добавился всего один простой SQL запрос в базу, в таблицу с 10 строками. И на этом фоне разница по скорости ответа была меньше 10%.

    Иными словами если ваш сервис работает с базой - критической разницы по скорости работы между Go/Rust/PHP/Node/Java, особо не будет.

    Другое дело если ваш сервис не будет делать запросы в базу, или будет кешировать результаты запросов, тогда вы почувствуете ощутимую разницу.

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

    Один экземпляр Go занимал в памяти порядка 6мб RAM, при том, что Pytho+Django порядка 60мб.
    Node уже не помню сколько, но что-то близкое к Python'у.

    Вот тут уже, когда серверов у вас будет много - количество серверов с Go у вас будет в 10 раз меньше, соответственно расходы за эти сервера у вас будут в 10 раз меньше :)

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

    Где-то читал статью, что у людей было API на порядка 40 серверов на Node, после переписывания на Go - серверов осталось два, из которых второй запасной :)
    Ответ написан
    13 комментариев
  • Почему не видна функция определённая в другом файле, но в том же пакете?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Суть проблемы в том, что вы указываете только файл с тестом, а нужно указать еще и файл с декларацией и реализацией (если они разные).

    Вот так будет работать:
    go test -v ./driver_test.go ./driver.go

    Подозреваю, что реально вы хотели сделать чтобы у вас выполнялись не все тесты в пакете, а конкретный один, если так - тогда проще делать вот так (я сам так часто делаю :)))
    go test -v -run ^Test_Deposit$
    Ответ написан
    Комментировать
  • Как удалить письмо через imap в golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Нужно сначала отметить сообщения как удалённые (установить флаг), потом вызвать Expunge.
    В документации написано, что если вызвать Close - то сообщения, отмеченные как удалённые, будут удалены, но я не проверял :)

    Вот тут есть пример: https://godocs.io/github.com/emersion/go-imap/clie...
    var c *client.Client
    
    mbox, err := c.Select("INBOX", false)
    if err != nil {
        log.Fatal(err)
    }
    
    if mbox.Messages == 0 {
        log.Fatal("No message in mailbox")
    }
    
    seqset := new(imap.SeqSet)
    for _, message := range mbox.Messages {
         // если сообщение должно быть удалено - тогда вызываете AddNum
         // используем именно message.Uid а не message.SeqNum потому, что ниже мы будем вызывать UidStore, а не Store, так будет явно надёжнее
         seqset.AddNum(message.Uid)
    }
    
    // отмечаем сообщения как удалённые
    item := imap.FormatFlagsOp(imap.AddFlags, true)
    flags := []interface{}{imap.DeletedFlag}
    if err := c.UidStore(seqset, item, flags, nil); err != nil {
        log.Fatal(err)
    }
    
    // удаляем сообщение
    if err := c.Expunge(nil); err != nil {
        log.Fatal(err)
    }
    
    log.Println("Last message has been deleted")
    Ответ написан
  • Какой подход использовать при написании кода для crud в БД с большим количеством таблиц?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я использую самописный генератор кода.

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

    Есть много готовых генераторов, можете посмотреть в интернете и выбрать какой больше понравится.
    Вот неплохая статья с примерами
    https://medium.com/@zaurio/generator-the-client-to...
    Ответ написан
    1 комментарий
  • Почему приложения на go падает при ошибке postgres?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Из за log.Fatalf
    Ответ написан
  • Как сравнить время?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вы создаёте время в UTC зоне, а когда вызываете time.Now() - он возвращает вам время в вашей зоне, не в UTC.
    Просто добавьте вызов UTC() в time.Now().UTC()

    var dateStart = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 9,  0, 0, 0, time.UTC)
    var dateEnd   = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 17, 0, 0, 0, time.UTC)
    
      if dateStart.Before(time.Now().UTC() ){
        println("star ok")
      }
      if dateEnd.After(time.Now().UTC()) {
        println("end ok")
      }
    Ответ написан
    2 комментария
  • Как записывать stdout в буфер и считывать оттуда построчно?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вы всё верно понимаете, горутину можно не использовать.

    Вы можете сделать что-то типа такого
    работающий вариант

    package main
    
    import (
        "bufio"
        "bytes"
        "fmt"
        "io"
        "log"
        "os/exec"
    )
    
    type Executor struct {
        wr io.Writer
    }
    
    func (e *Executor) SetStdout(w io.Writer) {
        e.wr = w
    }
    
    func (e *Executor) Exec() error {
        cmd := exec.Command("ls", "/etc")
        cmd.Stdout = e.wr
        err := cmd.Run()
        return err
    }
    
    type BufferedBrokerWriter struct {
        buffer  bytes.Buffer
        scanner *bufio.Scanner
    }
    
    func NewBufferedBrokerWriter() *BufferedBrokerWriter {
        bw := &BufferedBrokerWriter{}
        bw.scanner = bufio.NewScanner(&bw.buffer)
        return bw
    }
    
    func (bw *BufferedBrokerWriter) Write(p []byte) (int, error) {
        return bw.buffer.Write(p)
    }
    
    func (bw *BufferedBrokerWriter) ReadLines(cnt uint) ([]string, error) {
        lines := make([]string, 0, cnt)
        linesCnt := uint(0)
    
        for bw.scanner.Scan() {
            line := bw.scanner.Text()
            err := bw.scanner.Err()
            if err != nil || line == `` {
                return lines, err
            }
    
            lines = append(lines, line)
            linesCnt++
            if linesCnt >= cnt {
                return lines, nil
            }
        }
    
        return nil, io.EOF
    }
    
    func main() {
        // подключаетесь к вашему брокеру сообщений,
    
        bw := NewBufferedBrokerWriter()
    
        e := &Executor{}
        e.SetStdout(bw)
    
        err := e.Exec()
        if err != nil {
            log.Fatal(err)
        }
    
        for {
            lines, err := bw.ReadLines(2)
            if err == io.EOF {
                break
            }
            fmt.Printf("Lines:\n%+v\n\n", lines)
        }
    }


    если возникнут сложности с реализацией - напишите, помогу
    Ответ написан
  • Можно ли в Golang изменить длину среза напрямую?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно попробовать сделать через reflect, например так
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func modify(input *[]int, newLen int) {
        val := reflect.Indirect(reflect.ValueOf(input))
        val.SetLen(newLen)
    }
    
    func main() {
        input := []int{1, 2, 3}
    
        fmt.Printf("Before: %p, value: %v\n", &input, input)
    
        modify(&input, len(input) - 1)
    
        fmt.Printf("After: %p, value: %v\n", &input, input)
    }

    Вывод:
    Before: 0xc00000c060, value: [1 2 3]
    After: 0xc00000c060, value: [1 2]
    Ответ написан
  • Go как добавить в поле в структуру которую встраиваю?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если я правильно понял как нужно сделать - то вот так можно:
    type Response struct {
      Code int `json:"code"`
      Message string `json:"message"`
    }
    
    type LoginResponse struct {
      Response
      Token string
    }


    JSON будет нормально сериализоваться, а инициировать структуру нужно будет вот так
    response := LoginResponse{
           Response : Response {
             Code: 10,
             Message: `error / success message`,
           },
           Token: `token`,
        }
    Ответ написан