Библиотека Go-разработчика | Golang
23.3K subscribers
2.07K photos
40 videos
87 files
4.43K links
Все самое полезное для Go-разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/32d20779

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4a8c2468
加入频道
🤔 В чем сила пакета singleflight?

🔸singleflight предоставляет механизм подавления дублирующихся вызовов функций. Например, наше приложение запрашивает данные из API или базы данных.

🔸Если несколько запросов инициируют один и тот же вызов, это может привести к множественным идентичным вызовам к БД или API. Это создает нагрузку на систему, ведет к потерям CPU, памяти и пропускной способности сети.

🔸Matthew Boyle, автор Domain-Driven Design with Golang, приводит пример использования singleflight для устранения дублирующихся вызовов. В примере, несмотря на то, что 5 горутин одновременно запрашивают данные для одного и того же ключа, функция fetchData будет вызвана только один раз благодаря функции group.Do из пакета singleflight.

💡singleflight также может используется в serverless кейсах. Google App Engine, например, применяет его как часть функции инициализации, поскольку там нет main.go.

#tip
👍20🤔9🥱31🔥1
💡 JSON Lines (JSONL) — формат для сохранения табличных данных в Go, в котором каждая строка является допустимым JSON-объектом.

Это позволяет обрабатывать данные построчно, упрощает использование CLI-инструментов (grep, awk, wc) и уменьшает неоднозначность по сравнению с CSV. Каждая строка JSONL может содержать более сложные данные, чем CSV-строка.

#tip
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15😁142
🤔 Могут ли адреса двух переменных быть одновременно равными и разными?

Код ниже определяет глобальную переменную a и локальную переменную b внутри main(). Затем он выводит адреса обеих переменных и, наконец, сравнивает их.

📌 Сравнение должно оцениваться как true, верно?

var a struct{}

func main() {
var b struct{}

fmt.Printf("&a: %p\n", &a)
fmt.Printf("&b: %p\n", &b)

fmt.Println("&a == &b:", &a == &b)
}


Пример выводит:

&a: 0x58e360
&b: 0x58e360
&a == &b: false


a и b — это пустые структуры, которые имеют нулевой размер, следовательно, они не должны занимать ячейки памяти. Они вообще не должны иметь адрес. Так что сравнивать их адреса с самого начала не имеет смысла.

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

📌 Спецификация Go говорит:

Две различные переменные нулевого размера могут иметь одинаковый адрес в памяти


#tip
👍76🤔1132👾2
Value receivers и nil

Представьте себе структуру с двумя методами: один использует pointer receiver, а другой — value receiver.


package main

type S struct {
N int
}

func (s *S) PointerRcv() {
}

func (s S) ValueRcv() {
}


Что происходит, если receiver равен nil?


func main() {
var s *S // s равно nil
s.PointerRcv()
s.ValueRcv()
}


Переменная s принимает нулевое значение типа *S, которое является nil. Поскольку ни один из методов не обращается к receiver'у, оба вызова метода должны пройти без проблем.

Однако, если мы выполняем этот код, вызов s.ValueRcv() вызовет панику!

📌 Что происходит?

Рассмотрим, что методы — это просто функции с некоторым синтаксическим сахаром. Метод func (s S) f() семантически идентичен функции func f(s S). Method receiver становится первым аргументом функции.

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


package main

type S struct {
N int
}

func PointerFunc(s *S) {
}

func ValueFunc(s S) {
}

func main() {
var s *S

PointerFunc(s)
ValueFunc(*s)
}


Теперь должно быть легко понять, почему ValueFunc() вызывает панику. Указатель s должен быть разыменован при передаче его в ValueFunc(). Разыменование nil указателя невозможно и приводит к панике.

Для метода func (s *S) PointerRcv(), receiver (или параметр функции во втором примере) не нуждается в разыменовании. Следовательно, паники не будет.

Так что, если у вас есть тип с pointer/value receivers, будьте осторожны, чтобы не вызывать какие-либо методы для nil значения этого типа.

#tip
👍49🥱61
💡 io.TeeReader можно использовать для копирования содержимого одного ридера в новый. Этот метод полезен, когда тело HTTP-запроса нужно прочитать дважды.

👉 Юзкейс: логирование входящих запросов с помощью middleware перед обработкой запросов.

#tip by Golangbot
👍45
👩‍💻 Функция Join пакета errors конкатенирует список ошибок и возвращает ошибку, если хотя бы одна из переданных ошибок не nil.

Join возвращает nil, если все переданные ошибки равны nil.

#tip by Golangbot
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32🔥5🌚2
💡 Если у вас запущено множество локальных серверов, и вам надоело обращаться к ним как localhost:8081, localhost:9000 и т. д., посмотрите в сторону Caddy. Он сделает настройку «доменов» для локальных серверов проще простого.

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


caddy reverse-proxy --from myserver.localhost --to :9000


и откройте https://myserver.localhost. Вы увидите, что сервер на localhost:9000 отвечает. Caddy даже предоставляет локальные TLS-сертификаты.

А если вы хотите проксировать больше серверов таким образом, создайте файл с именем Caddyfile и введите конфигурацию хоста следующим образом:


myapp.localhost {
reverse-proxy :9000
}

myhugoblog.localhost {
reverse-proxy :1313
}


#tip
31👏11👍8🔥4
Вы наверняка знакомы со «стандартным» способом ветвления кода в зависимости от заданного значения:


switch a {
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
default:
fmt.Println("default")
}


Так работает switch в Go и во многих других языках (за исключением того, что в Go не происходит перехода к последующим case).

Но оператор switch может делать больше. Вот несколько кейсов.

1. Несколько значений в одном case.

В case можно указать несколько значений для сопоставления:


switch a {
case 1:
fmt.Println("1")
case 2, 3, 4:
fmt.Println("2, 3 или 4")
// case 1,2: // ошибка: дублирование case 1, дублирование case 2
// fmt.Println("1 или 2")
}


Заданное значение может использоваться только в одном блоке case. Дублирование значений в case вызовет ошибку.

2. Инициализатор, как в цикле for.

Вы можете инициализировать значение перед использованием его в switch. Область видимости переменной a ограничена конструкцией switch:


switch a := f(); a {
case 1:
fmt.Println("1")
case 2:
fmt.Println("2")
}


3. Нет выражения switch, но есть выражения case.

case не ограничен статическими значениями. Если вы опустите выражение switch, вы можете использовать выражения для каждого case:


switch {
case a == 1:
fmt.Println("1")
case a >=2 && a <= 4:
fmt.Println("2")
case a <= 5:
fmt.Println("3")
}


Если текущее значение a совпадает более чем с одним case, выбирается первый подходящий.

4. Переключение по типу переменной.

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


switch v := a.(type) {
case int:
fmt.Println("a — это int:", v)
case string, []byte:
fmt.Println("a — это string:", v)
}


5. Переключение по типу параметра.

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


func do[T comparable](a any) {
switch v := a.(type) {
case int:
fmt.Println("a — это int:", v)
case T:
fmt.Printf("a — это %T: %v", v, v)
case []T:
fmt.Println("a — это срез:", v)
case []byte:
fmt.Println("a — это срез байт:", v)
}
}

func main() {
do[bool](a)
do[bool](true)
do[int]([]int{1, 2, 3})
}


Как и с выражениями case, если фактический тип a совпадает с несколькими case, выбирается первый подходящий.

👉 Go Playground

#tip
👍607🥱5🔥3😍1
Флаг -cpu можно использовать при запуске тестов Go, чтобы указать список значений GOMAXPROCS, с использованием которых необходимо запустить тесты. Например,
go test -cpu=4,5 


запустит тесты 2 раза. Тесты сначала будут запущены с четырьмя процессорами, а затем второй раз — с пятью.

#tip
🔥39👍104
👩‍💻 Пакет unique в Go 1.23 — отличный способ оптимизировать ваши ресурсы

#tip by Phuong Le
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🤔3
Кстати, функция LookupEnv в Go может использоваться для определения того, установлена ​​ли переменная окружения или нет.

👉 Go Playground

#tip
🥱25👍22🌚3👏1
🚀 Оптимизация и стресс-тесты в Go с флагом -cpu

Когда речь идет о тестировании производительности и устойчивости приложений, работающих в многопоточной среде, Go предоставляет отличный инструмент — флаг -cpu. Этот параметр позволяет запускать тесты с разным количеством логических процессоров (GOMAXPROCS), моделируя реальное поведение приложения в условиях разного уровня параллелизма.

📌 Как работает флаг -cpu?
Флаг -cpu указывается при запуске тестов и задаёт список значений, с которыми тесты должны быть выполнены. Например:
go test -cpu=4,5

👉 Этот пример запустит тесты дважды:
1️⃣ С четырьмя логическими процессорами.
2️⃣ Затем с пятью.

🛠 Зачем это использовать?
Тестирование под разной нагрузкой: использование нескольких значений -cpu позволяет понять, как ваше приложение поведёт себя на системах с разным количеством ядер.
Поиск узких мест: помогает выявить проблемы в конкурентном доступе, такие как гонки данных или узкие места в производительности.
Реализм тестов: ваш код проверяется в условиях, максимально приближенных к реальной эксплуатации.

🔑 Ключевые моменты:
Можно указать несколько значений через запятую, например -cpu=1,2,4,8, чтобы протестировать приложение в разнообразных сценариях.
Если -cpu не задан, тесты запускаются с текущим значением GOMAXPROCS.
Практическая выгода: Регулярное использование -cpu в тестах повышает устойчивость вашего кода и предотвращает неожиданные проблемы при высоких нагрузках.

💡 Пример для продвинутых:
Если вы хотите протестировать код на нескольких уровнях параллелизма, запустите:
go test -cpu=1,2,4,8 -v

📊 Результат: тесты покажут, как приложение справляется с 1, 2, 4 и 8 логическими процессорами. Это отличный способ убедиться, что ваш код работает эффективно и безопасно в конкурентной среде.

#tip
Please open Telegram to view this post
VIEW IN TELEGRAM
👍406😁2❤‍🔥1