Это система мониторинга и оповещения с открытым исходным кодом, разработанная для сбора и анализа метрик с серверов, приложений и других компонентов инфраструктуры.
позволяет отслеживать производительность, загрузку CPU, память, сетевой трафик и другие параметры.
использует модель pull (запрашивает данные у сервисов), а не push (как, например, Graphite). Это удобнее для управления и отказоустойчивости.
поддерживает кастомные метрики, которые можно интегрировать в своё приложение.
позволяет настроить уведомления при достижении критических значений.
помогает анализировать тренды и предсказывать возможные сбои.
легко развертывается в облаке, Kubernetes, Docker и других средах.
собирают данные.
опрашивает эти экспортеры по HTTP (pull-модель).
(язык запросов) используется для анализа данных.
или другие инструменты визуализируют метрики.
отправляет уведомления (Slack, Email, Telegram и др.).
Для интеграции в Go-приложение можно использовать пакет
prometheus/client_golang
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var httpRequests = prometheus.NewCounter(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
)
func handler(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc() // Увеличиваем счетчик запросов
w.Write([]byte("Hello, Prometheus!"))
}
func main() {
// Регистрируем метрику
prometheus.MustRegister(httpRequests)
// Эндпоинт для сбора метрик
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В реальных проектах часто используются:
- Builder — для конфигурации сложных объектов (например, HTTP-клиентов).
- Factory — для создания сервисов по интерфейсу.
- Singleton — для централизованного логгера или глобальных параметров.
- Также могут применяться Strategy, Adapter, Observer, особенно при построении архитектуры с плагинами, хранилищами, UI-обработкой.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go дополнительный блок фигурных скобок
{}
в функции используется для ограничения области видимости переменных и улучшения читаемости кода. Это позволяет временно вводить переменные или выполнять локальные операции без влияния на остальную часть функции.Переменные, объявленные внутри блока
{}
, видимы только в пределах этого блока. Это полезно для временных переменных, которые не должны "загрязнять" основную область видимости функции.func processData() {
{
value := 42
fmt.Println("Внутренний блок, value =", value) // value доступна только здесь
}
// fmt.Println(value) // Ошибка: value не определена за пределами блока
}
С помощью блоков можно явно управлять временем жизни переменных, таких как файлы, соединения или буферы. Это особенно важно для ресурсов, которые необходимо закрыть или освободить.
func readFile() {
{
file, err := os.Open("example.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close() // Закроется при выходе из блока
// Работа с файлом
fmt.Println("Чтение файла")
}
// Здесь файл уже закрыт
}
В больших функциях дополнительный блок позволяет логически разделить код на части, делая его более понятным.
func main() {
{
// Работа с настройками
config := loadConfig()
fmt.Println("Конфигурация загружена:", config)
}
{
// Работа с данными
data := fetchData()
fmt.Println("Данные получены:", data)
}
}
Можно использовать дополнительные блоки для изоляции сложных кусков кода и последующего их упрощения.
func calculate() int {
result := 0
{
a := 10
b := 20
result = a + b
}
return result
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Шардирование — горизонтальное распределение данных по разным узлам или серверам. Например, одни пользователи хранятся в одном шарде, другие — в другом.
- Партиционирование — разбиение данных внутри одной базы или таблицы, например по дате, географии или категории. Все данные при этом могут оставаться на одном сервере.
Ключевое отличие: шардирование — про масштабирование инфраструктуры, партиционирование — про организацию данных внутри.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Нет, строки (
string
) в Go неизменяемы (immutable). Это значит, что нельзя просто изменить один символ в строке. Строка в Go — это байтовый срез (
[]byte
), но неизменяемый. Когда вы создаёте строку s := "hello"
Ошибка при попытке изменения символа напрямую
s := "hello"
s[0] = 'H' // Ошибка: cannot assign to s[0]
Поскольку строка неизменяема, вам нужно создать новую строку с заменённым символом.
Способ 1: Преобразовать в
[]byte
(для ASCII-строк) Если строка содержит только английские буквы и символы ASCII, её можно преобразовать в
[]byte
, заменить символ и создать новую строку. package main
import "fmt"
func main() {
s := "hello"
b := []byte(s) // Преобразуем в изменяемый []byte
b[0] = 'H' // Меняем первый символ
s = string(b) // Преобразуем обратно в строку
fmt.Println(s) // "Hello"
}
Способ 2: Преобразовать в
[]rune
(для Unicode)Если строка содержит русские буквы, эмодзи или другие многобайтовые символы, используйте
[]rune
. package main
import "fmt"
func main() {
s := "привет"
r := []rune(s) // Преобразуем в []rune (массив символов)
r[0] = 'П' // Меняем первый символ
s = string(r) // Преобразуем обратно в строку
fmt.Println(s) // "Привет"
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
ACID — это набор свойств транзакций в базах данных, обеспечивающий надежность и целостность данных.
Транзакция выполняется либо полностью, либо не выполняется вовсе.
Пример: Если при переводе денег со счета A на счет B ошибка произошла на полпути, изменения откатываются.
Данные остаются в правильном состоянии до и после транзакции.
Пример: Если сумма на всех счетах банка должна оставаться неизменной, транзакция не должна нарушить это правило.
Одновременные транзакции не мешают друг другу.
Пример: Два клиента покупают один и тот же товар, но база данных правильно обрабатывает, кто купил первым.
После завершения транзакции изменения сохраняются, даже если система сломается.
Пример: Если заказ оформлен, он не исчезнет при внезапном отключении электричества.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Go использует трассирующий сборщик мусора с метками (mark-and-sweep).
Он проходит в два этапа:
1. Mark — находят все доступные (живые) объекты.
2. Sweep — освобождают недоступные (мертвые) объекты.
GC работает инкрементально и в параллель с приложением, начиная с Go 1.5.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Существуют встроенные функции для преобразования типов, включая преобразование из строки в целое число и наоборот. Для этих целей используются функции из стандартной библиотеки
strconv
. Рассмотрим, как это делается.Для этого используется функция
strconv.Atoi
. Она возвращает два значения: само число и ошибку, если преобразование не удалось.package main
import (
"fmt"
"strconv"
)
func main() {
str := "123"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error converting string to int:", err)
} else {
fmt.Println("Converted number:", num)
}
}
Для этого используется функция
strconv.Itoa
.package main
import (
"fmt"
"strconv"
)
func main() {
num := 123
str := strconv.Itoa(num)
fmt.Println("Converted string:", str)
}
Важно обрабатывать ошибки при преобразовании типов, особенно при преобразовании строки в целое число, чтобы избежать неожиданных сбоев в программе.
package main
import (
"fmt"
"strconv"
)
func main() {
str := "abc"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Converted number:", num)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Перед названием метода указывается ресивер — переменная, к которой метод привязан. Он описывается в круглых скобках перед именем метода и указывает, для какого типа метод определён.
Здесь u *User — это ресивер. Он определяет, что метод PrintName относится к типу User, и при вызове будет доступ к его полям.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Размер числа в байтах зависит от его типа данных. В Go есть несколько числовых типов, и каждый занимает определённое количество байтов в памяти.
Можно проверить размер типа с помощью
unsafe.Sizeof()
package main
import (
"fmt"
"unsafe"
)
func main() {
var a int64
var b float64
var c byte // то же самое, что uint8
fmt.Println("int64:", unsafe.Sizeof(a)) // 8
fmt.Println("float64:", unsafe.Sizeof(b)) // 8
fmt.Println("byte:", unsafe.Sizeof(c)) // 1
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Карты (maps) реализованы на основе хеш-таблиц, что обеспечивает быстрый доступ к значениям по ключам. Давайте рассмотрим, как происходит поиск по ключу в карте, и какие этапы включены в этот процесс.
Сначала вычисляется хеш-значение ключа. Функция хеширования преобразует ключ в целое число, которое служит индексом в хеш-таблице.
Хеш-значение используется для доступа к соответствующей "ячейке" или "корзине" (bucket) в хеш-таблице.
Если корзина содержит несколько элементов (из-за коллизий хеширования), Go выполняет линейный поиск среди этих элементов, сравнивая ключи с использованием оператора
==
.Когда вы пытаетесь получить значение по ключу, Go сначала вычисляет хеш-значение этого ключа. Хеш-функция берет ключ (например, строку или целое число) и преобразует его в индекс хеш-таблицы.
Хеш-значение указывает на конкретную корзину в хеш-таблице. Корзина может содержать один или несколько элементов. В случае коллизий (когда несколько ключей хешируются в один и тот же индекс) корзина может содержать связанный список или другой механизм для хранения нескольких элементов.
Если корзина содержит несколько элементов, Go выполняет линейный поиск среди этих элементов. Для каждого элемента в корзине сравнивается ключ с искомым ключом с использованием оператора
==
. Если ключи совпадают, возвращается соответствующее значение. Если ключ не найден, возвращается нулевое значение типа (zero value) и флаг, указывающий на отсутствие ключа.package main
import "fmt"
func main() {
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}
value, exists := myMap["Alice"]
if exists {
fmt.Println("Alice:", value) // Alice: 25
} else {
fmt.Println("Alice not found")
}
value, exists = myMap["Charlie"]
if exists {
fmt.Println("Charlie:", value)
} else {
fmt.Println("Charlie not found") // Charlie not found
}
}
Даже при хорошей хеш-функции неизбежны коллизии, когда разные ключи хешируются в один и тот же индекс. Эффективно обрабатывает такие случаи, используя корзины для хранения элементов с одинаковыми хеш-значениями.
В среднем, доступ к элементу в карте осуществляется за константное время O(1), что делает карты очень эффективными для поиска по ключу. Однако в худшем случае, при большой нагрузке коллизий, производительность может деградировать до линейного времени O(n).
Карты не являются потокобезопасными. Если одна горутина изменяет карту, в то время как другая горутина читает из нее, это может привести к панике. Для обеспечения потокобезопасности используйте мьютексы или структуру
sync.Map
.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Интеграционные тесты проверяют взаимодействие между компонентами, например, работу сервиса с базой или API с внешней системой.
Юнит-тесты дают быструю обратную связь, интеграционные — показывают, как работает система целиком.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Завершение множества горутин требует организованного подхода, так как управление ими не предоставляет прямых средств для их остановки. Основные практики включают использование каналов для сигнализации о необходимости завершения, контекстов для управления временем выполнения и ограничениями, а также синхронизации с помощью
sync.WaitGroup
. Вот каждый из этих методов.Каналы могут использоваться для отправки сигналов горутинам о том, что им следует завершить свою работу. Это один из наиболее часто используемых подходов, так как он прост в реализации и очень эффективен.
package main
import (
"fmt"
"sync"
"time"
)
func worker(stopCh <-chan struct{}, wg *sync.WaitGroup, id int) {
defer wg.Done()
for {
select {
case <-stopCh:
fmt.Printf("Worker %d stopping\n", id)
return
default:
// выполнение полезной работы
fmt.Printf("Worker %d working\n", id)
time.Sleep(time.Second)
}
}
}
func main() {
var wg sync.WaitGroup
stopCh := make(chan struct{})
// запуск горутин
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(stopCh, &wg, i)
}
// остановка горутин после 3 секунд
time.Sleep(3 * time.Second)
close(stopCh) // отправка сигнала всем горутинам остановиться
wg.Wait() // ожидание завершения всех горутин
}
Предоставляет функциональность для передачи контекста внутрь вашей программы, включая сигналы о необходимости завершения работы. Это может быть полезно, если у вас есть иерархия горутин с общим временем выполнения или дополнительными ограничениями.
package main
import (
"context"
"fmt"
"sync"
"time"
)
func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
defer wg.Done()
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d stopping\n", id)
return
default:
// выполнение полезной работы
fmt.Printf("Worker %d working\n", id)
time.Sleep(time.Second)
}
}
}
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
// запуск горутин
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(ctx, &wg, i)
}
wg.Wait() // ожидание завершения всех горутин
cancel() // убедиться, что все ресурсы освобождены
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM