Ответы пользователя по тегу Go
  • Я усложняю или так правильно?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Вы можете использовать любые правила которые считаете удобными.

    Например:
    1. вы можете группировать всё по объектам (так появляются каталоги users/v1)
    2. вы можете группировать всё по слоям (хранилища отдельно, сервисы с хендлерами отдельно, команды отдельны и так далее)

    Вам стоит выбрать правила по которым происходит импорт, иначе может сложиться ситуация когда users/v1 ссылается на groups/v1 а groups/v1 ссылается на users/v1 и приводит к не возможности это скомпилировать.

    Вот пример второго варианта:
    tree .
    .
    ├── Dockerfile
    ├── Makefile
    ├── README.md
    ├── api
    ├── dbconfig.yml
    ├── docker-compose.override.yml
    ├── docker-compose.yml
    ├── env
    │   ├── postgres.ci.env
    │   ├── postgres.env
    │   ├── postgres.example.env
    │   ├── testing.ci.env
    │   ├── testing.env
    │   └── testing.example.env
    ├── examples
    │   └── config.user.add.yaml
    ├── go.mod
    ├── go.sum
    ├── internal
    │   ├── commands
    │   │   ├── cli
    │   │   │   ├── command.go
    │   │   │   └── users
    │   │   │       └── command.go
    │   │   └── server
    │   │       └── command.go
    │   ├── constant
    │   ├── service
    │   │   └── service.go
    │   ├── services
    │   │   └── grpc
    │   │       └── users
    │   │           └── service.go
    │   └── storages
    │       ├── storages.go
    │       └── users
    │           ├── interface.go
    │           ├── postgres
    │           │   ├── add.go
    │           │   ├── storage.go
    │           │   └── storage_test.go
    │           ├── redis
    │           │   ├── add.go
    │           │   ├── storage.go
    │           │   └── storage_test.go
    │           ├── tests
    │           │   ├── add.go
    │           │   ├── delete.go
    │           │   └── generage.go
    │           └── user.go
    ├── main.go
    └── migrations
        ├── 20231217192045-schema.sql.sql
        └── 20231217192051-other.sql.sql
    
    19 directories, 35 files
    Ответ написан
    Комментировать
  • Как шарить экземпляр класса между http хендлерами используя контекст?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    В https://golang.org/pkg/net/http/#Handler находим интерфейс Handler:

    type Handler interface {
            ServeHTTP(ResponseWriter, *Request)
    }


    Создаём реализацию:
    package handlers
    
    import (
            "project/context"
    )
    
    type AddItemsRequestData struct {
            Name string `json:"name"`
    }
    
    type AddItems {
            ctx *context.Context
    }
    
    func NewAddItems(ctx *context.Context) (*AddItems, error) {
            return &AddItems{
                    ctx: ctx,
            }, nil
    }
    
    func (h *AddItems) ServeHTTP(res http.ResponseWriter, req *http.Request) {
            ...
            h.ctx.Profiler.Get("name")   <-- здесь для каждого запроса пользователя вызывается метод Get() заранее инициализированного объекта
            ...
    }


    package main
    
    import (
            "log"
    
            "project/context"
            "project/handlers"
    )
    
    func main() {
            ...
            ctx, err := context.New()
            if err != nil {
                    log.Fatalf(err)
            }
            ...
            http.Handle("/profiler/", CheckHandler(handlers.NewAddItem(ctx)))
            ...
            err := http.ListenAndServe(":80", nil)
            log.Fatalf(err)
    }
    
    // CheckHandler проверяет создание хандлера
    func CheckHandler(handler http.Handler, err error) http.Handler {
            if err != nil {
                    log.Fatalf("handler error: %v", err)
            }
            if handler == nil {
                    log.Fatalf("empty handler")
            }
            return handler
    }


    Это если нужно передать данные в хандлер на этапе инициализации. Если же при каждом запросе нужно что-то инициализировать - то это можно сделать в самом хандлере. Например:

    package handlers
    
    import (
            "project/context"
            "project/profiler"
    )
    
    type AddItemsRequestData struct {
            Name string `json:"name"`
    }
    
    type AddItems {
            ctx *context.Context
    }
    
    func NewAddItems(ctx *context.Context) (*AddItems, error) {
            return &AddItems{
                    ctx: ctx,
            }, nil
    }
    
    func (h *AddItems) ServeHTTP(res http.ResponseWriter, req *http.Request) {
            ...
            pr := profiler.New() <-- здесь для каждого запроса пользователя создаётся новый экземпляр
            pr.SetName("name")
            ...
    }


    В качестве project/context используется обычный пакет со структурой в которой хранятся любые объекты. Например:
    package context
    
    import (
            "project/profiler"
    )
    
    // Context содержит необходимые библиотеки, такие как хранилища например
    type Context struct {
            Profiler *profiler.Profiler
    }
    
    // New возвращает новый Context
    func New(
            profiler *profiler.Profiler
    ) (
            *Context,
    ) {
            context := &Context{
                    Profiler: profiler,
            }
            return context
    }
    Ответ написан
    1 комментарий
  • Как записать число дробью в Go?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Сделайте структуру, передавайте в неё текст, дальше сделайте из этого пакет и добавьте методы по управлению, конвертации и так далее...

    package main
    
    import (
    	"fmt"
    	"strings"
    	"errors"
    )
    
    func main() {
    	fmt.Println("Hello, playground")
    	
    	d := New()
    	d.Parse("1/4")
    	fmt.Println(d)
    }
    
    type Shot struct {
    	i string                   // number
    	f string                   // fraction
    }
    
    func New() (s *Shot) {
    	s = new(Shot)
    	return
    }
    
    func (s *Shot) Parse(text string) (err error) {
    	v := strings.Split(text, "/")
    	if len(v) != 2 {
    		err = errors.New("Error parse shot")
    		return	
    	}
    	s.i = v[0]
    	s.f = v[1]
    	return
    }
    Ответ написан
    Комментировать
  • Как парсить время на Go?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    alst161: можете так же посмотреть библиотеку https://github.com/mantyr/newtime , специально для того что бы кодировать в более привычном виде сделал. Внутри time и ряд функций для работы в объектном стиле.

    Например так:
    package main
    
    import (
        "github.com/mantyr/newtime"
        "fmt"
    )
    
    func main() {
        date := newtime.NewTime()
        date.Parse("16-08-2016 15:11:06", "dd-mm-yyyy HH:ii:ss")
        fmt.Println(date.Format("YYYY-mm-dd HH:ii:ss"))  // "2016-08-16 15:11:06"
    }
    Ответ написан
    Комментировать
  • Go. Почему перенаправляет на "/"?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    https://ru.wikipedia.org/wiki/Список_кодов_состоян...

    • 301 Moved Permanently — запрошенный документ был окончательно перенесен на новый URI, указанный в поле Location заголовка. Некоторые клиенты некорректно ведут себя при обработке данного кода. Появился в HTTP/1.0.
    • 302 Found, 302 Moved Temporarily — запрошенный документ временно доступен по другому URI, указанному в заголовке в поле Location. Этот код может быть использован, например, при управляемом сервером согласовании содержимого. Некоторые клиенты некорректно ведут себя при обработке данного кода. Введено в HTTP/1.0.
    • 303 See Other — документ по запрошенному URI нужно запросить по адресу в поле Location заголовка с использованием метода GET несмотря даже на то, что первый запрашивался иным методом. Этот код был введён вместе с 307-ым для избежания неоднозначности, чтобы сервер был уверен, что следующий ресурс будет запрошен методом GET. Например, на веб-странице есть поле ввода текста для быстрого перехода и поиска. После ввода данных браузер делает запрос методом POST, включая в тело сообщения введённый текст. Если обнаружен документ с введённым названием, то сервер отвечает кодом 303, указав в заголовке Location его постоянный адрес. Тогда браузер гарантировано его запросит методом GET для получения содержимого. В противном случае сервер просто вернёт клиенту страницу с результатами поиска. Введено в HTTP/1.1.
    • 304 Not Modified — сервер возвращает такой код, если клиент запросил документ методом GET, использовал заголовок If-Modified-Since или If-None-Match и документ не изменился с указанного момента. При этом сообщение сервера не должно содержать тела. Появился в HTTP/1.0.
    • 305 Use Proxy — запрос к запрашиваемому ресурсу должен осуществляться через прокси-сервер, URI которого указан в поле Location заголовка. Данный код ответа могут использовать только исходные HTTP-сервера (не прокси). Введено в HTTP/1.1.
    • 306 (зарезервировано) — использовавшийся раньше код ответа, в настоящий момент зарезервирован. Упомянут в RFC 2616 (обновление HTTP/1.1).
    • 307 Temporary Redirect — запрашиваемый ресурс на короткое время доступен по другому URI, указанный в поле Location заголовка. Метод запроса (GET/POST) менять не разрешается. Например, POST запрос должен быть отправлен по новому URI тем же методом POST. Этот код был введён вместе с 303 вместо 302-го для избежания неоднозначности. Введено в RFC 2616 (обновление HTTP/1.1).


    Если в крадце, то разница лишь в нюансах.
    • 303 сделает временный редирект и уберёт POST запрос заменив его на GET
    • 307 сделает POST запрос на новый адрес если изначально был именно POST


    Для себя сделал вот так:
    package tmpl
    
    import (
        "net/http"
    )
    
    func Redirect(w http.ResponseWriter, address string) {
        if address == "" {
            address = "/"
        }
        w.Header().Set("Location", address)
        w.Header().Set("Cache-Control", "private, no-store, max-age=0, must-revalidate")
        w.WriteHeader(303)                                                                      // see https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP#303
    }
    Ответ написан
    Комментировать
  • Стоит ли подготовить все запросы в init?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Прежде чем использовать подготовленные запросы прочитайте это:
    - go-database-sql.org/prepared.html
    - go-database-sql.org/surprises.html

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

    Если вам важно увеличить пропускную способность простых INSERT то посмотрите в сторону HandlerSocket, уверен под Golang оно уже должно было появиться, если нету - сможете реализовать самостоятельно, протокол не особо сложный.
    Ответ написан
    Комментировать
  • Как удалить первый елемент из map?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Обычный map не гарантирует порядок. Варианты:
    • можно сделать ordered map, но с этим окуратнее, куча подводных камней
    • можно хранить в элементах время и либо ходить по нему через range удаляя все устаревшие элементы либо делать проверку при доступе к данным, если данные устарели - удаляем.


    Можно сделать чуть проще:
    • делаем ch := make(chan string, 100000)
    • далее время от времени смотрим len(ch) и если он выше ну скажем половины от того что туда может влезть (подберите имперически исходя из количества и скорости пополнения данными или высчитывайте гибко) и удаляете тот элемент что первым читается из ch


    https://play.golang.org/p/sopEgcwRZC
    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    type Map struct {
        sync.RWMutex
        ch chan string
        d map[string]string
    }
    
    func NewMap() (m *Map) {
        m = new(Map)
        m.ch = make(chan string, 10000)
        m.d  = make(map[string]string)
        return
    }
    
    func (m *Map) Add(key, value string) {
        m.Lock()
        defer m.Unlock()
        m.check(50, true)
        m.d[key] = value
        m.ch <- key
    }
    
    func (m *Map) Get(key string) string {
        m.RLock()
        defer m.RUnlock()
        if v, ok := m.d[key]; ok {
            return v
        }
        return ""
    }
    
    func (m *Map) Check() {
        m.check(50, false)
    }
    
    func (m *Map) check(len_max int, is_lock bool) {
        ch_len := len(m.ch)
        if ch_len > len_max {
            if !is_lock {
                m.Lock()
                defer m.Unlock()
            }
    
            ch_len = len(m.ch)
            for i := 0; i < ch_len - len_max; i++ {
                delete(m.d, <- m.ch)
            }
        }
    }
    
    func (m *Map) Len() int {
        return len(m.ch)
    }
    
    func main() {
    	m := NewMap()
    	for i := 0; i < 1000; i++ {
                m.Add(fmt.Sprintf("key_%s", i), fmt.Sprintf("value_%s", i))	
    	}
    	fmt.Println(m.Len())
    }

    • храним не более len_max элементов+1 в map, при этом удаляем сначала самые старые в порядке их добавления
    • есть проблема - если ключ был добавлен несколько раз то программа не видит что элемент обновился и всё равно его удалит как старый
    • что бы этого не происходило нужно добавлять дату в ключь и проверять, если ключ имеет другую дату (или вместо даты инкрементный счётчик) то такой элемент пропускается (мы знаем что он уже где-то есть дальше в m.ch)
    Ответ написан
  • Golang Benchmark почему разные результаты?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Во время подсчёта "покрытия" часть ресурсов тратится на эту задачу и она не обязательно должна быть эффективной. По этому бенчмарк в это время ресурсов недополучает и вообще может показывать не корректные замеры так как фоновые процедуру могут выполняться и нагружать CPU не равномерно.

    При бенчмарке желательно:
    • что бы нигде ничего не было запущено
    • что бы нигде ничего не работало в фоне
    • что бы не нужные куски кода были отделены от таймера (например не нужные инициализации, подготовка данных)

    Нужны ли бенчмарки? Бенчмарки нужны что бы отбраковать совсем плохие результаты или выбрать между двумя очень похожими. Так же они дают представление о том сколько тратится на каждую итерацию того кода что вы тестируете.

    Пример простого бенчмарка с инициализацией вынесеной за таймер:

    package conf // <-- здесь название библиотеки для которой предназначены бенчмарки
    
    import (
        "testing"
    )
    
    func BenchmarkLoadGo(b *testing.B) {
        conf := NewConfig()
        conf.SetDefaultFile("properties")
        conf.SetDefaultCatalog("./testdata") // default "./configs"
    
        b.ResetTimer() // <-- обнуляем таймер что бы вся "сложная" и "единомоментная" работа не попала в подсчёт
    
        for i := 0; i < b.N; i++ {
            go conf.GetSection("server_1", "storage") // <-- в данном случае тестируется сценарий в конкурентном виде, но чаще всего этого не требуется и конкретно в том коде откуда взят бенчмарк есть отдельный бенч для не конкурентного доступа и сравнивая их можно иметь представление о том как оно себя поведёт в обоих сценариях.
        }
    }
    Ответ написан
    Комментировать
  • Стоит ли так делать?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    len([]byte("string")) // <-- количество байт

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

    По этому размер "таблицы" или "базы данных" в каждом случае считается по своему.

    Из in-memory баз данных с гарантированным сохранением на диск сейчас активно пиарят Tarantool, там как раз все данные в памяти, но при записи они становятся доступными только когда произошла запись в лог на диск. Если есть достаточное количество оперативки то хороший выбор, на нём живёт множество сервисов "большого" русского интернета.

    Если оперативки мало (500 мегабайт например), то больше подойдёт классическая база данных с кешированием горячих данных.
    Ответ написан
    Комментировать
  • Как бы вы организовали кодогенерацию сайта?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Берите уже PHP, что уж там:)
    Ответ написан
  • Как юзать WebSocket?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    package main
    
    import (
        "github.com/gorilla/websocket"
        "net/http"
    )
    
    var upgrader = websocket.Upgrader{
        ReadBufferSize:  4096,
        WriteBufferSize: 4096,
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
    }
    
    func wsHandler(w http.ResponseWriter, r *http.Request) {
        // вот тут вы можете проверить login/password и прочее или же сделать для этого отдельный метод /login, а тут проверять лишь есть ли у него сессия
        ws, err := upgrader.Upgrade(w, r, nil)
        if _, ok := err.(websocket.HandshakeError); ok {
            http.Error(w, "Not a websocket handshake", 400)
            return
        } else if err != nil {
            return
        }
    
        ws_list.Append(ws, r) // вот тут вы уже знаете что сокет вам подходит и от какого он пользователя и дальше заносите его в какое-то хранилище которое их хранит и по которому потом рассылаете данные.
    }


    И опять таки, читайте документацию, https://godoc.org/github.com/gorilla/websocket
    Ответ написан
  • Как элегантно реализовать задания на сайте?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    https://play.golang.org/p/NJ-X38UrWC

    В дополнение к комментарию на другой ответ.
    package events
    
    import (
      "sync"
    )
    
    var events *Events
    
    type Events struct {
        sync.RWMutex
        list map[string]func(ps []string)
    }
    
    func NewEventsList() (list *Events) {
        list = new(Events)
        list.list = make(map[string]func(ps []string))
        return list
    }
    
    func init() {
      events = NewEventsList()
    }
    
    func Add(name string, callback func(ps []string)) {
        events.Add(name, callback)
    }
    
    func Call(name string, ps []string) bool {
        return events.Call(name, ps)
    }
    
    
    // Подписка на событие
    func (l *Events) Add(name string, callback func(ps []string)) {
        l.Lock()
        defer l.Unlock()
        l.list[name] = callback
    }
    
    // Вызов события
    func (l *Events) Call(name string, ps []string) bool {
        l.RLock()
        defer l.RUnlock()
        if f, ok := l.list[name]; ok {
            go f(ps)
            return true
        }
        return false
    }
    Ответ написан
    Комментировать
  • Как распарсить JavaScript Object (не валидный JSON) в Golang?

    @mantyr Автор вопроса
    Пишу много Golang кода с удовольствием:)
    Я пока не сравнивал два эти варианта по производительности, но в будущих задачах предпочту использовать первый вариант.

    Вариант №1
    На stackoverflow посоветовали взглянуть на библиотеку https://godoc.org/launchpad.net/rjson#Unmarshal однако в ней не оказалось поддержки значений в одинарных ковычках.

    В результате сделал форк и добавил поддержку одинарных ковычек.
    package main
    
    import (
        "fmt"
        "github.com/mantyr/rjson"
    )
    
    func main() {
        data := []byte(`{
            middle : {
                src: "pictures/product/123.jpg",
                place : '#preview-img',                // Added support for JavaScript object parsing
                title: "title"
            }
        }`)
    
        var v struct {
            Middle struct {
                Src string
                Place string
                Title string
            }
        }
        err := rjson.Unmarshal(data, &v)
        fmt.Println(v)   // print {{pictures/product/123.jpg #preview-img title}}
        fmt.Println(err) // print <nil>
    }

    https://github.com/mantyr/rjson
    Оригинальный пакет от автора тут: https://github.com/rogpeppe/rjson

    Если у вас есть пожелания по дополнению rjson - напишите мне или предложите pool request.

    Вариант №2
    Можно запустить VM https://github.com/robertkrimen/otto и передав ей немного дополненный текст получить из неё нужное поле.
    package main
    
    import (
        "testing"
        "github.com/robertkrimen/otto"
    )
    
    func BenchmarkVMGet(b *testing.B) {
        vm := otto.New()
    
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
            st := `
                {
                        middle : {
                        src: 'pictures/product/middle/14906_middle.jpg',
                        place : '#preview-img',
                        title: '343880-090-slantsy-nike-benassi-just-do-it'
                    }
                }
            `
    
            vm.Run(`
                obj = `+st+`
                src = obj.middle.src;
            `)
            src, err := vm.Get("src")    // <-----
            _ = src
            _ = err
        }
    }


    # go test -bench=".*" ottotest
    testing: warning: no tests to run
    PASS
    BenchmarkVMGet	   30000	     58103 ns/op
    ok  	ottotest	2.367s


    Однако есть минусы:
    • может прилететь произвольный кусок текста и выполнять его бездумно не правильно (вспомним eval в php)
    • достаточно медленно
    • используется очень большая библиотека - плохая зависимость


    Плюсы:
    • всё таки достаточно быстро
    • можно не останавливать VM, а пушить туда через Run, но если пушить бесконечно остаётся вопрос - умрёт однажды или не умрёт - без хорошего знания внутренностей библиотеки сказать этого заранее нельзя


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

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Всегда можно попасть в ситуацию когда:
    • Delims менять нельзя (так как много кода уже с ним)
    • Когда Delims символы нужны только в одном месте и надо бы вывести их нативно
    <script>
        var title = '{{title}}';
        var obj = {{ { }}{{ } }}       // var obj = {}
    </script>
    Ответ написан
    Комментировать
  • Как лучше запустить серьезный GO веб-сервис в продакшен?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    supervisord и если его функций для внешнего мониторинга и управления в будущем не зватит - собственный supervisor написанный на Golang под собственные требования.

    Надо учитывать что supervisord это python программа, но со своей задачей справляется на ура и подходит для запуска чего угодно.
    Ответ написан
    Комментировать
  • Использовать ли глобальные переменные для конфигов в golang?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Вот вопросы которые возникают при написании движка для конфигов:
    • количество копий данных
    • конкуентный доступ (что если программа должна будет обновлять часть конфига на лету)
    • сохранение конфига на диск в случае изменения программой и мердж с уже имеющимся конфигом на диске в случае если он уже был изменен в ручную и не был загружен приложением до момента мерджа
    • доступ из нескольких пакетов (как правило тех где бизнес-логика, а не универсальных библиотек)
    • формат хранения данных, xml, json, ini или любой другой вариант, включая базу данных
    • своевременное обновление конфига при изменении из вне. С перезагрузкой приложения, без перезагрузки в автоматическом режиме (программа сама отслеживает изменился ли файл конфига) в режиме получения сигнала на то что конфиг нужно перечитать.


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

    Идеального конфига под Golang пока не видел, но есть очень интересные.

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

    Некоторые конфиги работающие по принципу "возьмём файл и скиним его на структуру" в этом случае не работают потому что структура не совпадает. А не совпадать она может очень часто, потому что клиентам и их сотрудникам заранее не известно что это очень чувствительное место программы.
    Ответ написан
  • 2d движки на golang или как делать просчёт координат на сервере?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    По сути у вас две задачи:
    1. анимация перемещения
    2. взаимодействие с другими предметами/игроками

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

    @mantyr
    Пишу много Golang кода с удовольствием:)
    То что вы сделали называется embed (встраивание). Можно отойти от него явно и не путаться.

    type Animal struct {
      Name string
    }
    
    type Rabbit struct {
      Ani Animal
    }
    
    func (this *Rabbit) Walk() *Rabbit{
      this.Ani.Walk()
      fmt.Println("...and Jump")
      return this
    }
    
    func (this *Rabbit) Say() *Rabbit{
      this.Ani.Say()
      return this
    }

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

    Так же можно не дублировть функции так:
    rabbit := Rabbit{ }
      rabbit.Ani.Name = "Кроль"
      rabbit.Ani.Walk()
      rabbit.Ani.Say()
      rabbit.Ani.Walk()
    Ответ написан
    2 комментария
  • Golang mysql: Invalid Connection?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Прослеживаем цепочку через документацию
    1. https://github.com/go-sql-driver/mysql используется совместно со стандартным https://golang.org/pkg/database/sql/
    2. находим в https://golang.org/pkg/database/sql/ функцию
    func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

    3. видим что результат *Rows, находим эту структуру и изучая находим функцию
    func (rs *Rows) Close() error
    4. точно такую же функцию видим у Stmt
    func (s *Stmt) Close() error
    5. делаем вывод что надо закрывать соединения, в определённых случаях, принудительно

    Подобный вопрос был на toster.ru уже разва два.
    Вот тут можете прочитать дискуссию по поводу особенностей работы с MySQL в Golang: https://toster.ru/answer?answer_id=685014#comments...

    Полезные ссылки для тех кто хочет разобраться в вопросе:
    4gophers.ru/article/go-i-sql-bazy-dannyh
    go-database-sql.org/surprises.html
    Ответ написан
    1 комментарий
  • Си или Go для приложения по обработке сетевого трафика до 1.5М пакетов/с?

    @mantyr
    Пишу много Golang кода с удовольствием:)
    Для обработки сетевого трафика больше подойдёт модуль ядра Linux написанный на Си. Если кто-то умеет такое делать на Golang - дайте знать, с удовольствием послушаю, почитаю на эту тему... Агрегированные же данные (прилетающие от такого модуля) вполне хорошо обрабатывать на Golang (в этом случае можно не заморачиваться одной железкой, а поставить сразу несколько распределив по ним трафик).

    P.S. Возможно речь идёт не о linux, но в топике это как-то отдельно не оговаривалось.
    Ответ написан
    4 комментария