Golang | Вопросы собесов
4.7K subscribers
30 photos
961 links
Download Telegram
🤔 Как использовать линтеры (linters)?

Линтеры — это инструменты для автоматической проверки кода на ошибки, потенциальные баги и несоответствие стилю кодирования.

🟠Популярные линтеры для Go
В Go есть несколько популярных линтеров:
golangci-lint – самый мощный и популярный, объединяет множество линтеров.
go vet – стандартный инструмент для поиска ошибок.
golint – проверяет стиль кода (но устарел).
staticcheck – анализирует код на ошибки и неэффективность.

🟠Установка линтера
Устанавливаем golangci-lint (лучший вариант)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest


После установки проверьте версию:
golangci-lint --version


🟠Запуск линтера
Запустить проверку в проекте можно так:
golangci-lint run


Можно проверить только определённый файл:
golangci-lint run myfile.go


Если хотите автоматически исправлять ошибки, используйте:
golangci-lint run --fix


🟠Использование `go vet` (встроенный анализатор)
Go уже имеет встроенный линтер
go vet ./...


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что такое CI/CD?

Это подход к автоматизации сборки, тестирования и развертывания приложений. Он позволяет разработчикам быстрее и стабильнее доставлять обновления пользователям.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как эффективно склеивать множество строк?

В Go эффективное объединение строк – важная задача, поскольку строки неизменяемые. Неправильный подход (например, простая конкатенация s1 + s2 + s3) может привести к множественным аллокациям памяти и копированиям.

🚩Способы объединения строк

Использование strings.Builder (Рекомендуется)
Это самый эффективный способ склеивания строк, так как он минимизирует количество аллокаций.
package main

import (
"fmt"
"strings"
)

func main() {
var sb strings.Builder

sb.WriteString("Hello")
sb.WriteString(", ")
sb.WriteString("World!")

result := sb.String()
fmt.Println(result) // Hello, World!
}


Использование + (Неэффективно )
s := "Hello" + ", " + "World!"


Использование fmt.Sprintf (Неэффективно )
s := fmt.Sprintf("%s, %s!", "Hello", "World")


Использование strings.Join (Хорошо для срезов )
Если строки хранятся в []string, strings.Join – это оптимальный вариант
package main

import (
"fmt"
"strings"
)

func main() {
words := []string{"Hello", "World", "Go"}
result := strings.Join(words, ", ")
fmt.Println(result) // Hello, World, Go
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что такое микросервисная архитектура?

Микросервисная архитектура — это подход к разработке программного обеспечения, при котором приложение состоит из маленьких, независимых и модульных сервисов, каждый из которых выполняет определенную функцию и общается с другими сервисами посредством легковесных механизмов, таких как HTTP API. Это позволяет упростить разработку, тестирование, развертывание и масштабирование каждого сервиса независимо.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Куда смотреть если лагает сервер?

Когда сервер тормозит, важно быстро найти причину. Разберём основные направления диагностики.

🟠Логи (логирование)
Сначала проверяем логи!
Системные логи (journalctl -u myservice, /var/log/syslog, /var/log/messages)
Логи приложения (например, logs/app.log)
Nginx / Apache (/var/log/nginx/error.log, /var/log/httpd/error.log)
Пример просмотра последних 100 строк лога:
tail -n 100 /var/log/syslog


Если используете docker:
docker logs my_container --tail 100 -f


🟠Нагрузка на процессор и память
Проверяем, не перегружен ли сервер:
top
htop # Более удобный интерфейс


Или просто:
ps aux --sort=-%cpu | head -10  # Топ-10 процессов по CPU
ps aux --sort=-%mem | head -10 # Топ-10 процессов по памяти


Если процесс "myapp" жрет CPU, смотрим его нагрузку:
pidstat -p $(pgrep myapp)


🟠Сетевые проблемы
Проверяем сетевую активность:
netstat -tulnp  # Слушающие порты
ss -tulnp # Альтернатива netstat


Пингуем сервер:
ping google.com


Проверяем скорость соединения:
wget --output-document=/dev/null https://speedtest.tele2.net/10MB.zip


🟠Нагрузка на диск
Проверяем, не забит ли диск:
df -h   # Свободное место
du -sh /* | sort -h # Топ занимаемых мест


Мониторинг активности диска:
iotop  # Если доступен


🟠База данных
Если сервер использует PostgreSQL / MySQL, смотрим запросы:
SHOW PROCESSLIST;  # MySQL: текущие запросы
SELECT * FROM pg_stat_activity; # PostgreSQL: активные запросы


Проверяем медленные запросы (если включен slow_query_log):
tail -n 100 /var/log/mysql/slow.log


---

🟠Go-профилирование (если сервер на Go)
Включаем pprof и анализируем:
import _ "net/http/pprof"
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()


Запускаем профайлер:
go tool pprof https://localhost:6060/debug/pprof/profile?seconds=30


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как устроен процесс удаления из конца и начала массива?

Удаление с конца массива — операция быстрая, так как не требует сдвига других элементов. Удаление с начала — медленнее, так как все последующие элементы приходится сдвигать. Это особенно важно для больших массивов или при частых операциях.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как строки работают в Golang внутри?

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

🚩Неизменяемость

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

🚩Структура строки

Внутренне строка представлена структурой, которая содержит два поля:
Указатель на массив байтов: Это указатель на первый элемент массива байт, который фактически хранит символы строки в кодировке UTF-8.
Длина: Количество байт в строке, а не количество рун или символов. Это важное различие, поскольку в UTF-8 один символ может занимать от 1 до 4 байт.

🚩UTF-8 как стандартная кодировка

Go использует UTF-8 как стандартную кодировку для строк. Это позволяет эффективно работать с международным текстом, поддерживая широкий спектр символов без сложностей, связанных с другими кодировками. Однако это также означает, что операции, такие как получение длины строки в рунах (символах) или доступ к отдельному символу, могут потребовать дополнительных вычислений для обработки многобайтовых символов.

🚩Срезы строк

Поскольку строки неизменяемы, любая операция, которая кажется "изменяющей" строку, на самом деле создает новую строку. Операции среза строк в Go особенно эффективны, потому что новые строки создаются путем указания на тот же массив байтов, что и исходная строка, с изменением только начальной позиции и длины. Это делает срезы строк очень быстрыми и экономичными с точки зрения использования памяти.

🚩Производительность и память

Благодаря неизменяемости и способу хранения строк в виде срезов байтов, Go обеспечивает эффективное управление памятью и производительность при работе со строками. Однако необходимо быть осторожным с операциями, которые могут казаться невинными, но приводят к частому созданию новых строк, так как это может повлечь за собой издержки на выделение памяти и сборку мусора.
s := "Hello, world"      // Создание строки
t := s[7:] // Срез строки, создает новую строку "world"

fmt.Println(s) // Выводит: Hello, world
fmt.Println(t) // Выводит: world


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как называется структура, которая копирует, когда что-либо передаешь?

Такая структура называется значимым типом (value type).
Когда ты передаёшь её в функцию или присваиваешь другой переменной, создаётся копия значения, а не ссылка. В Go к value types относятся: int, float, bool, struct, array.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊3👍2
🤔 Сколько весит число в байтах?

Размер числа в байтах зависит от его типа данных. В Go есть несколько числовых типов, и каждый занимает определённое количество байтов в памяти.

🚩Проверка размера в Go (`unsafe.Sizeof`)

Можно проверить размер типа с помощью 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
👍1
🤔 Что такое сага?

Это архитектурный паттерн управления долгоживущими распределёнными транзакциями.
Вместо одной глобальной транзакции, используется последовательность локальных операций, каждая из которых имеет компенсирующее действие, если что-то пошло не так.
Применяется в микросервисной архитектуре, где нельзя использовать обычные транзакции между сервисами. Есть два варианта исполнения: оркестрация (центральный координатор) и хореография (сервисы реагируют на события друг друга).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как каналы устроены в Go?

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

🚩Основы

Могут быть типизированы, что означает, что канал может передавать значения только одного определённого типа. Они могут быть объявлены и инициализированы с помощью ключевого слова chan:
ch := make(chan int) // Канал для передачи значений типа int


🚩Отправка и получение данных

Для отправки значения в канал используется оператор <-:
ch <- 10 // Отправка значения 10 в канал


Для получения значения из канала тот же оператор используется, но в другом контексте:
value := <-ch // Прочитать значение из канала и присвоить его переменной value


🚩Блокировки

Особенностью является то, что операции отправки и получения данных являются блокирующими:
Если горутина пытается отправить данные в канал, она блокируется до тех пор, пока другая горутина не прочитает эти данные.
Аналогично, если горутина пытается прочитать данные из канала, она блокируется до тех пор, пока другая горутина не отправит данные в этот канал.

🚩Буферизация

Могут быть буферизированными, что означает, что они могут хранить ограниченное количество значений без необходимости немедленного получения. Буферизированный канал инициализируется с указанием размера буфера:
ch := make(chan int, 5) // Буферизированный канал с размером буфера 5


В буферизированном канале отправка не блокируется до тех пор, пока буфер не заполнится, и получение не блокируется до тех пор, пока буфер не опустеет.

🚩Закрытие каналов

Каналы можно закрывать, если больше нет необходимости отправлять через них данные. После закрытия канала нельзя отправлять данные, но можно продолжать получать данные до тех пор, пока канал не опустеет:
close(ch)


Проверка на то, что канал закрыт и данные исчерпаны, возможна в операции чтения:
value, ok := <-ch
if !ok {
// Канал закрыт и все данные получены
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 В чем отличия HTTP 1.1 и HTTP 2?

HTTP/2 поддерживает мультиплексирование запросов, бинарный формат и сжатие заголовков, что делает его быстрее, чем текстовый и последовательный HTTP/1.1.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 В чем разница между императивным и декларативным?

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

🚩Императивное программирование

Это стиль программирования, где выражается последовательность команд для выполнения задач. В этом подходе программист указывает машине, как изменять своё состояние пошагово, контролируя поток выполнения через управляющие конструкции, такие как циклы, условные операторы и т.д.
🟠Подробное описание процесса
Программист должен указывать все шаги, которые необходимо выполнить для достижения результата.
🟠Примеры языков
Java, C, Python в их традиционном использовании.
🟠Управление состоянием
Императивные программы часто включают явное управление состоянием и его изменениями.
# Императивный подход к сортировке массива методом пузырька
def bubble_sort(array):
n = len(array)
for i in range(n):
for j in range(0, n-i-1):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
return array


🚩Декларативное программирование

Это стиль программирования, где описывается желаемый результат, но не детализируется процесс его достижения. В декларативном стиле программа определяет, что должно быть сделано, а не как.
🟠Абстракция от процесса
Описываются желаемые свойства результата, а система сама определяет, как его достичь.
🟠Примеры языков
SQL, HTML, CSS, функциональные языки программирования, такие как Haskell.
🟠Сокрытие сложности
Декларативный подход часто предполагает высокий уровень абстракции, что уменьшает количество деталей, которые нужно учитывать.

-- Декларативный запрос в SQL для получения списка сотрудников, отсортированного по зарплате
SELECT name, salary FROM employees ORDER BY salary DESC;


🚩Основные различия

🟠Описание vs. результат
Императивное программирование фокусируется на описании шагов, необходимых для достижения результата, в то время как декларативное программирование описывает желаемый результат без спецификации конкретных шагов.

🟠Управление состоянием
Императивный подход требует активного управления состоянием программы, в то время как в декларативном подходе состояние управляется системой или вовсе абстрагировано.

🟠Поддержка языка
Разные языки поддерживают разные стили программирования. Некоторые языки, как JavaScript, могут поддерживать оба стиля в зависимости от использования.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Что будет, если подставить переменную в массив?

Если переменную подставить как индекс, будет доступ к соответствующему элементу массива. Если переменная подставляется как значение, она становится одним из элементов массива. Некорректное использование может привести к ошибкам компиляции или выполнения.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Какие функции у map?

Карты (maps) предоставляют несколько основных функций и операций для работы с ними. Эти функции позволяют добавлять, удалять, получать значения и проверять наличие ключей в карте.

🚩Основные операции

🟠Объявление карты
Для этого используется ключевое слово map, после которого указываются типы ключей и значений.
var myMap map[string]int


🟠Инициализация карты
Это можно сделать с помощью функции make или литерала карты.
// Инициализация с помощью make
myMap = make(map[string]int)

// Инициализация с помощью литерала карты
myMap = map[string]int{
"Alice": 25,
"Bob": 30,
}


🟠Добавление и обновление элементов
Для этого используется синтаксис индексирования.
myMap["Charlie"] = 35
myMap["Alice"] = 26 // обновление значения по ключу "Alice"


🟠Извлечение значения по ключу
Для этого используется синтаксис индексирования.
age := myMap["Alice"]
fmt.Println(age) // 26


🟠Проверка существования ключа
Чтобы проверить это можно использовать двойное присваивание.
age, exists := myMap["David"]
if exists {
fmt.Println("Age of David:", age)
} else {
fmt.Println("David not found")
}


🟠Удаление элемента
Для этого используется встроенная функция delete.
delete(myMap, "Bob")


🟠Итерация по карте
Для этого используется цикл for range.
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}


Пример
package main

import (
"fmt"
)

func main() {
// Инициализация карты с помощью литерала
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

// Добавление нового элемента
myMap["Charlie"] = 35

// Обновление существующего элемента
myMap["Alice"] = 26

// Извлечение значения по ключу
age := myMap["Alice"]
fmt.Println("Age of Alice:", age) // 26

// Проверка существования ключа
age, exists := myMap["David"]
if exists {
fmt.Println("Age of David:", age)
} else {
fmt.Println("David not found")
}

// Удаление элемента
delete(myMap, "Bob")

// Итерация по карте
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как устроены горутины, сколько памяти они занимают в стеке?

Горутины — это легковесные потоки, управляемые рантаймом Go, а не ОС.
Каждая горутина:
- Получает начальный стек размером ~2 КБ.
- Имеет собственные регистры состояния и структуру для планирования.
- Может перераспределять стек по мере роста (growable stack), что экономит ресурсы.
Благодаря этому в Go можно создать десятки или сотни тысяч горутин, в отличие от потоков ОС.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
🤔 Что такое структура (stuct) в Go Зачем они нужны?

Структуры (struct) представляют собой агрегированные типы данных, которые позволяют объединять несколько различных типов данных под одним именем. Они служат для моделирования объектов и хранения связанных данных, предоставляя удобный способ управления сложными данными.

🚩Определение структуры

Определяются с использованием ключевого слова struct. В структуре могут быть поля различных типов.
package main

import "fmt"

// Определение структуры Person
type Person struct {
Name string
Age int
}

func main() {
// Создание экземпляра структуры
var p Person
p.Name = "Alice"
p.Age = 30

fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}


🚩Инициализация структур

Существует несколько способов инициализации структур.
package main

import "fmt"

// Определение структуры Person
type Person struct {
Name string
Age int
}

func main() {
// Инициализация с использованием литерала структуры
p := Person{Name: "Bob", Age: 25}
fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
}


Инициализация по умолчанию
package main

import "fmt"

// Определение структуры Person
type Person struct {
Name string
Age int
}

func main() {
// Инициализация по умолчанию (поля будут нулевыми значениями)
var p Person
fmt.Println("Name:", p.Name) // Пустая строка
fmt.Println("Age:", p.Age) // 0
}


🚩Вложенные структуры

Могут содержать другие структуры в качестве полей, что позволяет моделировать более сложные данные.
package main

import "fmt"

// Определение структур Address и Person
type Address struct {
City string
State string
}

type Person struct {
Name string
Age int
Address Address
}

func main() {
// Инициализация структуры с вложенной структурой
p := Person{
Name: "Charlie",
Age: 40,
Address: Address{
City: "New York",
State: "NY",
},
}

fmt.Println("Name:", p.Name)
fmt.Println("Age:", p.Age)
fmt.Println("City:", p.Address.City)
fmt.Println("State:", p.Address.State)
}


🚩Методы структур

Могут быть ассоциированы со структурами, что позволяет добавлять функциональность к структурам.
package main

import "fmt"

// Определение структуры Person
type Person struct {
Name string
Age int
}

// Метод для структуры Person
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)
}

func main() {
p := Person{Name: "David", Age: 35}
p.Greet()
}



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

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Почему поиск в B-tree работает быстрее, чем линейный перебор?

B-tree (сбалансированное дерево) организует данные так, чтобы:
- Быстро сужать поиск на каждом уровне.
- Работать эффективно с дисковой памятью (большие блоки и минимум обращений). Линейный перебор проходит все элементы, а B-tree уменьшает количество операций до логарифмического времени — особенно важно при работе с большими объёмами данных.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Почему пустой интерфейс можно привести к любому типу?

В Go пустой интерфейс interface{} является особым типом, который может содержать значение любого типа. Это связано с тем, что в Go любой тип реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать.

🚩Пустой интерфейс

Поскольку пустой интерфейс не требует реализации каких-либо методов, любой тип в Go автоматически реализует этот интерфейс. Это делает пустой интерфейс универсальным контейнером для значений любых типов.
type interface{} interface {}


🚩Внутреннее представление интерфейсов в Go

🟠Type
Типа конкретного значения
🟠Value
Самого значения

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

🚩Приведение пустого интерфейса к конкретному типу

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

Присваивание значений пустому интерфейсу
package main

import "fmt"

func main() {
var i interface{}
i = 42
fmt.Println(i) // 42

i = "hello"
fmt.Println(i) // hello
}


Утверждение типа (Type Assertion)
package main

import "fmt"

func main() {
var i interface{} = "hello"

// Утверждение типа с проверкой
s, ok := i.(string)
if ok {
fmt.Println("String:", s)
} else {
fmt.Println("Not a string")
}

// Утверждение типа без проверки
// Это вызовет панику, если тип не соответствует
s = i.(string)
fmt.Println("String:", s)
}


Использование 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")
printType(42)
printType(true)
printType(3.14)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Чем отличается конкурентность от параллелизма?

- Конкурентность (concurrency) — это способность обрабатывать несколько задач одновременно, переключаясь между ними (в т.ч. в одном потоке).
- Параллелизм (parallelism) — это одновременное выполнение задач на нескольких процессорах или ядрах.
Конкурентность — про структуру кода. Параллелизм — про реальное одновременное исполнение.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4