Ответы пользователя по тегу Go
  • Как получить значения высшего по уровню range в template golang?

    Tyranron
    @Tyranron
    Создать переменные итерации для внешнего цикла:

    <form method="POST" action="/saveLucr">
            {{ range $i, $p := .Proflist }}
            <b> <input name="name_lucru" type="text" value="{{ $p.NameProf }}" />&nbsp;&nbsp;</b>
            <b> <input name="orar_prof" type="text" value="{{ $p.OrarProf }}" /> </b>
           <label  id="addProfesie">add profesie</label>
            <table id="{{ $p.NameProf }}">
                <tbody>
                    {{ range $p.PersList }}
                    <tr name = "tr" id = "tr_{{ $p.NameProf }}">
                        <td> Numele: <input name="name" type="text" value="{{ .NameLucr }}" /></td>
                        ...
                    </tr>
                    {{ end }} 
                 </tbody>
            </table>
            <P><label  id="addLucrator">Add_Lucrator</label></P>
            {{ end }}
            <input type="submit" value="GO!">
        </form>
    Ответ написан
  • Как строить и деплоить Go-приложения?

    Tyranron
    @Tyranron
    1. Нормально будет как внутри единого процесса крутить (с помощью какого-то robfig/cron, к примеру), так и отдельными процессами (cron-задачами, или даже демонами). Здесь всё зависит от конкретной ситуации и того что Вам нужно. У каждого подхода свои очевидные трейд-офф'ы. Просто выбираете то, что Вам больше подходит. На кодовую базу это мало влияет, ибо всегда можно переиспользовать пакеты, да и вообще можно держать всё в едином бинарнике, просто прокинув разные CLI-интерфейсы.

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

    3. Если речь идёт строго об одном процессе, который нельзя размножить, то смотрите в сторону graceful updates/restarts. Если же Вы можете множить приложение (в конце-концов живём в век победившых Docker/Kubernetes), то есть более общие техники, подходящие абсолютно для любого приложения: blue/green deployment либо rolling update (в том же Kubernetes из-коробки).
    Ответ написан
    3 комментария
  • Есть ли разница между горутинами и await в C#?

    Tyranron
    @Tyranron
    Несмотря на то, что эти инструменты созданы для решения одних и тех же проблем, делают они это по-разному, как под капотом, так и в плане предлагаемых абстракций. Потому нельзя сказать что async/await в C# - это "просто обертка над горутинами".

    Горутины в Go - это концепция stackful coroutines (под капотом) + CSP (в абстракциях). Каждый раз, когда мы создаём горутину, под неё выделяется отдельный стек вызовов для её собственных нужд. При этом, когда происходит паника, то stack unwinding (размотка стека вызовов) происходит только в пределах этой горутины и не покидает границ её стека. Стек горутины полностью отвязан от стека её создания/вызова, потому горутина не может возвращать результат. Любое общение между горутинами выполняется либо посредством каналов, либо какой-то общей памяти.

    async/await в C# (то есть, Task'и) - это концепция stackless coroutines (под капотом) + futures (в абстракциях). Код с async/await'ами компилятор превращает в определенную стэйт-машину с yield point'ами. У них нет отдельного стека, они выполняются в том же стеке что и вызывающий их код. Соотвественно, есть возможность словить exception'ы (аналог panic'и) возникающие внутри асинхронного Task'а прямо в запускающем его коде. Так как выполнение идёт на том же стеке - Task нормально может возвращать результат и мы его можем считать в вызывающем коде без дополнительных примитивов/инструментов.

    При этом, если мы запустим Go с GOMAXPROCS=1, то мы получим однопоточный асинхронный код в Go (по умолчанию он многопоточный). Также и в C# мы можем выполнять Task'и как на одном потоке, так и на thread pool, получая аналогичные Go гарантии рантайма.

    С точки зрения абстракций/использования - это уже вкусовщина. Кому как больше нравится. У futures лучше дизайн в плане composability (их эргономичнее join'ить и select'ить), но они вынуждают писать везде async и await. У горутин надо постоянно городить чехарду с синхронизацией (попробуйте сделать аналог await для произвольной горутины), но если эту чехарду прятать под капот (как обычно и делают), то код вообще выглядит полностью синхронным и программисты радуются.
    Ответ написан
    4 комментария
  • Стоит ли избегать знака присваивния := в Go при нагруженном бекенде?

    Tyranron
    @Tyranron
    Это неважно.
    Не следует "экономить на спичках" и соблюдать пустые ритуалы. Вы пишите приложение нормально, чтобы его понимали и нормально читали другие разработчики. Когда же дело доходит до оптимизации, Вы профилируете и смотрите где конкретно у Вас проблемы и затыки. Их и убираете.

    Что же касается конкретно аллокаций, то всё не совсем так (вернее, совсем не так).
    Аллокация - это выделение куска памяти в куче (heap), который потом должен быть убран сборщиком мусора. Именно потому это и считается ударом по производительности, так как добавляет работы как аллокатору, так и сборщику мусора.
    Но использование := само по себе не включает/выключает использование аллокации. Оно лишь декларирует новую переменную.
    Будет ли произведена аллокация зависит от другого. В первую очередь, от того какие Вы данные создаёте. Если это примитивный тип фиксированного размера, размещаемый на стэке (например int), то работы с кучей у Вас не будет вовсе, соответственно и аллокаций. Во вторую очередь, от такой магической штуки как escape analysis, которая может запросто разместить данные из кучи на стэке, если ей так того захочется, тогда аллокации у Вас происходить тоже не будет.

    Давайте посмотрим примеры:
    1. var i int
      i = 1
      i = 2

      0 аллокаций, так как i размещается строго на стэке.

    2. i := 1
      i := 2

      0 аллокаций, так как i размещается строго на стэке.

    3. i := new(Struct{1,2,3})
      i := new(Struct{3,2,1})

      2 аллокации, так как new разместит данные в куче и вернёт указатель на них. Но только если escape analysis не переместит это всё в стэк (что конкретно в данном случае достаточно вероятно).

    4. var i *Struct
      i = new(Struct{1,2,3})
      i = new(Struct{3,2,1})

      2 аллокации, так как new все ещё разместит данные в куче и вернёт указатель на них.



    Как Вы видите, := сам по себе никак к аллокациям не относится, и является лишь синтаксическим сахаром.

    P.S. Давеча на Хабре была отличнейшая статья про стандартную библиотеку Go, показывающая что занятие преждевременными микрооптимизациями без профилирования - вполне себе бессмысленная затея.
    Ответ написан
    Комментировать
  • Как протестировать вызовы методов у websocket.Conn из gorilla/websocket?

    Tyranron
    @Tyranron
    Чтобы мокать в модульных тестах, есть вот такое:
    https://github.com/posener/wstest

    Также есть вариант делать интеграционки/E2E-тесты через
    https://github.com/gavv/httpexpect
    Запилено тут, юзать через форк (ибо у автора 2 месяц руки не доходят поревьюить).
    Там можно тесты натравливать как напрямую на HTTP-сервер с WS, так и подменить клиент чтобы он сразу к хэндлерам присасывался, не подымая полноценный HTTP-сервер (как раз идею и слизал с posener/wstest).

    Оба решения используются мной на реальном проекте без проблем и вполне удачно.
    Ответ написан
    2 комментария
  • GOLANG приложение и свои сертификаты?

    Tyranron
    @Tyranron
    Если всё делать по стандартному фэн-шую, то никаких отличий от обыкновенных OpenSSL туториалов то и нет:

    0. Если нет уже готового CA (Certificate Authority), то генерируем новый: ecdsa.GenerateKey() + x509.CreateCertificate() (self-signed).

    1. Генерируем приватный ключ PK (Private Key) для своего сертификата: ecdsa.GenerateKey().

    2. Генерим запрос на создание сертификата CSR (Certificate Signing Request): x509.CreateCertificateRequest(). В качесте CN (Common Name) указываем тот адрес, по которому будем стучаться к приложению. Если таких адресов предполагается несколько, то используем SAN расширение в шаблоне сертификата.

    3. Берем CA и выпускаем себе сертификат по сгенерированному CSR: x509.CreateCertificate().

    4. Используем сертификат для TLS: http.ServeTLS().

    Приватный ключ и сертификат (и свой, и CA) сохраняем в любую желаемую директорию в файловой системе. Права к приватным ключам при этом выставляем 0600. Если это дело всё разовое, то можно и во временную директорию (os.TempDir()), чтобы не мусорить.

    Если это только чтобы поиграться, и не предполагается выстраивание своей PKI (Public Key Infrastructure), то можно и не париться с CA/CSR, а сразу выпускать self-signed сертификат с нужными Вам CN/SAN. То есть, остается только нулевой шаг.

    Если у нас приложение пырится во внешний мир не "голым", а прикрыто каким-то проксирующим сервером (например, Nginx), что, в принципе, есть даже рекомендуемой практикой, то сертификат можно подключать для нашего хоста прямо там, а приложение оставить себе без TLS. В этом случае Nginx будет расшифровывать трафик у себя и в приложение пробрасывать уже незашифрованный трафик. Подобное называется терминацией TLS.
    Если же мы хотим, чтобы между прокси-сервером и нашим приложением продолжал ходить зашифрованный трафик, то прокси-серверу нужно в настройках скормить наш CA сертификат, либо отключить проверку CA.
    Ответ написан
    2 комментария
  • Golang Redis получить значения?

    Tyranron
    @Tyranron
    Ну так Вы же их (данные) явно и удаляете. У Вас что в коде producer'а, что в коде consumer'а, в функции init() идет вызов client.FlushDB().

    Подозреваю, Вы добавили этот вызов в producer чтобы обеспечить "чистоту эксперимента". А когда переносили код в consumer просто забыли убрать. В результате, когда у Вас запускается consumer, он подключается, вычищает всю БД, а потом пытается уже что-то из неё вычитать, и, естественно, читать уже нечего.
    Ответ написан
    Комментировать
  • Как правильно настроить gcc для gobot?

    Tyranron
    @Tyranron
    Потому что Вы не подтянули зависимости (dependencies) для сорцов gobot. Их можно установить через go get. Смотрим официальную доку.
    Ответ написан
  • Почему type []User нельзя использовать как type []UserInterface?

    Tyranron
    @Tyranron
    Это сделано намеренно, дабы избежать неявных дорогих преобразований.

    Первое, что нужно знать — это отличия «структуры» от «интерфейса» в Go. Это просто, и этого достаточно, чтобы понять, почему вы просто так не можете «скастить» слайс структур в слайс интерфейсов.
    ...
    Второе — и созвучное с выше обсужденными слайсами — это то, операция конвертирования слайсов — дорогая операция. По времени это O(n) и компилятор Go подобные дорогие вещи не будет делать, чтобы не давать нерадивым программистам писать медленный код. Хотите делать потенциально дорогую операцию — будьте добры, сделайте это явно, вся ответственность на вас, а не на компиляторе.

    Детальнее:
    Холиворная статья: причина №4
    Объяснение на SO
    Ответ написан
    Комментировать
  • Перспективы у языка GO?

    Tyranron
    @Tyranron
    По поводу сложности языка.
    Просто оставлю это здесь.
    Ответ написан
    2 комментария
  • В чем проблема отправки сигнала дочернему процессу?

    Tyranron
    @Tyranron
    Извините за прямоту, но проблема в недостаточном знании Вами матчасти.
    Что почитать по теме: Zombie process, а также Docker and the PID 1 zombie reaping problem.

    А теперь ближе к Вашему примеру.
    Cуть: и SITERM'ом, и SIGKILL'ом Вы child процесс успешно убиваете, после чего он превращается в зомби-процесс, то есть все еще висит в памяти под своим PID ожидая reaping (системного вызова wait() от parent процесса). После завершения parent процесса наш зомби-child усыновляется init процессом, который его и reap'ит.
    Если запустить Ваши программы в том виде, котором Вы их запостили, и во время тиканья написать ps, то увидим следующую картину:
    459 ttys000    0:00.03 -bash
    498 ttys000    0:00.00 ./parent
    499 ttys000    0:00.00 (child)
    То, что child указан в скобках, как раз и означает, что процесс был остановлен (то есть он совершил системный вызов exit()) и ожидает reaping'а (когда результат exit()'а прочтут wait()'ом).
    Если перед посылкой child'у добавитьtime.Sleep(10 * time.Second) и сразу после вывода child'овского PID'а набрать глянуть что творится в процессах, то увидим такую картину:
    459 ttys000    0:00.04 -bash
    510 ttys000    0:00.00 ./parent
    511 ttys000    0:00.00 ./child
    Сравните с тем как он отображается в процессах после того, как был убит.
    А если мы после посылки сигнала child'у добавимcmd.Process.Wait(), то Ваш цикл ticker'а все равно, возможно, еще будет крутится, если в системе появился новый процесс с таким PID'ом, но если при этом заглянуть в ps, то увидим, что нашего child'а уже нет совсем:
    459 ttys000    0:00.05 -bash
    536 ttys000    0:00.01 ./parent

    Кстати, о волшебной силе Process.Wait() пишется прямо в его доке.
    Ответ написан
    1 комментарий
  • Как бы вы сделали проверку на запуск второго экземпляра программы?

    Tyranron
    @Tyranron
    Мне не нравится 3й вариант, так как он не решает задачу целиком. Порт, который слушать - это больше конфигурационный параметр, значит надо задумываться уже, ничего ли страшного не случится при смене порта и рестарте (обычно не должно, но раз на раз не приходится). А что, если демону и вовсе не нужно слушать какой-либо порт? Не универсальненько.

    Увы, кросс-платформенного решения "из коробки" нет, да. Я когда стряпал свою поделку, то меня интересовали только *nix-like платформы, мне хватило старого хорошего PID-файла с syscall.Flock. То, что видел в других решениях, более кросс-платформенных, - люди заморачивались на platform specific код, для Windows они использовали регистрацию процесса в виде сервиса. Обернуть это дело в отдельный пакет с единым интерфейсом и platform specific компиляцией совсем не сложно в случае с Go. Для работы с сервисами Windows есть замечательный, пусть и не входящий в стандартную либу, но все же официальный пакет golang.org/x/sys/windows/svc, и костылить на чистых syscall'ах даже не нужно.
    Также загляните в этот тред, там как раз про решение для Windows в виде semaphore/mutex аналогично тому, что указал Владимир Мартьянов в комментарии к Вашему вопросу и тому, что Вы указали 2м пунктом.
    Ответ написан
    Комментировать
  • Как сконвертить [4]uint8{0,0,0,0} в []uint8{}?

    Tyranron
    @Tyranron
    Касаемо вопроса:
    Ну Вы бы доки хотя бы поглядели, ситуёвина то детская. Здесь конкретно по Вашему вопросу.
    x := [3]string{"Лайка", "Белка", "Стрелка"}
    s := x[:] // a slice referencing the storage of x


    Касаемо чесаной репки:
    ИМХО, не стоит делать versus. И на том, и на другом языке эта задача замечательно решается.
    Go очень хорош и удобен для написания демонов. Наверное, можно сказать, что фактически это его основная ниша сейчас.
    Rust более низкоуровневый, но, и в то же время, гораздо интереснее и выразительнее Go.
    Если проект домашний и есть интерес поколошматить мозги об Rust, то я бы выбрал его. Если надо быстро, легко, удобно и понятно (не только Вам), то, конечно же Go и не заморачивался бы.
    Ответ написан
  • Выбор функционального языка программирования?

    Tyranron
    @Tyranron
    Если под "функциональным" подразумевается функциональная парадигма, то Go тут явно аутсайдер. Советую Haskell для ознакомления с парадигмой фактически в "чистом" виде. После него - Scala и/или Rust, как удачные смешения функциональной парадигмы с другими парадигмами/направлениями. И не забудьте повертеть Erlang.

    Если же под "функциональным" подразумевается удобный инструмент с многими возможностями из коробки, то тут однозначно Go, так как и порог вхождения мал, и прививает хорошие практики. После него Scala + FRP + TypesafeStack тоже должны показаться интересными, но там порог вхождения повыше будет.
    Ответ написан
    Комментировать
  • Как грамотно реализовывать каналы в Go?

    Tyranron
    @Tyranron
    Как вариант: вот.
    Постарался реализовать все условия задания. К сожалению, в задании ни слова о размере очереди. Я сделал её 2*N*P.
    Ответ написан
    Комментировать
  • Особая магия с channels в golang?

    Tyranron
    @Tyranron
    Сначала отвечу на второй вопрос.
    close() никакого отношения к scope не имеет вообще, он просто делает канал "закрытым", то есть таким, что при чтении значений из канала и записи из него возникает panic. Это всё. Соответственно...
    что случится, если закрыть буферизированый канал до того, как считать из него все значения, успевшие туда попасть?
    Канал закроется, при следующей попытке считать с него значение получите panic. Значения, что остались внутри, считайте потерянными, Вы их больше никак не получите.
    UPD: Это не так! (см. конец поста и комментарии)
    Соберется ли такой chan сборщиком мусора при уходе его в out of scope? Что будет с обьектами внутри канала?
    Соберется он сборщиком мусора только тогда, когда на него никто больше не будет ссылаться (если он объявлен локально, то да, out of scope). Объекты внутри тоже соберутся, если на них больше никто не ссылается. Те, на которые еще ссылается кто-то, останутся.

    Теперь замечания по Вашему примеру:
    select {
    case b <- number:
        fmt.Printf("Sent into b: %d\n", number)
    default:
        fmt.Printf("b chan closed\n")
    }
    Этот кусок здорово дезинформирует. Во-первых select на запись c default секцией никоим образом не спасает от panic при записи в закрытый канал. Он всего лишь делает запись в канал всегда неблокируемой. Как только Вы таким select'ом попытаетесь записать в закрытый канал что-то, словите сразу панику. Потому правильно для восприятия это место выглядит следующим образом:
    select {
    case b <- number:
        fmt.Printf("Sent into b: %d\n", number)
    default:
        fmt.Printf("Number %d just has been thrown away\n", number)
    }
    Если Вы сделаете канал a буферизированным, то тут Вам Ваши panic'и и полетят, потому что Вы пишете в закрытый канал.
    Закономерный вопрос: почему тогда panic'и не летят при небуферизированном канале а?
    Ответ: Вам просто везет =)
    Во-первых, на playground'е runtime.GOMAXPROC=1 и runtime.NumCPU=1. То есть в реальности все дело крутится в одном потоке и параллельность тут псевдо.
    Во-вторых, у меня локально (OS X) этот скрипт выкидывает panic'у после получения числа 25 даже при runtime.GOMAXPROC=1. Вам банально повезло, что внутренний планировщик горутин на playground'е ведет себя именно таки образом. При буферизированном канале он ведет себя немного иначе и Вы получаете закономерную panic'у сразу.

    Если совсем на пальцах по первому вопросу, то:
    При небуферизированном канале close(b) по каким-то соображения планировщика выполняется только после того как отработали обе горутины, если глянуть на вывод, то надпись "B:1" будет в самом конце. Потому то все и отрабатывает нормально, хотя это поведение совершенно не гарантированно логикой программы, наоборот, программа рассчитана на то, что close(b) выполнится сразу после того, как мы оттуда что-то считаем. Это и происходит, если канал a сделать буферизированым (надпись "B: 1" вылетает сразу), так как в этом случае планировщик меняет свои соображения, и мы получаем закономерную panic'у.

    Дополнительно:
    Канал b должен быть буферизированным, иначе, если горутина судья отработает быстрее чем главная горутина вообще начнет слушать канал, то все значения просто выкинутся. Эта проблема хорошо описана в этой статье на двух предпоследних абзацах.

    UPD:
    Я допустил ошибку, как верно указали в комментариях Виталий Яковенко и SilentFl, закрытый канал не выдает panic при чтении с него. Он разрешает считать все значения, которые в нем остались, после чего отдает "пустые" значения для свое типа, больше никогда не блокируя выполнение.
    Закрытый канал panic'ует только при попытке отправить в него значение.
    Ответ написан
  • Как в golang получить информацию о процессе?

    Tyranron
    @Tyranron
    Стандартной библиотекой Go не получится, потому что это дело совсем не платформо-независимое.
    Обычно предлагают или парсить вывод top/ps, или стучаться в /proc/stat .
    Детали с примерами на SO.

    UPD: Но если нужно мониторить потребление памяти программы написаннной на Go, а не вообще любого процесса, то для уменьшения накладных расходов и более точной информации, лучше организовать это дело внутренними средствами Go через доп. интерфейс или логирование.
    Пример: How to get statistics about memory in Golang
    Ответ написан
  • Как в Go получить абсолютный путь запущенной программы?

    Tyranron
    @Tyranron
    Все правильно, go run - это же не Ваш бинарник, это утилита, которая сначал компилит Ваш код в бинарник и помещает во временную директорию под уникальным номером, потом запускает. Gin, вероятно, действует иначе, компилит в ту же директорию где лежит скрипт и запускает.
    Чтобы не хардкодить - прекратите запускать сервера с помощью go run. Команда
    go build -o myapp && ./myapp
    не сильно сложнее, но зато будете уверены, что бинарник запускается там, где нужен.
    Обычно go run принято использовать для небольших скриптов, которые выполняют разовую работу, то есть таких, которые компилить нет особо смысла, хочется сразу прогнать.

    P.S. Пользуясь случаем, скромно попиарюсь и выложу ссылку на свою поделку для простой демонизации простых приложений на Go, когда юзать какой-то продвинутый гипервизор процессов влом или неоправданно. Но он пока только для linux/osx.
    Загляните в код, он небольшой и там это все есть.
    Ответ написан
  • Стоит ли использовать FrameWork'и для Go?

    Tyranron
    @Tyranron
    Ну, а Вы попробуйте, или просто на слово поверите? =)
    Придумайте типичное тестовое задание небольшое аля наваять/сделать форму регистрации/входа на сайт с механимом сессий (считай залогинен/незалогинен). Решите эту задачу сначала с помощью фреймворков, а потом только с помощью стандартной библиотеки (или наоборот, не суть).

    Вообще, все зависит от задачи, которую предстоит решить. По моему опыту (небольшие проекты) - на Go легко, удобно и приятно делать минифреймворки/тулкиты под конкретную задачу, если Вас не устраивают существующие решения. Но для чего-то большого, чувствую, что на определенном этапе голый net/http без какой-либо продуманной архитектуры приложения начнет приносить боль.
    Ответ написан
    5 комментариев