Golang: Мои Открытия

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

В Go директива //go:embed используется для встраивания файлов и папок в бинарный файл Go на этапе компиляции. Эта функция улучшает безопасность, производительность и простоту кода за счёт возможности прямого импорта файлов без использования функций операционной системы.

Пример:

//go:embed all:frontend/dist 
var assets embed.FS
  • //go:embed all:frontend/dist Эта директива указывает компилятору Go встроить содержимое каталога frontend/dist в бинарный файл. Префикс all: используется для включения не только файлов из каталога, но и всех вложенных каталогов и их файлов рекурсивно.
  • var assets embed.FS Это объявляет переменную с именем assets типа embed.FS, который является интерфейсом файловой системы, предоставляемым пакетом embed. Эта переменная будет предоставлять доступ к встроенным файлам во время выполнения.
  • Преимущество заключается в том, что импортирование определённых файлов напрямую и использование embed вместо функций операционной системы повышает безопасность, производительность и простоту использования.
  • Можно попробовать использовать это для создания настольного приложения и загрузить все данные, связанные с интерфейсом, в сервер Go.
  • Оператор select позволяет горутине ожидать выполнения нескольких операций взаимодействия.

Ну, это что-то, что вы могли бы увидеть на https://go.dev/tour/concurrency/5, но в чём здесь польза?

go func(){
   _, err := destConn.Write(requestBuffer1)
   if err != nil {
      errChan1 <- err
      close(requestBuffer1Chan)
      return 
   }
   successfullyWrittenBuffer1Chan <- true
}()
//write the reply to mongo client
select {
case <-ctx.Done():
      destConn.Close()
      return
case err = <-errChan1:
      logger.Error("failed to write the reply message to mongo client", zap.Error(err))
      return 
case <-successfullyWrittenBuffer1Chan:
}
  • Давайте рассмотрим пример, где мы записываем данные в конечное соединение. В случае закрытия приложения вы не хотите писать в закрытое конечное соединение.
  • Код не обязательно “останавливается” на ctx.Done(). Оператор select ожидает, когда один из случаев станет готовым. Если контекст отменен (или его срок действия истекает), до того как будет получена ошибка или до того как операция записи будет подтверждена как успешная, тогда случай ctx.Done() будет выполняться первым, что приведет к закрытию destConn и раннему завершению функции и её возврату.
  • Если контекст не завершён и произошла ошибка, то выполнится случай err = <-errChan1, который залогирует ошибку и вернёт её.
  • Если контекст не завершён и ошибка не произошла, а операция записи выполнилась успешно первой, то выполнится последний случай, позволяя функции продолжить выполнение за пределами оператора select.

В Go интерфейс представляет собой тип, который определяет набор сигнатур методов (названия методов, параметры и типы возвращаемых значений), но не реализует сами методы. Это способ определения поведения как набора действий. Тип реализует интерфейс, реализуя все методы, объявленные в интерфейсе.

Реализуйте следующий интерфейс:

package main

import (
    "fmt"
)

// Определение интерфейса
type Speaker interface {
    Speak() string
}

// Реализация интерфейса с помощью типа Dog
type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return fmt.Sprintf("%s говорит гав!", d.Name)
}

// Реализация интерфейса с помощью типа Cat
type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return fmt.Sprintf("%s говорит мяу!", c.Name)
}

func main() {
    var dog Speaker = Dog{Name: "Барсик"}
    var cat Speaker = Cat{Name: "Мурка"}

    fmt.Println(dog.Speak())
    fmt.Println(cat.Speak())
}
  • Приведенный выше фрагмент кода демонстрирует контракт для получения данных о студенте. Любой тип, который реализует этот интерфейс, может предоставлять данные о студенте, что позволяет легко заменять различные источники данных или реализации без изменения остального кода. Это может быть особенно полезно при работе с различными системами баз данных и внешними службами или при создании заглушек данных для тестирования.
  • Какова практическая польза от этого? Абстракция, полиморфизм, гибкость и еще много чего.

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

package config

import (
 "fmt"
 "os"
 "strconv"

 "github.com/joho/godotenv"
)

type Config struct {
 AppName              string
 SuperAdmin           string
}

var config Config

// Должен запускаться в самом начале перед любым другим пакетом или кодом.
func init() {
 appEnv := os.Getenv("APP_ENV")
 if len(appEnv) == 0 {
  appEnv = "dev"
 }

 configFilePath : "./config/.env"
 if os.Getenv("APP_ENV") == "test" {
  configFilePath = "../config/.env"
 }

 e := godotenv.Load(configFilePath)
 if e != nil {
  fmt.Println("error loading env: ", e)
  panic(e.Error())
 }
 config.AppName = os.Getenv("SERVICE_NAME")
 config.SuperAdmin = os.Getenv("SuperAdmin")
}

func Get() Config {
 return config
}

Какова выгода от этого? Вам не нужно инициализировать функцию init вручную; вам просто нужно импортировать функцию, и значения будут извлечены и назначены.

appname := config.Get().AppName
  • Отложенные вызовы работают как стек, что означает “первым вошел, последним вышел” (LIFO). Так что, если кто-то напишет что-то вроде этого:
defer closeCon()
defer cleancon()
defer printCon()
  • Таким образом, когда эта функция завершится, сначала будет выполнена функция printCon, затем cleanCon, и, наконец, closeCon.

В приведенном ниже фрагменте кода на Go мы имеем мощный шаблон обработки событий, который облегчает создание отзывчивых приложений.

runtime.EventsOn(ctx, "event:check:health", func(args ...interface{}) {
 responseEventName := args[0].(string)
 runtime.EventsEmit(ctx, responseEventName, map[string]interface{}{
  "success": true,
  "version": config.GetConfig().Version,
 })
})

  • Регистрация события: runtime.EventsOn – это вызов функции, который регистрирует слушатель событий для события с именем “event:check:health”. Когда это событие срабатывает, вызывается функция (замыкание), предоставленная вторым аргументом.
  • Излучение события: затем используется runtime.EventsEmit для отправки события с именем responseEventName. Вместе с именем в качестве данных события передается карта, содержащая статус успешности и текущую версию приложения (полученную через config.GetConfig().Version).
  • В заключение, данный фрагмент кода устанавливает слушатель событий, который при срабатывании события “event:check:health” отвечает другим событием, указывающим на успешность и версию приложения. Это элегантный пример того, как Go может кратко обрабатывать сложные событийно-ориентированные рабочие процессы.

Взято из репозитория Slashbase.

  • В Go паника (panic) – это встроенная функция, которая прекращает обычный поток управления и начинает паниковать. Когда вызывается функция panic, выполнение текущей функции немедленно останавливается, и управление начинает разворачивать стек, выполняя при этом все отложенные функции.
  • Если паника достигает вершины стека вызовов горутины без восстановления, программа завершается с ненулевым кодом статуса и, как правило, выводит сообщение о панике и стек вызовов в стандартный поток ошибок.
  • Для обработки паники в Go предоставляется функция recover, которая может остановить последовательность паники и вернуть значение, переданное функции panic. Она полезна только внутри операторов defer, потому что после начала паники это единственный способ вернуть контроль над программой.
func mayPanic() {
    panic("a problem")
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Восстановлено после паники:", r)
        }
    }()
    mayPanic()
    fmt.Println("После вызова mayPanic()")
}
+1
2
+1
3
+1
0
+1
0
+1
0

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *