Golang Portal
8.06K subscribers
417 photos
26 videos
7 files
448 links
Присоединяйтесь к нашему каналу и погрузитесь в мир для Golang-разработчика

Связь: @devmangx
Download Telegram
Управляем множеством горутин с errgroup

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

Ты, наверное, знаком с sync.WaitGroup, да? Но есть пакет errgroup, который упрощает эту задачу.

В примере мы загружаем две страницы и ждем их завершения с g.Wait().

errgroup помогает управлять несколькими горутинами и обрабатывать ошибки, которые они могут вернуть.

Три ключевых момента:

🔹Для параллельного выполнения задач используй g.Go(), передавая в него функцию.
🔹g.Wait() ждет завершения всех горутин и возвращает первую возникшую ошибку (но не все сразу).
🔹errgroup хорошо работает с контекстом.

При использовании errgroup.WithContext() в случае ошибки контекст отменяется.

Как это работает внутри
1. Group использует несколько механизмов:
🔹sync.WaitGroup — чтобы дождаться завершения всех горутин.
🔹sync.Once — чтобы безопасно сохранить первую ошибку.
🔹Семафорный chan — для ограничения числа одновременно работающих горутин (можно задать лимит через errg.SetLimit()).

2. errg.done() — вспомогательная функция, которая помечает горутину завершенной.
🔹Уменьшает счетчик активных горутин (если установлен лимит).
🔹Сообщает WaitGroup, что горутина завершилась.

3. Ошибки обрабатываются централизованно.
🔹С помощью errOnce фиксируется только первая ошибка, которая затем приводит к остановке и возвращается через Wait().

И да, горутины — это не всегда лучшее решение. Если задачи быстрые, последовательное выполнение может быть эффективнее

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍111
Опенсорсное, самохостовое решение для ведения заметок, разработанное для удобного развертывания и кроссплатформенного доступа.

GitHub: https://github.com/usememos/memos

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍43
Гайд для новичков по запуску приложения на Go + React в Docker

Он охватывает настройку окружения, создание простых примеров Go API и приложения React, а также настройку Nginx.

🔜 Читать

👉 @juniorGolang | #cтатья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍116
В Go 1.24 появился новый struct тег omitzero в encoding/json, который позволяет автоматически пропускать поля с нулевыми значениями.

Интересно то, что для него можно определить собственный метод IsZero().

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

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27🔥4
Полезный репозиторий с пет-проектами на Go

Если попробовать реализовать хотя бы часть из этого, то Go станет понятнее, да и между непустым профилем в GitHub и полученным оффером есть положительная корреляция

Ссылка: тык

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥1
Трюк для сокрытия методов и предотвращения утверждений интерфейсов в Go.

Например, есть тип A с методом Hello(), который удовлетворяет интерфейсу Greeter.
Если передать экземпляр A в Greet(), интерфейсное утверждение успешно пройдет, и будет вызван Hello().

Но если нужно использовать A, не открывая его метод Hello(), можно скрыть его через встраивание.
При вызове Greet(ANoHello{A: A{}}) интерфейсное утверждение провалится и выведет: "g does not implement Greeter".

Хотя ANoHello встраивает A, содержащий метод Hello(), неэкспортируемое поле noHello имеет приоритет и скрывает его.

Полезно в редких случаях (например, os.File), но стоит знать этот трюк для лучшего понимания встраивания.

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
💡 Шпаргалка по REST API для начинающих

Шесть фундаментальных принципов, которые служат строительными блоками архитектуры REST API:

Клиент-серверная архитектура
Взаимодействие без сохранения состояния
Возможность кэширования
Многоуровневая система
Поддержка кода по требованию
Унифицированный интерфейс

🔜 Читайте подробнее на Medium

👉 @juniorGolang
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83
This media is not supported in your browser
VIEW IN TELEGRAM
Компилятор shell-скриптов, который превращает их в безопасные, переносимые и статические бинарные файлы.

В отличие от других инструментов, Bunster не просто упаковывает ваш скрипт в бинарник — он компилирует его в автономные программы, не зависящие от оболочки.

👉 https://github.com/yassinebenaid/bunster

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52🌚1
Буферизированные каналы как семафоры для ограничения выполнения горутин

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

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

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

В этом примере:

🔹wg.Add(10) — мы подготавливаемся к завершению 10 горутин.
🔹make(chan struct{}, 3) — это создает семафор, позволяющий одновременно работать только 3 горутинам.

Если нужен более аккуратный способ управления этим процессом, можно рассмотреть создание типа Semaphore, который будет обрабатывать все действия, связанные с семафором.

Использование такого пользовательского типа Semaphore упрощает управление доступом к ресурсам в наших функциях.

Также существует реализация семафора в пакете golang.org/x/sync/semaphore, которая представляет собой взвешенный семафор.

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

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

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍111
⚙️ Веб-приложение для управляемой передачи файлов, созданное на Go, с использованием rclone для надежной передачи данных

https://github.com/StarFleetCPTN/GoMFT

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍81
Настройка GOMAXPROCS для контейнерных сред (Kubernetes, Docker и др.)

⚠️ (Внимание: длинный и скучный материал)

Что такое GOMAXPROCS?

По умолчанию Go может запускать до 10 000 потоков одновременно, но фактическое количество потоков, выполняющихся параллельно, определяется важным параметром — GOMAXPROCS.

GOMAXPROCS устанавливает ограничение на количество потоков ОС, которые могут одновременно исполнять пользовательский Go-код (не просто конкуррентно, а именно параллельно).

Значение по умолчанию соответствует количеству логических ядер CPU, которое видит ОС (runtime.NumCPU()).

Например, на моем MacOS это 8 ядер, а значит, Go будет одновременно выполнять до 8 потоков.

Запуск Go в контейнерах (Docker и Kubernetes)

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

Пример:
🔹limit: 250m = 1/4 ядра
🔹limit: 1 = 1 полное ядро

Но проблема в том, что Go не видит эти ограничения. Он по-прежнему определяет количество доступных CPU на уровне хост-машины, а не контейнера.

(Хост-машина или нода может содержать несколько подов.)

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

"Но почему? Ведь чем больше CPU, тем лучше!"

Вот несколько причин, почему это может быть проблемой:

Контекстные переключения – если потоков больше, чем ядер, ОС вынуждена чаще переключаться между ними.
Неэффективное планирование – планировщик Go создаёт больше готовых к выполнению горутин, чем CPU может реально обработать, что приводит к избыточной конкуренции за процессорное время.
Неэффективное выполнение CPU-bound задач – Go-программы часто CPU-bound, а значит, лучше работают, когда каждое ядро загружено ровно одним потоком без необходимости ожидания.

Если GOMAXPROCS больше, чем реально выделенные CPU-ресурсы, Go-рантайм планирует больше потоков, чем есть доступных ядер, что приводит к неэффективному выполнению.

Решение

Если хочется автоматизировать настройку, можно использовать uber-go/automaxprocs. Эта библиотека корректирует GOMAXPROCS в соответствии с ограничением CPU контейнера.

(Если интересно, как работает uber-go/automaxprocs под капотом — просто скажите.)

Если вы понимаете спецификацию деплоя/пода, можно вручную задать переменную окружения GOMAXPROCS в соответствии с лимитом.

Но с точки зрения DevOps лучше вовсе избегать CPU-лимитов, а вместо них устанавливать только CPU requests (об этом подробнее ниже). Это касается не только Go-сервисов.

Задумайтесь об этом, если в вашем контейнере не задан лимит CPU.

Полезные ссылки:
Почему не стоит использовать CPU-лимиты в Kubernetes
Kubernetes CPU Limits и Go

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112
⚙️ Go-проект, который предоставляет программное решение для управления, хранения и распространения конфиденциальных данных, включая секреты, сертификаты и ключи

https://github.com/openbao/openbao

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2
Оптимизируйте множественные вызовы с помощью singleflight

Допустим, у вас есть функция, которая получает данные по сети или выполняет ввод-вывод, и её выполнение занимает около 3 секунд:

Эта функция выдаёт новое значение каждые 10 секунд.

🔹Если вызвать эту функцию 3 раза подряд, общее время ожидания составит примерно 9 секунд.
🔹Если использовать 3 горутины, общее время ожидания может сократиться до 3 секунд, но функция всё равно будет вызвана 3 раза для получения одного и того же результата (~99%).

Здесь на помощь приходит пакет singleflight, который доступен по ссылке:
👉 golang.org/x/sync/singleflight.

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

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

Как это работает?
1️⃣ Создаём объект singleflight.Group
2️⃣ Передаём в метод group.Do() функцию, которая выполняет дорогостоящие вычисления.

Метод group.Do() возвращает:
(result any, err error, shared bool)

Параметр shared показывает, был ли результат разделён между несколькими вызовами.

Зачем нужен аргумент key?
Ключ (key) — это идентификатор запроса.
Если поступает несколько запросов с одним и тем же ключом, singleflight понимает, что они запрашивают одно и то же.

Пример работы: Go Playground

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

👉 @juniorGolang | #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍102
⚙️ Лёгковесный микросервис для обработки криптоплатежей, написанный на Golang, предназначенный для создания и обработки счетов в криптовалюте через gRPC

https://github.com/goipay/goipay

👉 @juniorGolang | #ресурсы
Please open Telegram to view this post
VIEW IN TELEGRAM
👍62
Разбор DNS: от основ до создания DNS-сервера на Go

Статья объясняет основы DNS, процесс разрешения доменных имен и создание собственного DNS-сервера на Go.

Автор рассматривает работу DNS-запросов и делится проектом DNS-сервера с веб-интерфейсом на GitHub.

Полезно для изучающих сети и Go

👉 Читать

👉 @juniorGolang | #cтатья
Please open Telegram to view this post
VIEW IN TELEGRAM
👍63🤔1