Golang | Вопросы собесов
4.35K subscribers
29 photos
712 links
Download Telegram
Какие недостатки есть у Go ?
Спросят с вероятностью 25%

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

1️⃣Отсутствие дженериков (до Go 1.18)
До введения дженериков, одним из основных недостатков языка было отсутствие поддержки дженериков, что приводило к тому, что разработчики должны были использовать интерфейсы и тип interface{} для создания функций и структур, работающих с различными типами данных. Это могло привести к ухудшению производительности и увеличению сложности кода.

2️⃣Управление памятью
Управление памятью осуществляется сборщиком мусора, который, хоть и очень эффективен, может быть непредсказуемым в плане времени выполнения. Это может быть критично для приложений с жёсткими требованиями к времени отклика, таких как высокопроизводительные игры или реалтаймовые системы.

3️⃣Интерфейсы в рантайме
Поскольку типы интерфейсов проверяются во время выполнения, а не во время компиляции, может возникнуть проблема с отладкой ошибок, связанных с типами данных. Кроме того, использование интерфейсов может привести к дополнительным накладным расходам на производительность.

4️⃣Неиммутабельные строки
Неизменяемы, что может привести к неэффективному использованию памяти и времени процессора при частой модификации строк, например, в циклах или больших объёмах текстовых данных.

5️⃣Зависимости и версионирование
До введения модулей в Go 1.11 управление зависимостями было сложным и могло привести к конфликтам и трудностям в сопровождении кода. Хотя модули значительно улучшили ситуацию, система версионирования и управления зависимостями в Go все еще может быть не такой гибкой, как в некоторых других языках.

7️⃣Ошибки в рантайме
Такие ошибки, как работа с нулевыми указателями, остаются довольно распространенными в Go, поскольку язык обладает автоматическим разыменованием указателей, что может привести к панике в рантайме.

7️⃣Отсутствие некоторых продвинутых функций
Стремится быть простым и эффективным языком, что иногда ведет к отсутствию поддержки некоторых более сложных или специализированных функций, доступных в других языках, таких как метапрограммирование, генерики (до Go 1.18) или тонкая настройка управления памятью.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Привет, ребят, хочу сделать так, чтобы для каждого вопроса было поясняющее видео в reels/shorts формате.

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

Если интересует такая подработка напишите мне @kivaiko
Как устроены строки в Go ?
Спросят с вероятностью 17%

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

Основные характеристики :

1⃣Неизменяемость: Как уже было упомянуто, строки не могут быть изменены после создания. Это свойство упрощает работу с строками в многопоточных средах и помогает избежать ошибок, связанных с изменением данных.

2⃣Unicode: Gо умолчанию кодируются с использованием UTF-8, что делает их подходящими для работы с множеством языков и символов. UTF-8 является переменной длиной кодировки, где один символ может занимать от 1 до 4 байт.

3⃣Эффективность: Благодаря использованию UTF-8 и неизменяемости строк, операции с текстами могут быть довольно эффективными по памяти и скорости выполнения, поскольку не требуют постоянного выделения новой памяти при изменении строк.
package main

import (
"fmt"
)

func main() {
// Создание строки
s := "Привет, мир!"

// Доступ к символу (на самом деле байту)
fmt.Println(s[0]) // вывод: 208 (числовое представление байта)

// Работа с рунами для доступа к символам Unicode
for _, runeValue := range s {
fmt.Printf("%#U ", runeValue)
}
// вывод: U+041F U+0440 U+0438 U+0432 U+0435 U+0442 U+002C U+0020 U+043C U+0438 U+0440 U+0021
}


Зачем нужны и как они используются

Строки используются практически в каждом приложении: от вывода сообщений пользователю до работы с сетевыми запросами и хранения данных. Всё это благодаря их гибкости, универсальности и поддержке Unicode, что делает Go востребованным для интернациональных приложений.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Как встроить стандартный профайлер в свое приложение ?
Спросят с вероятностью 8%

Встроенный профайлер предоставляет мощные инструменты для анализа производительности приложения. Он позволяет собирать данные о CPU, памяти, блокировках и других аспектах работы программы. Чтобы встроить стандартный профайлер в свое приложение, можно воспользоваться пакетом net/http/pprof.

Шаги для встраивания

1️⃣Импортировать необходимые пакеты:
Импортируйте пакеты net/http, net/http/pprof и, возможно, runtime/pprof для дополнительных возможностей профайлинга.

2️⃣Настроить маршруты для профайлера:
Настройте стандартные маршруты для профайлера, чтобы они были доступны через HTTP.

3️⃣Запустить HTTP сервер для профайлера:
Запустите HTTP сервер, который будет обслуживать запросы к профайлеру.

Пример:
package main

import (
"log"
"net/http"
_ "net/http/pprof"
)

func main() {
// Запуск основного HTTP сервера вашего приложения
go func() {
log.Println("Starting application server on :8080")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world!"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}()

// Запуск HTTP сервера для профайлера
log.Println("Starting pprof server on :6060")
log.Fatal(http.ListenAndServe(":6060", nil))
}


Доступ к профайлеру

После запуска приложения профайлер будет доступен по URL-адресу http://localhost:6060/debug/pprof/. Вот несколько полезных маршрутов:

/debug/pprof/: Основная страница профайлера с доступом к различным профилям.
/debug/pprof/profile: Сбор профиля CPU за 30 секунд (по умолчанию).
/debug/pprof/heap: Профиль использования памяти.
/debug/pprof/goroutine: Профиль использования горутин.
/debug/pprof/block: Профиль блокировок (взаимных блокировок).

Дополнительные возможности профайлинга

Можно вручную собирать и сохранять профили для последующего анализа. Например, профиль CPU:
package main

import (
"os"
"runtime/pprof"
"time"
)

func main() {
f, err := os.Create("cpu_profile.prof")
if err != nil {
log.Fatal("Could not create CPU profile: ", err)
}
defer f.Close()

if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("Could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()

// Выполнение вашей рабочей функции
time.Sleep(30 * time.Second) // Замените это на вашу рабочую функцию
}


Анализ профилей

Для этого можно использовать инструменты go tool pprof и pprof:

1️⃣Установка `pprof`:
      go install github.com/google/pprof@latest


2️⃣Анализ профиля:
      go tool pprof cpu_profile.prof


Или, если вы используете pprof:
      pprof -http=:8081 cpu_profile.prof


Это откроет интерфейс для анализа профиля в вашем браузере.

Встраивание стандартного профайлера приложение позволяет вам собирать и анализировать данные о производительности вашего кода, что помогает находить и устранять узкие места. Настройка профайлера с использованием пакета net/http/pprof и маршрутов профайлера через HTTP делает этот процесс простым и удобным.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Что известно о lock-free концепции ?
Спросят с вероятностью 8%

Концепция lock-free (без блокировок) относится к методам и структурам данных, которые позволяют параллельным потокам взаимодействовать без использования блокировок (мьютексов) для синхронизации. Цель — улучшение производительности и обеспечение высокой степени параллелизма, минимизируя задержки и исключая взаимные блокировки (deadlocks).

Основные характеристики

1️⃣Без блокировок:
Lock-free алгоритмы не используют мьютексы или другие механизмы блокировки для управления доступом к общим ресурсам.
Каждый поток выполняет свою работу без ожидания других потоков.

2️⃣Прогресс (Progress):
Гарантируется, что по крайней мере один поток завершит свою операцию за конечное количество шагов, даже если другие потоки могут быть приостановлены или прерваны.
Это предотвращает ситуации, когда все потоки зацикливаются и не могут продвинуться (livelock).

3️⃣Атомарные операции:
Lock-free алгоритмы используют атомарные операции, такие как Compare-and-Swap (CAS) или Load-Link/Store-Conditional (LL/SC), чтобы гарантировать согласованное обновление данных.

Преимущества

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

2️⃣Отсутствие взаимных блокировок:
Исключаются взаимные блокировки, поскольку потоки не ждут освобождения блокировок.

3️⃣Лучшее использование многопроцессорных систем:
Потоки могут эффективно использовать ресурсы многопроцессорных систем, выполняя параллельные операции без ожидания.

Недостатки

1️⃣Сложность реализации:
Реализация сложнее, чем использование блокировок, особенно для сложных структур данных.
Требуются глубокие знания низкоуровневых операций и архитектуры процессора.

2️⃣Проблемы с отладкой и тестированием:
Труднее отлаживать и тестировать из-за их параллельного и недетерминированного характера.

3️⃣Ограниченная поддержка в языках программирования:
Не все языки предоставляют необходимую поддержку для атомарных операций и lock-free конструкций.

Примеры lock-free алгоритмов

1️⃣Lock-free очереди (Queues):
Реализация очередей без блокировок, например, очередь Майкла-Скотта (Michael-Scott queue), использующая CAS для управления доступом к элементам очереди.

2️⃣Lock-free стеки (Stacks):
Реализация стеков без блокировок, например, стек Treiber, использующий CAS для добавления и удаления элементов.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Чем горутины отличаются от тредов ?
Спросят с вероятностью 17%

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

1⃣Модель управления

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

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

2⃣Затраты ресурсов

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

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

3⃣Масштабируемость

Горутины могут масштабироваться до большого количества параллельных задач благодаря меньшим требованиям к ресурсам и управлению со стороны runtime Go.

Треды ограничены в масштабируемости физическими ресурсами системы и более высокими затратами на управление.

4⃣Контекст переключения

Горутины имеют намного более эффективный контекст переключения, так как 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")
}


Этот пример демонстрирует запуск двух задач параллельно: главная функция main() запускает say("Hello") в горутине, позволяя ей выполняться одновременно с say("World").

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Как тестировать распределённую систему ?
Спросят с вероятностью 8%

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

Основные типы

1️⃣Модульное тестирование (Unit Testing):
Тестирование отдельных компонентов или функций системы в изоляции.
Использование моков (mocking) и заглушек (stubbing) для имитации поведения зависимостей.

2️⃣Интеграционное тестирование (Integration Testing):
Тестирование взаимодействия между различными компонентами системы.
Проверка корректности интеграции и обмена данными между модулями.

3️⃣Системное тестирование (System Testing):
Тестирование всей системы в целом.
Проверка выполнения функциональных и нефункциональных требований.

4️⃣Тестирование производительности (Performance Testing):
Оценка производительности системы под нагрузкой.
Использование нагрузочного тестирования (load testing) и стресс-тестирования (stress testing).

5️⃣Тестирование устойчивости (Resilience Testing):
Проверка способности системы восстанавливаться после сбоев.
Имитация отказов компонентов и проверка реакции системы.

6️⃣Тестирование безопасности (Security Testing):
Поиск уязвимостей и проверка защиты данных.
Использование инструментов для анализа безопасности и проведения пентестов (penetration testing).

Подходы и методы

1️⃣Использование автоматизированных тестов:
Разработка автоматизированных тестов для различных уровней тестирования (модульные, интеграционные, системные).
Использование фреймворков для тестирования, таких как JUnit, pytest, Go testing и другие.

2️⃣Контейнеризация и оркестрация:
Использование контейнеров (Docker) и систем оркестрации (Kubernetes) для создания изолированных и воспроизводимых тестовых окружений.
Имитация различных условий и сценариев работы распределённой системы.

3️⃣Использование моков и заглушек:
Создание моков и заглушек для имитации поведения зависимостей и компонентов, которые сложно или дорого тестировать напрямую.
Использование библиотек, таких как Mockito, GoMock, WireMock и других.

4️⃣Нагрузочное и стресс-тестирование:
Проведение нагрузочного тестирования для оценки производительности системы под реальными условиями.
Использование инструментов, таких как JMeter, Gatling, Locust и других.

5️⃣Тестирование устойчивости:
Проведение тестов на отказоустойчивость, имитируя сбои компонентов и сетевые проблемы.
Использование инструментов, таких как Chaos Monkey, Gremlin и других для проведения хаос-инжиниринга.

6️⃣Мониторинг и логирование:
Настройка систем мониторинга и логирования для сбора данных о состоянии и производительности системы.
Использование инструментов, таких как Prometheus, Grafana, ELK Stack и других.

Использование Docker и Kubernetes
# Пример файла конфигурации Kubernetes для тестового окружения
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 8080


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🔥Тесты для подготовки к собеседованию🔥
Выбери своё направление:

1. Frontend
2. Python
3. Java
4. Тестировщик QA
5. Data Science
6. DevOps
7. C#
8. С/C++
9. Golang
10. PHP
11. Kotlin
12. Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Как завершить много горутин ?
Спросят с вероятностью 17%

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

1⃣Использование каналов для управления горутинами

Каналы могут использоваться для отправки сигналов горутинам о том, что им следует завершить свою работу. Это один из наиболее часто используемых подходов, так как он прост в реализации и очень эффективен.
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() // ожидание завершения всех горутин
}


2⃣Использование пакета context

Предоставляет функциональность для передачи контекста внутрь вашей программы, включая сигналы о необходимости завершения работы. Это может быть полезно, если у вас есть иерархия горутин с общим временем выполнения или дополнительными ограничениями.
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() // убедиться, что все ресурсы освобождены
}


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Как проверить тип переменной в среде выполнения ?
Спросят с вероятностью 8%

Существует несколько способов проверки типа переменной в среде выполнения. Вот методы, которые помогут вам определить тип переменной во время выполнения программы.

1️⃣Использование reflect пакета

Пакет reflect предоставляет мощные инструменты для работы с типами переменных во время выполнения.
package main

import (
"fmt"
"reflect"
)

func main() {
var x interface{} = 42
fmt.Println("Type:", reflect.TypeOf(x))
fmt.Println("Value:", reflect.ValueOf(x))
}


Здесь:
reflect.TypeOf(x) возвращает тип переменной x.
reflect.ValueOf(x) возвращает значение переменной x.

2️⃣Использование типа-утверждения (type assertion)

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

import "fmt"

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

if str, ok := x.(string); ok {
fmt.Println("x is a string:", str)
} else {
fmt.Println("x is not a string")
}
}


Здесь:
x.(string) пытается утверждать, что x имеет тип string.
Если утверждение верно, переменная str будет содержать значение типа string, а ok будет равно true.
Если утверждение неверно, ok будет равно false.

3️⃣Использование оператора switch по типам

Go поддерживает использование оператора switch для проверки типа переменной.
package main

import "fmt"

func main() {
var x interface{} = 3.14

switch v := x.(type) {
case int:
fmt.Println("x is an int:", v)
case float64:
fmt.Println("x is a float64:", v)
case string:
fmt.Println("x is a string:", v)
default:
fmt.Println("x is of an unknown type")
}
}


В этом примере:
Оператор switch по типу переменной x.
В каждой ветке проверяется, является ли x определенным типом (int, float64, string).
Переменная v получает значение типа, если проверка успешна.

Проверка типа переменной может быть выполнена с использованием пакета reflect, типа-утверждения (type assertion) и оператора switch по типам. Каждый метод имеет свои особенности и может быть полезен в различных сценариях.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Что делает команда килл в linux ?
Спросят с вероятностью 17%

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

Основной синтаксис команды kill выглядит так:
kill [options] <pid>

Здесь <pid> обозначает идентификатор процесса (process ID), которому нужно отправить сигнал. По умолчанию, если сигнал не указан, команда kill отправляет сигнал SIGTERM (15), который просит процесс корректно завершиться.

Существует несколько сигналов, которые часто используются с командой kill:
SIGTERM (15): Корректное завершение процесса. Позволяет процессу корректно закрыть открытые файлы, освободить ресурсы и т. д.
SIGKILL (9): Немедленное принудительное завершение процесса. ОС немедленно завершает процесс, не давая ему возможности освободить ресурсы или корректно завершить работу.
SIGHUP (1): Обычно используется для перезапуска процессов, например, демонов или сервисов.
SIGINT (2): Сигнал прерывания, аналогичный нажатию Ctrl+C в терминале.

Для отправки сигнала SIGTERM процессу с PID 1234:
kill 1234


Для принудительного завершения процесса (если он не реагирует на SIGTERM):
kill -9 1234


Для отправки сигнала SIGHUP для перезагрузки конфигурации сервиса:
kill -1 1234


Использование с pgrep и pkill

Могут использоваться для упрощения поиска и завершения процессов по имени или другим критериям:
pgrep возвращает список PID'ов по заданным критериям.
pkill отправляет сигналы процессам, удовлетворяющим заданным критериям.

Например, если необходимо завершить все процессы с именем nginx:
pkill nginx


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
В чем отличия http 1.1 и http 2 ?
Спросят с вероятностью 17%

HTTP/1.1 и HTTP/2 — это версии протокола HTTP, каждая из которых имеет свои особенности и улучшения по сравнению с предыдущими версиями. Важные различия между этими версиями включают следующие аспекты:

1⃣Мультиплексирование

HTTP/1.1:
Поддерживает одновременное открытие нескольких TCP соединений (обычно 6-8), что позволяет загружать несколько ресурсов параллельно. Однако каждое соединение может обрабатывать только один запрос за раз, что приводит к задержкам из-за блокировки очереди (head-of-line blocking).

HTTP/2:
Вводит мультиплексирование, позволяющее отправлять множество запросов и ответов асинхронно через одно единственное TCP соединение. Это значительно уменьшает задержки и улучшает производительность при загрузке страниц с большим количеством ресурсов.

2⃣Бинарный протокол

HTTP/1.1:
Является текстовым протоколом, что означает, что запросы и ответы форматируются в виде читаемого текста.

HTTP/2:
Бинарный протокол, который делает передачу данных более эффективной и менее подверженной ошибкам в синтаксическом анализе. Бинарный формат упрощает реализацию парсеров и уменьшает размер передаваемых данных.

3⃣Сжатие заголовков

HTTP/1.1:
Заголовки передаются без сжатия, что может привести к значительному объему передаваемых данных, особенно если одни и те же заголовки отправляются повторно с каждым запросом.

HTTP/2:
Использует механизм сжатия заголовков HPACK, который уменьшает избыточность заголовков, сжимая их перед отправкой. Это особенно эффективно для повторяющихся запросов к одним и тем же серверам.

4⃣Приоритизация запросов

HTTP/1.1:
Не поддерживает приоритизацию запросов, из-за чего браузеры должны использовать эвристики для управления приоритетами ресурсов.

HTTP/2:
Поддерживает явную приоритизацию запросов, позволяя клиенту указывать приоритет обработки ресурсов, что делает загрузку страниц более эффективной.

5⃣Server Push

HTTP/1.1:
Не имеет возможности "server push", при котором сервер инициативно отправляет ресурсы клиенту, даже если они не были явно запрошены.

HTTP/2:
Включает функцию "server push", позволяя серверам отправлять ресурсы клиенту в предвидении будущих запросов, что может ускорить загрузку веб-страниц.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
В чем разница между WHERE и HAVING ?
Спросят с вероятностью 17%

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

Ключевое слово WHERE

Используется для фильтрации строк перед тем, как будут выполнены какие-либо группировки (GROUP BY) или агрегации (SUM, COUNT и т.д.).
Цель: Отфильтровать записи на основе конкретных условий, которые должны быть выполнены отдельными строками в исходной таблице.
    SELECT employee_id, name
FROM employees
WHERE department = 'Sales';

В этом примере запрос возвращает всех сотрудников отдела продаж. Фильтрация происходит до того, как данные попадут в результаты запроса.

Ключевое слово HAVING


Используется для фильтрации групп после того, как они были сформированы оператором GROUP BY.
Цель: Отфильтровать группы на основе агрегатных функций или условий, которые не могут быть применены до выполнения группировки.
    SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING COUNT(*) > 10;

В этом примере запрос считает количество сотрудников в каждом отделе и возвращает только те отделы, где число сотрудников превышает 10. Здесь фильтрация происходит после группировки результатов.

Важные различия

1⃣Момент применения: WHERE фильтрует строки до любой агрегации, в то время как HAVING применяется после группировки данных.
2⃣Использование с агрегатными функциями: WHERE не может использоваться с агрегатными функциями, потому что агрегация ещё не произошла. HAVING специально предназначен для работы с агрегатными функциями.
3⃣Логический порядок выполнения: В процессе выполнения SQL-запроса WHERE выполняется до GROUP BY, а HAVING — после.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
В чем разница между императивным и декларативным ?
Спросят с вероятностью 17%

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

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

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

Особенности:
Подробное описание процесса: Программист должен указывать все шаги, которые необходимо выполнить для достижения результата.
Примеры языков: 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, могут поддерживать оба стиля в зависимости от использования.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Что такое эвакуация, и в каком случае она будет происходить ?
Спросят с вероятностью 17%

Термин "эвакуация" в контексте Go относится к процессу, связанному с работой сборщика мусора (garbage collector, GC). Это не тот же самый смысл, как у эвакуации в повседневной жизни или в чрезвычайных ситуациях, но понятие может быть аналогичным в том смысле, что объекты "эвакуируются" из одного места памяти в другое.

Контекст и Причины Эвакуации

Cборка мусора — это важная часть управления памятью, которая автоматически освобождает память, выделенную для объектов, которые больше не используются программой. Go использует сборщик мусора с три-цветной маркировкой и алгоритмом "stop-the-world".

Эвакуация в этом контексте происходит во время фазы маркировки и сжатия (compaction) сборщика мусора. Основная цель эвакуации — переместить объекты, доступные для выживания (т.е. объекты, на которые ещё ссылаются в программе), в другую область памяти, обычно в более свежие или новые регионы памяти, чтобы уменьшить фрагментацию и оптимизировать производительность доступа к памяти.

Процесс Эвакуации

1⃣Определение "живых" объектов: Сборщик мусора идентифицирует объекты, которые все еще доступны (доступны означает, что существует по крайней мере одна ссылка на объект из вашей программы).

2⃣Перемещение объектов: Эти "живые" объекты перемещаются в новое место в памяти, которое часто организуется более эффективно.

3⃣Обновление ссылок: Все ссылки на эвакуированный объект обновляются, чтобы указывать на новое местоположение объекта в памяти.

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

Когда система обнаруживает, что объем памяти, используемый программой, достигает определенного порога.
Когда количество новых аллокаций достигает определенного лимита, что требует очистки для выделения дополнительной памяти.
Может быть запущен явно программистом с помощью вызова runtime.GC().

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Какие отличия между SSL и TLS ?
Спросят с вероятностью 8%

SSL (Secure Sockets Layer) и TLS (Transport Layer Security) — это криптографические протоколы, используемые для защиты передачи данных. Является преемником SSL и включает улучшенные методы шифрования и безопасности. Рассмотрим основные отличия между SSL и TLS.

Основные отличия

1️⃣История и развитие

SSL:
SSL 1.0: Никогда не был выпущен публично из-за серьезных уязвимостей.
SSL 2.0: Выпущен в 1995 году, но быстро признан небезопасным.
SSL 3.0: Выпущен в 1996 году и стал гораздо более популярным, но позже также обнаружил уязвимости и был устаревшим в пользу TLS.

TLS:
TLS 1.0: Выпущен в 1999 году как улучшение SSL 3.0.
TLS 1.1: Выпущен в 2006 году с дополнительными улучшениями безопасности.
TLS 1.2: Выпущен в 2008 году с улучшенными алгоритмами шифрования и безопасностью.
TLS 1.3: Выпущен в 2018 году с значительными улучшениями производительности и безопасности.

2️⃣Алгоритмы шифрования и безопасности

SSL:
Использует менее безопасные и устаревшие алгоритмы шифрования.
Уязвим для различных атак, таких как POODLE (Padding Oracle On Downgraded Legacy Encryption).

TLS:
Включает более современные и безопасные криптографические алгоритмы.
TLS 1.2 и TLS 1.3 используют алгоритмы шифрования, которые более устойчивы к современным атакам.

3️⃣Процесс Handshake

SSL:
Более сложный и уязвимый к определенным атакам процесс Handshake.
Использует фиксированное число шагов для установки защищенного соединения.

TLS:
Процесс Handshake был оптимизирован и упрощен, особенно в TLS 1.3, что уменьшает задержки и повышает безопасность.
В TLS 1.3 Handshake происходит быстрее за счет уменьшения количества раунд-трипов.

4️⃣Поддержка протоколов

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

TLS:
Отключил поддержку устаревших алгоритмов и протоколов.
TLS 1.2 и TLS 1.3 включают поддержку более безопасных и эффективных алгоритмов.

5️⃣Механизмы шифрования

SSL:
В нем использовались CBC (Cipher Block Chaining) режимы, которые подвержены атакам типа BEAST (Browser Exploit Against SSL/TLS).

TLS:
Включает поддержку AEAD (Authenticated Encryption with Associated Data) алгоритмов, таких как AES-GCM (Galois/Counter Mode), которые обеспечивают улучшенную защиту от атак на целостность данных.

Преимущества

1️⃣Безопасность:
Включает улучшенные методы шифрования и механизмы защиты от известных атак, что делает его более безопасным по сравнению с SSL.

2️⃣Производительность:
Оптимизировал процесс Handshake, уменьшив количество раунд-трипов, что улучшает производительность соединений.

3️⃣Совместимость:
Является текущим стандартом для защиты сетевых соединений, поддерживается всеми современными браузерами и серверами.

Пример:
package main

import (
"fmt"
"log"
"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, TLS!")
}

func main() {
http.HandleFunc("/", helloHandler)

// Путь к сертификату и ключу
certFile := "path/to/cert.pem"
keyFile := "path/to/key.pem"

// Запуск HTTPS сервера
log.Println("Starting server on https://localhost:8443")
err := http.ListenAndServeTLS(":8443", certFile, keyFile, nil)
if err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}


TLS (Transport Layer Security) является более безопасным и эффективным преемником SSL (Secure Sockets Layer). Включает улучшенные методы шифрования, оптимизированный процесс Handshake и поддержку современных алгоритмов безопасности, что делает его предпочтительным выбором для защиты передачи данных в сетях.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🤔 Какая функция в Go используется для параллельного выполнения процедур?
Anonymous Quiz
72%
go
1%
func
4%
chan
23%
goroutine
Насколько увеличивается слайс при append ?
Спросят с вероятностью 17%

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

Алгоритм увеличения размера слайса

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

Если текущий размер слайса меньше 1024 элементов, то новый размер будет примерно в два раза больше текущего.
Если текущий размер слайса 1024 элемента или больше, то новый размер будет увеличен примерно на 25%.

Эти числа не являются строго фиксированными и могут немного варьироваться в зависимости от реализации и версии компилятора.

Давайте рассмотрим пример, демонстрирующий использование append() и увеличение размера слайса:
package main

import "fmt"

func main() {
slice := make([]int, 0, 4) // начальный размер 0, вместимость 4
fmt.Println("Начальная длина:", len(slice), "Начальная вместимость:", cap(slice))

for i := 0; i < 10; i++ {
slice = append(slice, i)
fmt.Println("Добавлен элемент:", i, "Длина:", len(slice), "Вместимость:", cap(slice))
}
}

Выходные данные покажут, как вместимость слайса увеличивается при добавлении новых элементов. Заметьте, что после того как вместимость исчерпана, она увеличивается (приблизительно удваивается до определенного размера, а затем растет на 25%).

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🤔 Какой тип в Go предназначен для синхронизации доступа к данным между горутинами?
Anonymous Quiz
3%
map
7%
interface
88%
mutex
3%
slice
Что такое TLS ?
Спросят с вероятностью 8%

TLS (Transport Layer Security) — это криптографический протокол, предназначенный для обеспечения безопасности передачи данных. Является преемником протокола SSL (Secure Sockets Layer) и включает улучшенные методы шифрования и аутентификации, что делает его более безопасным и эффективным.

Основные цели

1️⃣Шифрование данных:
Обеспечивает конфиденциальность данных, передаваемых между клиентом и сервером, защищая их от перехвата и чтения третьими лицами.

2️⃣Аутентификация:
Гарантирует, что сервер и клиент являются теми, за кого себя выдают, с помощью цифровых сертификатов.

3️⃣Целостность данных:
Обеспечивает, что данные не были изменены или подменены в процессе передачи, с помощью контрольных сумм и цифровых подписей.

Как он работает

Процесс установки TLS-соединения включает несколько этапов, известных как TLS Handshake:

1️⃣ClientHello:
Клиент инициирует соединение, отправляя серверу сообщение ClientHello, которое включает поддерживаемые версии протокола TLS, криптографические алгоритмы и сессионные параметры.

2️⃣ServerHello:
Сервер отвечает сообщением ServerHello, выбирая наилучшие параметры из предложенных клиентом, а также отправляет свой цифровой сертификат.

3️⃣Аутентификация и обмен ключами:
Сертификат сервера проверяется клиентом с помощью доверенного центра сертификации (CA).
Клиент генерирует сессионный ключ для симметричного шифрования, шифрует его с использованием публичного ключа сервера и отправляет серверу.

4️⃣Завершение Handshake:
Сервер расшифровывает сессионный ключ и подтверждает установку безопасного соединения.
Оба участника обмена завершают Handshake, отправляя сообщения Finished.

5️⃣Передача данных:
После успешного завершения Handshake все данные передаются с использованием симметричного шифрования.

Преимущества

1️⃣Безопасность:
Обеспечивает высокую степень защиты данных благодаря использованию современных алгоритмов шифрования и аутентификации.

2️⃣Совместимость:
Широко поддерживается в современных веб-браузерах, серверах и других интернет-устройствах.

3️⃣Прозрачность:
Пользователи могут видеть, что их соединение защищено, через индикаторы безопасности в браузере (например, значок замка).

4️⃣Производительность:
TLS 1.3, последняя версия протокола, включает оптимизации для уменьшения накладных расходов на установку соединения и улучшения общей производительности.

Недостатки

1️⃣Производительность:
Хотя он улучшил производительность, шифрование и дешифрование данных все равно требуют дополнительных вычислительных ресурсов.

2️⃣Сложность настройки:
Настройка и управление сертификатами могут быть сложными и требуют знаний в области сетевой безопасности.

3️⃣Совместимость с устаревшими системами:
Некоторые старые системы и устройства могут не поддерживать современные версии TLS, что может вызвать проблемы совместимости.

Версии

1️⃣TLS 1.0:
Первая версия, введенная в 1999 году, заменившая SSL 3.0.

2️⃣TLS 1.1:
Введена в 2006 году, с улучшениями защиты от некоторых атак.

3️⃣TLS 1.2:
Введена в 2008 году, с улучшениями в алгоритмах шифрования и безопасности.

TLS (Transport Layer Security) — это протокол, обеспечивающий безопасность передачи данных в Интернете. Он использует шифрование, аутентификацию и механизмы проверки целостности данных для защиты коммуникаций между клиентами и серверами. Является эволюцией протокола SSL и включает множество улучшений для повышения безопасности и производительности.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
Какие основные структуры данных есть в Go ?
Спросят с вероятностью 17%

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

1⃣Слайсы (Slices)
Это динамические массивы в Go, которые могут автоматически увеличиваться и уменьшаться. Слайсы построены на базе массивов, но обеспечивают более гибкое управление коллекциями элементов. Они поддерживают операции добавления, удаления и доступа к элементам.

2⃣Массивы (Arrays)
Это коллекции фиксированного размера, которые хранят элементы одного типа. Размер массива должен быть известен на этапе компиляции, что делает массивы менее гибкими по сравнению со слайсами, но они полезны для задач, где известно точное количество элементов, что может улучшить производительность.

3⃣Мапы (Maps)

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

4⃣Структуры (Structs)

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

5⃣Каналы (Channels)
Это средства передачи данных между горутинами, которые поддерживают параллельное и асинхронное выполнение. Каналы предоставляют способ синхронизации и коммуникации между горутинами без использования традиционных примитивов синхронизации, таких как мьютексы и условные переменные.

6⃣Интерфейсы (Interfaces)
Интерфейсы в Go представляют собой наборы методов, которые определяют поведение. Типы могут реализовывать интерфейсы, предоставляя реализации методов. Интерфейсы широко используются для создания гибких и модульных программ, поддерживающих различные типы поведения.
// Структура данных
type Person struct {
Name string
Age int
}

// Слайс
people := []Person{
{"Alice", 30},
{"Bob", 25},
}

// Мапа
emails := map[string]string{
"Alice": "alice@example.com",
"Bob": "bob@example.com",
}

// Использование мапы и структуры
for _, person := range people {
email := emails[person.Name]
fmt.Printf("Name: %s, Age: %d, Email: %s\n", person.Name, person.Age, email)
}


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых