Создание кастомного балансировщика нагрузки на Go для gRPC с приоритизацией адресов
Это решение позволяет гибко управлять распределением клиентских запросов между серверами с разными уровнями доступности и обеспечивает подключение к оптимальному ЦОД с минимальными задержками.
👉 Читать
👉 @juniorGolang | #cтатья
Это решение позволяет гибко управлять распределением клиентских запросов между серверами с разными уровнями доступности и обеспечивает подключение к оптимальному ЦОД с минимальными задержками.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Как использовать пакет 🕒
Пакет
1️⃣ Создание контекста с тайм-аутом
Контекст с тайм-аутом автоматически отменяет операцию, если она не завершилась за указанное время
Пример:
Что здесь происходит:
🔹 context.WithTimeout создаёт контекст, который завершится через 1 секунду.
🔹 Функция doWork проверяет канал ctx.Done() для отмены.
Результат: работа прерывается через 1 секунду.
2️⃣ Контекст с дедлайном
Дедлайн задаёт фиксированное время завершения.
Это полезно для заданий, где нужно строгое время завершения
3️⃣ Встраивание значений в контекст
Контекст может передавать данные между частями программы. Используйте
Когда использовать:
🔹 Передача метаданных, например, ID пользователя, токенов аутентификации.
🔹 Не используйте контекст для передачи больших объёмов данных.
4️⃣ Прерывание нескольких горутин
Контекст позволяет координировать отмену между несколькими горутинами:
Что происходит:
🔹 Отмена контекста (cancel) сигнализирует всем горутинам остановиться через ctx.Done().
5️⃣ Советы:
🔹 Используйте context только для управления временем выполнения и метаданными. Не передавайте через контекст сложные данные.
🔹 Добавляйте тайм-ауты ко всем сетевым операциям. Это помогает избежать зависания при проблемах с соединением.
🔹 Обязательно вызывайте cancel() для освобождения ресурсов
👉 @juniorGolang | #заметки
context в Go? Пакет
context помогает управлять временем выполнения и отменой операций, особенно в веб-приложениях и микросервисах. Это необходимо для предотвращения зависаний и утечек ресурсовКонтекст с тайм-аутом автоматически отменяет операцию, если она не завершилась за указанное время
Пример:
package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context) {
select {
case <-time.After(2 * time.Second):
fmt.Println("Work completed")
case <-ctx.Done():
fmt.Println("Work canceled:", ctx.Err())
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() // Освобождаем ресурсы
go doWork(ctx)
time.Sleep(2 * time.Second)
fmt.Println("Done")
}
Что здесь происходит:
Результат: работа прерывается через 1 секунду.
Дедлайн задаёт фиксированное время завершения.
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second))
defer cancel()
Это полезно для заданий, где нужно строгое время завершения
Контекст может передавать данные между частями программы. Используйте
context.WithValue:func doWork(ctx context.Context) {
userID := ctx.Value("userID")
fmt.Println("User ID:", userID)
}
func main() {
ctx := context.WithValue(context.Background(), "userID", 42)
doWork(ctx)
}Когда использовать:
Контекст позволяет координировать отмену между несколькими горутинами:
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d stopped\n", id)
return
default:
fmt.Printf("Worker %d working\n", id)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
for i := 1; i <= 3; i++ {
go worker(ctx, i)
}
time.Sleep(2 * time.Second)
cancel() // Отменяем все горутины
time.Sleep(1 * time.Second)
fmt.Println("All workers stopped")
}Что происходит:
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍6
Основные функции:
Установка:
go install github.com/essentialkaos/aligo/v2@latest
Пример использования:
sudo aligo --completion=bash 1> /etc/bash_completion.d/aligo
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥7❤2
Оптимизация использования памяти в Golang: когда переменная выделяется в куче
Основные моменты статьи:
➖ Анализ побега (Escape Analysis)
➖ Влияние на производительность
➖ Рекомендации по оптимизации
👉 Читать статью
👉 @juniorGolang | #cтатья
Основные моменты статьи:
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3
Нереально большое количество полезных ресурсов по Go
Здесь масса ссылок на разные туториалы, гайды, примеры практического использования Go
Уверен, каждый найдёт здесь ответы на многие свои вопросы
⛓ Ссылка: тык
👉 @juniorGolang | #ресурсы
Здесь масса ссылок на разные туториалы, гайды, примеры практического использования Go
Уверен, каждый найдёт здесь ответы на многие свои вопросы
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍2
Неожиданные побочные эффекты при использовании
Работа со срезами в Go кажется простой, но функции, такие как
Пример:
На первый взгляд может показаться, что вызов append внутри функции изменит исходный срез. Но это не так! Причина в том, что append может создать новый массив, если емкости исходного среза недостаточно, и вернуть ссылку на новый массив, оставляя оригинальный срез неизменным
➖ Как исправить:
Чтобы избежать путаницы и явно модифицировать исходный срез, передавайте указатель на срез:
➖ Важные моменты:
🔹
🔹 Чтобы избежать побочных эффектов, помните, что срезы — это ссылки на массивы, но функции, такие как
⚡️ Советы:
🔹 Если нужно модифицировать исходный срез, работайте с указателями.
🔹 Учитывайте, что
👉 @juniorGolang | #заметки
appendРабота со срезами в Go кажется простой, но функции, такие как
append, могут приводить к неожиданным побочным эффектам, если не учитывать их особенностейПример:
func modifySlice(slice []int) {
slice = append(slice, 4)
fmt.Println("Inside function:", slice) // [1, 2, 3, 4]
}
func main() {
originalSlice := []int{1, 2, 3}
modifySlice(originalSlice)
fmt.Println("Outside function:", originalSlice) // [1, 2, 3]
}На первый взгляд может показаться, что вызов append внутри функции изменит исходный срез. Но это не так! Причина в том, что append может создать новый массив, если емкости исходного среза недостаточно, и вернуть ссылку на новый массив, оставляя оригинальный срез неизменным
Чтобы избежать путаницы и явно модифицировать исходный срез, передавайте указатель на срез:
func modifySlice(slice *[]int) {
*slice = append(*slice, 4)
fmt.Println("Inside function:", *slice) // [1, 2, 3, 4]
}
func main() {
originalSlice := []int{1, 2, 3}
modifySlice(&originalSlice)
fmt.Println("Outside function:", originalSlice) // [1, 2, 3, 4]
}append может изменить размер и емкость среза. Если емкости хватает, исходный массив модифицируется; если нет — создаётся новый массив.append, могут менять поведениеappend не гарантирует модификацию оригинального массива, если емкость среза исчерпана.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤3👀2🤯1
Функция
#tip by Golangbot
👉 @juniorGolang | #tips
Join пакета errors конкатенирует список ошибок и возвращает ошибку, если хотя бы одна из переданных ошибок не nil.Join возвращает nil, если все переданные ошибки равны nil.#tip by Golangbot
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤2🤔2
Затенение переменных
Затенение переменных — одна из частых ошибок в Go, которая может привести к неожиданным последствиям. Пример:
Внутри блоков
✅ Как исправить?
1️⃣ Использовать другую переменную во внутренних блоках:
2️⃣ Или присваивать напрямую:
Затенение переменных в Go может усложнять код и приводить к трудноуловимым багам. Лучше избегать подобных ситуаций, чтобы код был более предсказуемым и легко читаемым👍
👉 @juniorGolang | #заметки
Затенение переменных — одна из частых ошибок в Go, которая может привести к неожиданным последствиям. Пример:
var client *http.Client
if tracing {
client, err := createClientWithTracing()
if err != nil {
return err
}
log.Println(client)
} else {
client, err := createDefaultClient()
if err != nil {
return err
}
log.Println(client)
}
Внутри блоков
if и else переменная client заново объявляется с помощью оператора :=, из-за чего внешняя переменная client остаётся неинициализированной. В итоге это приводит к ошибке, ведь внешняя переменная всё ещё равна nilvar client *http.Client
if tracing {
c, err := createClientWithTracing()
if err != nil {
return err
}
client = c
} else {
c, err := createDefaultClient()
if err != nil {
return err
}
client = c
}
var client *http.Client
var err error
if tracing {
client, err = createClientWithTracing()
} else {
client, err = createDefaultClient()
}
if err != nil {
return err
}
Затенение переменных в Go может усложнять код и приводить к трудноуловимым багам. Лучше избегать подобных ситуаций, чтобы код был более предсказуемым и легко читаемым
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤1🌚1👀1
This media is not supported in your browser
VIEW IN TELEGRAM
В репозитории много примеров для вдохновения
https://github.com/NimbleMarkets/ntcharts
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Запуск горутины без управления её завершением
Запуск горутины (
Пример:
Если процесс завершится раньше, горутина продолжит выполнение, что может привести к утечке памяти или нарушению логики программы.
Как избежать ошибки?
1️⃣ Используйте контекст (
2️⃣ Используйте
Почему это важно?
Бесконтрольные горутины приводят к непредсказуемым утечкам памяти и нагружают приложение. Это одна из самых частых проблем в Go-программах, которую сложно диагностировать на поздних этапах.
👉 @juniorGolang
Запуск горутины (
go func()) без механизма её завершения может привести к утечкам ресурсов и непредсказуемому поведению приложения. Особенно это критично для долгоживущих приложений или сервисов с высоким уровнем конкурентностиПример:
func process() {
go func() {
// Выполняется какая-то длительная задача
time.Sleep(5 * time.Second)
fmt.Println("Горутина завершена")
}()
}Если процесс завершится раньше, горутина продолжит выполнение, что может привести к утечке памяти или нарушению логики программы.
Как избежать ошибки?
context.Context) для управления временем жизни:func process(ctx context.Context) {
go func() {
select {
case <-ctx.Done():
fmt.Println("Горутина остановлена")
return
case <-time.After(5 * time.Second):
fmt.Println("Задача выполнена")
}
}()
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
process(ctx)
// Завершаем процесс через 2 секунды
time.Sleep(2 * time.Second)
cancel()
time.Sleep(3 * time.Second) // Ждём, чтобы увидеть результат
}sync.WaitGroup для координации завершения:func process(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(5 * time.Second)
fmt.Println("Задача завершена")
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go process(&wg)
wg.Wait() // Ждём завершения всех горутин
fmt.Println("Все задачи завершены")
}Почему это важно?
Бесконтрольные горутины приводят к непредсказуемым утечкам памяти и нагружают приложение. Это одна из самых частых проблем в Go-программах, которую сложно диагностировать на поздних этапах.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤4🔥2
В первой части рассматривается разработка REST API и работа с базой данных.
Вторая часть посвящена построению пользовательского интерфейса на React с Tailwind CSS.
В третьей части происходит интеграция фронтенда и бэкенда, тестирование и подготовка приложения к продакшену.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Каналы в Go: синхронизация и обмен данными
Каналы — это важная часть параллелизма в Go, предоставляющие механизм синхронизации и передачи данных между горутинами. Вот несколько ключевых приемов работы с каналами, которые помогут вам эффективно использовать их
1️⃣ Каналы для синхронизации
Когда несколько горутин должны взаимодействовать или завершать выполнение последовательно, можно использовать каналы. Например, при помощи канала можно дождаться завершения одной горутины перед запуском другой:
Канал
2️⃣ Каналы для передачи данных
Каналы позволяют обмениваться данными между горутинами. Вот пример, как одна горутина может передать число другой через канал:
Это простая передача данных между двумя горутинами через канал numbers. Важное правило: одна горутина пишет в канал, другая — читает.
3️⃣ Буферизованные каналы
Буферизованные каналы позволяют записывать данные в канал без блокировки, если количество элементов меньше заданного буфера:
Буферизованный канал
4️⃣ Закрытие каналов
Закрытие канала важно, когда отправляющая горутина больше не собирается передавать данные:
После закрытия канала, все попытки чтения из него продолжаются, пока не будут прочитаны все данные, после чего канал вернет нулевое значение для своего типа.
Каналы — мощный инструмент в Go для управления параллелизмом. Они обеспечивают простой и безопасный способ взаимодействия между горутинами, помогая создавать эффективные параллельные программы.
👉 @juniorGolang
Каналы — это важная часть параллелизма в Go, предоставляющие механизм синхронизации и передачи данных между горутинами. Вот несколько ключевых приемов работы с каналами, которые помогут вам эффективно использовать их
Когда несколько горутин должны взаимодействовать или завершать выполнение последовательно, можно использовать каналы. Например, при помощи канала можно дождаться завершения одной горутины перед запуском другой:
done := make(chan bool)
go func() {
// Выполнение задачи
done <- true // Сигнал завершения
}()
<-done // Ожидание завершения
Канал
done передает сигнал о завершении работы горутины, обеспечивая синхронизацию.Каналы позволяют обмениваться данными между горутинами. Вот пример, как одна горутина может передать число другой через канал:
numbers := make(chan int)
go func() {
numbers <- 42
}()
fmt.Println(<-numbers)
Это простая передача данных между двумя горутинами через канал numbers. Важное правило: одна горутина пишет в канал, другая — читает.
Буферизованные каналы позволяют записывать данные в канал без блокировки, если количество элементов меньше заданного буфера:
buffered := make(chan int, 2)
buffered <- 1
buffered <- 2
Буферизованный канал
buffered на 2 элемента позволит записать два значения без ожидания их немедленного чтения другой горутиной.Закрытие канала важно, когда отправляющая горутина больше не собирается передавать данные:
close(numbers)
После закрытия канала, все попытки чтения из него продолжаются, пока не будут прочитаны все данные, после чего канал вернет нулевое значение для своего типа.
Каналы — мощный инструмент в Go для управления параллелизмом. Они обеспечивают простой и безопасный способ взаимодействия между горутинами, помогая создавать эффективные параллельные программы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍5❤2
Интересный способ визуализировать большие проекты с помощью результатов
go test -coverprofile.$ go install github.com/nikolaydubina/go-cover-treemap@latest
$ go test -coverprofile cover.out ./...
$ go-cover-treemap -coverprofile cover.out > out.svg
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤2
Замыкания = универсальность
В языке Go замыкания используются повсеместно, давая возможность гибко решать разные задачи, используя один и тот же механизм.
Например, рассмотрим работу с картой в пакете sync — sync.Map:
Для итерации по карте есть метод Range с сигнатурой:
Но что, если нам нужно, например, посчитать сумму всех значений карты? Кажется, метод Range напрямую не позволяет этого сделать. Однако, благодаря замыканиям, мы можем обойти это ограничение:
Анонимная функция, передаваемая в Range, использует внешнюю переменную sum как накопитель. Таким образом, не изменяя сигнатуру метода, можно добиться нужного поведения.
Замыкания дают мощный инструмент для гибкости в коде.
👉 @juniorGolang
В языке Go замыкания используются повсеместно, давая возможность гибко решать разные задачи, используя один и тот же механизм.
Например, рассмотрим работу с картой в пакете sync — sync.Map:
var m sync.Map
m.Store("john", 21)
m.Store("maria", 25)
m.Store("steve", 29)
Для итерации по карте есть метод Range с сигнатурой:
Range(f func(key, value any) bool)
Но что, если нам нужно, например, посчитать сумму всех значений карты? Кажется, метод Range напрямую не позволяет этого сделать. Однако, благодаря замыканиям, мы можем обойти это ограничение:
sum := 0
m.Range(func(key, value any) bool {
sum += value.(int)
return true
})
Анонимная функция, передаваемая в Range, использует внешнюю переменную sum как накопитель. Таким образом, не изменяя сигнатуру метода, можно добиться нужного поведения.
Замыкания дают мощный инструмент для гибкости в коде.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Вы, вероятно, слышали о gRPC, если еще не использовали её. Она особенно популярна для межсервисной связи благодаря своей эффективности, языковой независимости и встроенной поддержке таких вещей, как MTLS.
Выше представлен простой пример начала работы с gRPC от Matt Boyle.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Полезные приемы форматирования
Вот несколько приемов форматирования, которые могут быть вам полезны:
1️⃣ Закавыченная строка
Используйте
2️⃣ Названия полей структуры
Используйте
3️⃣ Тип значения
Используйте
4️⃣ Индекс аргумента
Можно явно указать, какой по порядку аргумент выводить. Полезно, если одно и то же значение выводится несколько раз (как в примере с
Нумерация с 1.
👉 @juniorGolang
Вот несколько приемов форматирования, которые могут быть вам полезны:
Используйте
%q, чтобы вывести строковое значение в кавычках.s := "Hello, World!"
fmt.Printf("%q\n", s)
// "Hello, World!"
Используйте
%+v, чтобы вывести названия полей структуры, а не только значения.alice := person{"Alice", 25}
fmt.Printf("%+v\n", alice)
// {name:Alice age:25}Используйте
%T, чтобы вывести тип значения.var val any
val = 42
fmt.Printf("%T: %v\n", val, val)
// int: 42
val = "Hello, World!"
fmt.Printf("%T: %v\n", val, val)
// string: Hello, World!
val = person{"Alice", 25}
fmt.Printf("%T: %v\n", val, val)
// main.person: {Alice 25}
Можно явно указать, какой по порядку аргумент выводить. Полезно, если одно и то же значение выводится несколько раз (как в примере с
val выше).num := 42
fmt.Printf("%[1]T: %[1]v\n", num)
// int: 42
Нумерация с 1.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤8
Анонимная сеть в 100 строк кода на Go
👉 Читать статью
👉 @juniorGolang | #cтатья
Прошло уже более года с тех пор как я написал статью - Анонимная сеть в 200 строк кода на Go. Пересмотрев её однажды осенним вечером я понял насколько всё в ней было ужасно - начиная с самого поведения логики кода и заканчивая его избыточностью. Сев за ноутбук и потратив от силы 20 минут у меня получилось написать сеть всего в 100 строк кода, используя лишь и только стандартную библиотеку языка.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥9