HTTP/3, работающий на основе QUIC, обеспечивает быструю передачу данных поверх UDP. Для его использования в Go можно применять библиотеки вроде quic-go, позволяющие интегрировать поддержку HTTP/3.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Один из полезных паттернов, который я использовал в реальном проекте на Go — это Worker Pool (пул воркеров).
Этот паттерн помогает ограничить количество горутин при обработке задач, чтобы не перегружать систему.
В моём проекте был сервис, который обрабатывал тысячи файлов параллельно. Если запускать новую горутину на каждый файл, то память быстро заканчивалась, и сервер "умирал".
Решение: вместо создания тысяч горутин я использовал Worker Pool с фиксированным числом воркеров (например, 5), которые брали задачи из очереди.
Есть канал задач (
jobs
). Есть пул воркеров (
workers
), которые читают задачи из канала. Каждый воркер обрабатывает одну задачу и берёт следующую.
package main
import (
"fmt"
"sync"
"time"
)
// Количество воркеров
const workerCount = 5
// Воркер, который берет задачи из канала и обрабатывает их
func worker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job)
time.Sleep(time.Second) // Имитация обработки
}
}
func main() {
jobs := make(chan int, 10)
var wg sync.WaitGroup
// Запускаем воркеров
for i := 1; i <= workerCount; i++ {
wg.Add(1)
go worker(i, jobs, &wg)
}
// Отправляем задачи в канал
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs) // Закрываем канал, чтобы воркеры знали, что задачи кончились
wg.Wait() // Ждём завершения всех воркеров
fmt.Println("Все задачи обработаны")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊3👍1🤔1
Оконные функции (window functions) — это SQL-функции, которые выполняются по строкам с учётом контекста «окна» (группы строк).
Примеры использования:
- Нумерация (ROW_NUMBER())
- Скользящие суммы/средние (SUM() OVER(...))
- Сравнение соседних строк (LAG(), LEAD())
- Ранжирование (RANK())
Они позволяют делать аналитику прямо в SQL, без подзапросов и группировок.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Это система мониторинга и оповещения с открытым исходным кодом, разработанная для сбора и анализа метрик с серверов, приложений и других компонентов инфраструктуры.
позволяет отслеживать производительность, загрузку 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
👍4
2. Через make: slice := make([]int, length, capacity), где length — длина, а capacity — ёмкость.
3. Пустой слайс: var slice []int.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Forwarded from easyoffer
Ура, друзья! Изиоффер переходит в публичное бета-тестирование!
🎉 Что нового:
🟢 Анализ IT собеседований на основе 4500+ реальных интервью
🟢 Вопросы из собеседований с вероятностью встречи
🟢 Видео-примеры ответов на вопросы от Senior, Middle, Junior грейдов
🟢 Пример лучшего ответа
🟢 Задачи из собеседований
🟢 Тестовые задания
🟢 Примеры собеседований
🟢 Фильтрация всего контента по грейдам, компаниям
🟢 Тренажер подготовки к собеседованию на основе интервальных повторений и флеш карточек
🟡 Тренажер "Реальное собеседование" с сценарием вопросов из реальных собеседований (скоро)
🟢 Автоотклики на HeadHunter
🟢 Закрытое сообщество easyoffer
💎 Акция в честь открытия для первых 500 покупателей:
🚀 Скидка 50% на PRO тариф на 1 год (15000₽ → 7500₽)
🔥 Акция уже стартовала! 👉 https://easyoffer.ru/pro
🎉 Что нового:
💎 Акция в честь открытия для первых 500 покупателей:
🚀 Скидка 50% на PRO тариф на 1 год (
🔥 Акция уже стартовала! 👉 https://easyoffer.ru/pro
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
👍6
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Горутины и потоки (треды) в традиционном понимании операционных систем — это две различные концепции параллельного выполнения кода, каждая из которых имеет свои особенности и преимущества. Вот ключевые различия между ними.
Горутины — это легковесные "зеленые" потоки, управляемые Go runtime. Они не являются потоками операционной системы, и Go runtime отвечает за их планирование и выполнение на доступных физических потоках. Это позволяет создавать тысячи и даже миллионы горутин в рамках одного приложения с относительно небольшими затратами памяти и CPU.
Треды — это потоки выполнения, управляемые непосредственно операционной системой. Каждый тред занимает значительно больше ресурсов, чем горутина, особенно в плане памяти и времени на создание и управление. Треды более подходят для задач, требующих высокой вычислительной мощности и прямого взаимодействия с операционной системой.
Горутины потребляют гораздо меньше памяти по сравнению с тредами. Например, стек горутины начинается с нескольких килобайт, что значительно уменьшает затраты при масштабировании.
Треды требуют большего количества памяти для каждого стека, обычно начиная от нескольких сотен килобайт до мегабайтов. Это ограничивает количество потоков, которые могут быть активными одновременно без значительного увеличения затрат на ресурсы.
Горутины могут масштабироваться до большого количества параллельных задач благодаря меньшим требованиям к ресурсам и управлению со стороны runtime Go.
Треды ограничены в масштабируемости физическими ресурсами системы и более высокими затратами на управление.
Горутины имеют намного более эффективный контекст переключения, так как Go runtime оптимизирован для работы с большим количеством горутин и их переключением.
Треды терпят большие затраты времени на переключение контекста, так как операционной системе требуется больше времени для управления потоками.
package main
import (
"fmt"
"time"
)
func say(text string) {
for i := 0; i < 5; i++ {
fmt.Println(text)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
go say("Hello")
say("World")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Линтеры интегрируются в IDE или CI/CD пайплайны. Например, golangci-lint используется для анализа Go-кода.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Это неизменяемые последовательности байтов, обычно представляющие текст в кодировке UTF-8. Работа с ними в Go основывается на использовании встроенных функций, методов стандартной библиотеки и специальных типов данных.
Самый простой способ объединить строки — использовать оператор
+
или функцию fmt.Sprintf
.package main
import "fmt"
func main() {
s1 := "Привет"
s2 := "Мир"
result := s1 + ", " + s2 + "!"
fmt.Println(result) // Привет, Мир!
// Через fmt.Sprintf
formatted := fmt.Sprintf("%s, %s!", s1, s2)
fmt.Println(formatted) // Привет, Мир!
}
Функция
len
возвращает длину строки в байтах, а не в символах.package main
import "fmt"
func main() {
str := "Привет"
fmt.Println(len(str)) // 12, так как кириллические символы занимают 2 байта
}
Строки можно обойти как в виде байтов, так и в виде рун (Unicode символов).
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
str := "Привет"
// Итерация по байтам
for i := 0; i < len(str); i++ {
fmt.Printf("Байт: %x\n", str[i])
}
// Итерация по символам (рунам)
for _, runeValue := range str {
fmt.Printf("Символ: %c\n", runeValue)
}
// Количество рун
fmt.Println("Количество символов:", utf8.RuneCountInString(str))
}
Так как строки неизменяемы, для извлечения подстроки используется срез (
slice
).package main
import "fmt"
func main() {
str := "Привет, Мир!"
substring := str[8:] // С 8-го байта до конца
fmt.Println(substring) // Мир!
}
Функции
strings.Split
и strings.Join
из пакета strings
позволяют разбивать строку на части или объединять части в строку.package main
import (
"fmt"
"strings"
)
func main() {
str := "apple,banana,cherry"
// Разделение строки
parts := strings.Split(str, ",")
fmt.Println(parts) // [apple banana cherry]
// Объединение строк
joined := strings.Join(parts, " | ")
fmt.Println(joined) // apple | banana | cherry
}
Для поиска подстроки можно использовать
strings.Contains
, strings.Index
или strings.HasPrefix/strings.HasSuffix
. Для замены — strings.Replace
.package main
import (
"fmt"
"strings"
)
func main() {
str := "Go — это круто!"
// Поиск подстроки
fmt.Println(strings.Contains(str, "круто")) // true
fmt.Println(strings.Index(str, "это")) // 4
// Замена подстроки
newStr := strings.Replace(str, "круто", "отлично", 1)
fmt.Println(newStr) // Go — это отлично!
}
Стандартная библиотека предоставляет функции для изменения регистра строки:
strings.ToLower
и strings.ToUpper
.package main
import (
"fmt"
"strings"
)
func main() {
str := "Hello, Go!"
fmt.Println(strings.ToUpper(str)) // HELLO, GO!
fmt.Println(strings.ToLower(str)) // hello, go!
}
Удаление пробелов или других символов с начала/конца строки выполняется с помощью
strings.Trim
, strings.TrimSpace
и других функций.package main
import (
"fmt"
"strings"
)
func main() {
str := " Hello, Go! "
fmt.Println(strings.TrimSpace(str)) // "Hello, Go!"
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🤔1
- Документо-ориентированная база данных.
- Хранит данные в формате BSON (похож на JSON).
- Подходит для гибкой схемы или её отсутствия.
- Хороша для быстрого прототипирования и хранения неструктурированных данных.
PostgreSQL:
- Реляционная СУБД с жёсткой схемой.
- Использует таблицы и SQL-запросы.
- Сильная поддержка транзакций, связей, индексов и сложных запросов.
Mongo — для гибкости и масштабируемости, Postgres — для структурированных и строгих данных.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Это интерфейс, который не содержит методов. Поскольку интерфейсы в Go определяют поведение, которое тип должен реализовать, пустой интерфейс, не имеющий методов, автоматически реализуется всеми типами. Это делает пустой интерфейс универсальным контейнером для значений любого типа.
Пустой интерфейс может содержать значение любого типа, потому что все типы в Go автоматически реализуют пустой интерфейс.
Пустой интерфейс широко используется для создания обобщенных (generic) структур данных, функций и методов, которые могут работать с данными любых типов.
Пустой интерфейс может использоваться для хранения значений различных типов в одной переменной.
package main
import "fmt"
func main() {
var i interface{}
i = 42
fmt.Println(i) // Output: 42
i = "hello"
fmt.Println(i) // Output: hello
i = true
fmt.Println(i) // Output: true
}
Пустой интерфейс позволяет создавать функции, которые могут принимать параметры любого типа.
package main
import "fmt"
func printValue(v interface{}) {
fmt.Println(v)
}
func main() {
printValue(42)
printValue("hello")
printValue(true)
}
Пустой интерфейс используется для создания структур данных, которые могут хранить значения различных типов.
package main
import "fmt"
func main() {
var values []interface{}
values = append(values, 42, "hello", true)
for _, v := range values {
fmt.Println(v)
}
}
Пустой интерфейс используется для обработки данных различных типов, например, при парсинге JSON.
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonData := `{"name": "Alice", "age": 30}`
var result map[string]interface{}
json.Unmarshal([]byte(jsonData), &result)
fmt.Println(result)
}
При работе с пустым интерфейсом часто возникает необходимость проверить тип хранимого значения и привести его к конкретному типу. Это можно сделать с помощью утверждения типа (
type assertion
) или конструкции switch
.Утверждение типа позволяет проверить и преобразовать значение пустого интерфейса к конкретному типу.
package main
import "fmt"
func main() {
var i interface{} = "hello"
s, ok := i.(string)
if ok {
fmt.Println("String:", s) // Output: String: hello
} else {
fmt.Println("Not a string")
}
n, ok := i.(int)
if ok {
fmt.Println("Integer:", n)
} else {
fmt.Println("Not an integer")
}
}
Конструкция
switch
позволяет обрабатывать значения различных типов, хранящиеся в пустом интерфейсе.package main
import "fmt"
func printType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Integer:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
func main() {
printType("hello") // Output: String: hello
printType(42) // Output: Integer: 42
printType(true) // Output: Boolean: true
printType(3.14) // Output: Unknown type: float64
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥1
Кэширование помогает:
- Ускорить доступ к часто используемым данным.
- Снизить нагрузку на сервер или БД.
- Сократить сетевой трафик и задержки.
- Обеспечить плавную работу при временных перебоях внешних сервисов.
Кэш может быть в памяти, на диске, на клиенте, в браузере или на уровне CDN.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
В Go обычный
map
не потокобезопасен. Если несколько горутин одновременно записывают в map
, возникнет ошибка "fatal error: concurrent map writes". Решение: использовать синхронизацию через
sync.Mutex
, sync.RWMutex
или sync.Map
. Блокируем доступ на запись и чтение через
sync.Mutex
. package main
import (
"fmt"
"sync"
)
type SafeMap struct {
mu sync.Mutex
m map[string]int
}
func (s *SafeMap) Set(key string, value int) {
s.mu.Lock() // Блокируем доступ
defer s.mu.Unlock()
s.m[key] = value
}
func (s *SafeMap) Get(key string) int {
s.mu.Lock() // Блокируем на чтение
defer s.mu.Unlock()
return s.m[key]
}
func main() {
safeMap := SafeMap{m: make(map[string]int)}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
safeMap.Set(fmt.Sprintf("key%d", i), i)
}(i)
}
wg.Wait()
fmt.Println("Готово:", safeMap.Get("key5")) // Получаем значение без гонок данных
}
sync.RWMutex
позволяет нескольким горутинам читать одновременно, но блокирует запись. type SafeMap struct {
mu sync.RWMutex
m map[string]int
}
func (s *SafeMap) Set(key string, value int) {
s.mu.Lock() // Блокируем только на запись
defer s.mu.Unlock()
s.m[key] = value
}
func (s *SafeMap) Get(key string) int {
s.mu.RLock() // Разрешаем множественное чтение
defer s.mu.RUnlock()
return s.m[key]
}
sync.Map
из стандартной библиотеки уже потокобезопасен, но работает немного медленнее обычного map
из-за внутренних механизмов. package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
m.Store(i, i*10) // Потокобезопасная запись
}(i)
}
wg.Wait()
val, ok := m.Load(5) // Потокобезопасное чтение
if ok {
fmt.Println("Значение:", val)
}
}
Использование канала (`chan`) вместо `map`
Вместо
map
можно передавать данные через канал (chan
), если логика позволяет. package main
import (
"fmt"
)
func main() {
ch := make(chan map[string]int, 1)
ch <- make(map[string]int)
go func() {
m := <-ch
m["key"] = 42
ch <- m
}()
m := <-ch
fmt.Println(m["key"]) // 42
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2