Golang | Вопросы собесов
4.34K subscribers
28 photos
700 links
Download Telegram
🤔 Почему в TDD тесты пишутся прежде кода?

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

🚩Причины

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

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

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

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

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

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

🚩Пример процесса TDD

1⃣Написание теста
package main

import "testing"

func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, but got %d", result)
}
}


2⃣Запуск теста
На этом этапе тест не пройдет, так как функция Add еще не реализована.

3⃣Реализация кода
package main

func Add(a, b int) int {
return a + b
}


4⃣Запуск теста снова
Теперь тест пройдет, так как функция Add реализована корректно.

5⃣Рефакторинг
При необходимости выполняется рефакторинг кода для улучшения его структуры, при этом тесты продолжают проходить, что гарантирует сохранение функциональности.

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

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Если у кода плохая организация, как это поймёшь?

🟠Высокое сцепление (Tight Coupling)
Модули сильно зависят друг от друга. Изменения в одном модуле требуют изменений в других.
🟠Низкая связанность (Low Cohesion)
Модуль выполняет несколько различных задач. Трудно поддерживать и изменять код.
🟠Большие функции и классы (God Objects)
Содержат слишком много логики. Длинные и сложные для понимания.
🟠Дублирование кода
Один и тот же код повторяется в нескольких местах.
🟠Отсутствие тестов
Код не покрыт тестами. Трудно проверять корректность.
🟠Неразборчивые имена переменных и функций
Имена не отражают назначение. Трудно понять код.
🟠Сложная и запутанная логика
Логика кода трудна для понимания. Много условных операторов.
🟠Отсутствие или недостаток документации
Нет комментариев и документации.

🚩Примеры

Высокое сцепление:
type UserService struct {
userRepository UserRepository
emailService EmailService
}


Низкая связанность:
type UserService struct {
db Database
businessLogic BusinessLogic
notificationService NotificationService
}


Большие функции и классы:
func ProcessData() {
// Много строк кода
}


Дублирование кода:
func CalculateSum(a, b int) int {
return a + b
}

func AddNumbers(x, y int) int {
return x + y
}


Неразборчивые имена:
func DoSomething(x int) int {
return x * 2
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница TCP и UDP?

TCP (Transmission Control Protocol) — это протокол, который обеспечивает надёжную передачу данных, гарантируя, что все пакеты будут доставлены в правильном порядке и без потерь. TCP устанавливает соединение между клиентом и сервером перед передачей данных, проверяет целостность пакетов и управляет повторной передачей потерянных данных. UDP (User Datagram Protocol) не гарантирует доставку пакетов, не обеспечивает контроль за порядком их получения и не требует установления соединения, что делает его более быстрым, но менее надёжным. UDP предпочтителен для приложений, где скорость важнее надёжности, например, для видеостриминга или онлайн-игр.

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

Включает различные методы, позволяющие процессам обмениваться данными и сигналами.

🚩Методы

🟠Каналы (Pipes)
Однонаправленные каналы между родительским и дочерним процессами. Обмен данными между произвольными процессами.
🟠Очереди сообщений (Message Queues)
Обмен сообщениями через общую очередь. System V, POSIX message queues.
🟠Общая память (Shared Memory)
Совместное использование памяти для обмена данными. POSIX (shm_open), System V shared memory.
🟠Семафоры (Semaphores)
Синхронизация доступа к ресурсам. POSIX (sem_open), System V semaphores.
🟠Сокеты (Sockets)
Обмен данными через сетевые соединения. Unix domain sockets, TCP/UDP.
🟠Сигналы (Signals)
Отправка и обработка событий. kill(), signal() в Unix.
🟠Файлы и базы данных
Обмен данными через файлы или базы данных. Файлы, SQLite.

🚩Примеры

Каналы (Pipes)
package main

import (
"fmt"
"os"
"syscall"
)

func main() {
fd := make([]int, 2)
if err := syscall.Pipe(fd); err != nil {
fmt.Println("Ошибка создания канала:", err)
os.Exit(1)
}

pid, err := syscall.ForkExec("", nil, &syscall.ProcAttr{Files: []uintptr{uintptr(fd[0]), uintptr(fd[1]), uintptr(os.Stderr.Fd())}})
if err != nil {
fmt.Println("Ошибка создания процесса:", err)
os.Exit(1)
}

if pid == 0 { // Child process
syscall.Close(fd[0])
_, err := syscall.Write(fd[1], []byte("Hello"))
if err != nil {
fmt.Println("Ошибка записи в канал:", err)
}
syscall.Close(fd[1])
} else { // Parent process
buffer := make([]byte, 5)
syscall.Close(fd[1])
_, err := syscall.Read(fd[0], buffer)
if err != nil {
fmt.Println("Ошибка чтения из канала:", err)
}
syscall.Close(fd[0])
fmt.Printf("Получено: %s\n", buffer)
}
}


Общая память (Shared Memory)
package main

import (
"fmt"
"os"
"syscall"
)

func main() {
// Создание сегмента разделяемой памяти
segmentID, _, err := syscall.Syscall(syscall.SYS_SHMGET, uintptr(syscall.IPC_PRIVATE), uintptr(4), uintptr(syscall.S_IRUSR|syscall.S_IWUSR))
if err != 0 {
fmt.Println("Ошибка создания сегмента разделяемой памяти:", err)
os.Exit(1)
}

// Присоединение к сегменту
sharedMemory, _, err := syscall.Syscall(syscall.SYS_SHMAT, segmentID, 0, 0)
if err != 0 {
fmt.Println("Ошибка присоединения к разделяемой пам


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

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

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

🟠Семафоры (Semaphores)
Позволяют синхронизировать доступ к ограниченным ресурсам, в данном случае — к оперативной памяти. Семафор может быть инициализирован значением 1, указывая, что доступен только один слот памяти. Когда один из процессов захватывает семафор, другой процесс будет заблокирован, пока семафор не освободится.
package main

import (
"fmt"
"sync"
"time"
)

var mutex sync.Mutex

func process(id int) {
mutex.Lock() // Захват семафора
fmt.Printf("Process %d: Accessing memory\n", id)
time.Sleep(2 * time.Second) // Имитируем доступ к памяти
fmt.Printf("Process %d: Releasing memory\n", id)
mutex.Unlock() // Освобождение семафора
}

func main() {
var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
process(1)
}()

go func() {
defer wg.Done()
process(2)
}()

wg.Wait()
}


🟠Мьютексы (Mutexes)
Обеспечивают эксклюзивный доступ к критической секции. Один процесс захватывает мьютекс перед доступом к памяти, другой процесс будет заблокирован до освобождения мьютекса.
package main

import (
"fmt"
"sync"
"time"
)

var mutex sync.Mutex

func process(id int) {
mutex.Lock() // Захват мьютекса
fmt.Printf("Process %d: Accessing memory\n", id)
time.Sleep(2 * time.Second) // Имитируем доступ к памяти
fmt.Printf("Process %d: Releasing memory\n", id)
mutex.Unlock() // Освобождение мьютекса
}

func main() {
var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
process(1)
}()

go func() {
defer wg.Done()
process(2)
}()

wg.Wait()
}


🟠Очередь сообщений (Message Queues)
Может быть использована для координации доступа к памяти, позволяя процессам обмениваться сообщениями о доступности ресурса.
package main

import (
"fmt"
"time"
)

type Message struct {
processID int
}

func accessMemory(processID int) {
fmt.Printf("Process %d: Accessing memory\n", processID)
time.Sleep(2 * time.Second) // Имитируем доступ к памяти
fmt.Printf("Process %d: Releasing memory\n", processID)
}

func main() {
msgChannel := make(chan Message)

go func() {
// "Дочерний" процесс
message := Message{processID: 1}
msgChannel <- message
msg := <-msgChannel
accessMemory(msg.processID)
}()

go func() {
// "Родительский" процесс
message := Message{processID: 2}
msgChannel <- message
msg := <-msgChannel
accessMemory(msg.processID)
}()

msg := <-msgChannel // Получаем сообщение от дочернего процесса
msgChannel <- msg // Отправляем обратно для завершения доступа
msg = <-msgChannel // Получаем сообщение от родительского процесса
msgChannel <- msg // Отправляем обратно для завершения доступа

time.Sleep(3 * time.Second) // Подождем завершения горутин
}


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

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

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

Объектно-ориентированное программирование (ООП) в Go и C# реализовано с использованием различных подходов и парадигм, отражающих философию и дизайн каждого языка.

🚩Go

🟠Классы и объекты
Используются структуры (struct).
type Person struct {
Name string
Age int
}

func (p *Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}


🟠Наследование
Используется композиция вместо наследования.
type Employee struct {
Person
Position string
}


🟠Инкапсуляция
Модификаторы доступа на уровне пакета (экспортируемые и неэкспортируемые поля).
type Person struct {
name string // неэкспортируемое поле
Age int // экспортируемое поле
}


🟠Полиморфизм
Реализуется через интерфейсы.
type Greeter interface {
Greet()
}

type Person struct {
Name string
}

func (p *Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}


🚩C#

🟠Классы и объекты
Используются классы.
public class Person {
public string Name { get; set; }
public int Age { get; set; }

public void Greet() {
Console.WriteLine($"Hello, my name is {Name}");
}
}


🟠Наследование
Поддерживается классическое наследование.
public class Employee : Person {
public string Position { get; set; }
}


🟠Инкапсуляция
Модификаторы доступа (public, private, protected).
public class Person {
private string name;
public int Age { get; set; }
}


🟠Полиморфизм
Через виртуальные методы и интерфейсы.
public class Person {
public virtual void Greet() {
Console.WriteLine("Hello!");
}
}

public class Employee : Person {
public override void Greet() {
Console.WriteLine("Hello, I am an employee!");
}
}


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

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

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

Объектно-ориентированная модель отличается от традиционных ООП-языков, таких как C# или Java. Нет классов и наследования в привычном понимании. Вместо этого используются структуры (structs) и интерфейсы для реализации основных принципов ООП: инкапсуляции, композиции и полиморфизма.

🚩Концепции

🟠Структуры (Structs)
Служат аналогом классов. Они позволяют объединять данные в логически связанные группы.
type Person struct {
Name string
Age int
}


🟠Методы
Могут быть определены для структур, что позволяет связывать функции с типами.
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}


🟠Инкапсуляция
Достигается через модификаторы доступа на уровне пакета. Поля и методы, начинающиеся с заглавной буквы, экспортируемые (public), остальные — нет (private).
type Person struct {
name string // неэкспортируемое поле
Age int // экспортируемое поле
}


🟠Композиция
Go не поддерживает классическое наследование. Вместо этого используется композиция для включения функциональности одного типа в другой.
type Employee struct {
Person
Position string
}


🟠Интерфейсы
Используются для определения поведения. Типы могут реализовывать интерфейсы неявно, просто предоставляя методы, указанные в интерфейсе.
type Greeter interface {
Greet()
}

func SayHello(g Greeter) {
g.Greet()
}

type Person struct {
Name string
}

func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}

func main() {
p := Person{Name: "John"}
SayHello(p)
}


🟠Полиморфизм
Достигается через интерфейсы. Любой тип, который реализует интерфейс, может быть использован вместо него.
type Greeter interface {
Greet()
}

func SayHello(g Greeter) {
g.Greet()
}

type Robot struct {
ID string
}

func (r Robot) Greet() {
fmt.Printf("Greetings, I am robot %s\n", r.ID)
}

func main() {
p := Person{Name: "John"}
r := Robot{ID: "XJ-9"}

SayHello(p)
SayHello(r)
}


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

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

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

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

🚩Различия

🟠Отсутствие иерархий классов
Наследование: В языках с традиционным ООП, таких как C# или Java, наследование создает иерархии классов, где подклассы наследуют свойства и методы суперклассов. Подклассы могут переопределять методы суперклассов (полиморфизм) и добавлять новые методы.
Встраивание: Go не поддерживает иерархии классов. Встраивание позволяет включать типы в другие типы без создания строгих иерархий. Встраивание не создает отношений "is-a" (как в наследовании), а реализует отношения "has-a". Здесь Employee наследует свойства и методы Person.
public class Person {
public string Name { get; set; }
public void Greet() {
Console.WriteLine($"Hello, my name is {Name}");
}
}

public class Employee : Person {
public string Position { get; set; }
}


Здесь Employee включает Person как встроенное поле, но не наследует его в традиционном смысле.
type Person struct {
Name string
}

func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}

type Employee struct {
Person
Position string
}

func main() {
e := Employee{Person: Person{Name: "John"}, Position: "Developer"}
e.Greet() // Вызов метода Greet из встроенного типа Person
fmt.Println("Position:", e.Position)
}


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

🟠Отсутствие переопределения методов
Наследование: Подклассы могут переопределять методы суперклассов, что является основой для полиморфизма.
Встраивание: методы встроенных типов могут быть "переопределены" путем объявления методов с теми же именами в внешнем типе, но это не является полноценным переопределением как в наследовании. Вызов методов не поддерживает виртуальные методы как в C# или Java.
type Person struct {
Name string
}

func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}

type Employee struct {
Person
}

func (e Employee) Greet() {
fmt.Println("Hello, I am an employee")
}

func main() {
e := Employee{Person: Person{Name: "John"}}
e.Greet() // Вызов метода Greet из Employee
e.Person.Greet() // Вызов метода Greet из встроенного типа Person
}


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

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

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

Существует несколько способов написания обобщенного кода. До выхода Go 1.18, который представил встроенную поддержку обобщений (generics), разработчики использовали различные техники для достижения аналогичного эффекта.

🚩Подходы

🟠Параметризованные типы (Generics) в Go 1.18+
С выходом Go 1.18 язык получил встроенную поддержку обобщений, позволяющую создавать обобщенные функции и типы. Это позволяет писать код, который работает с любыми типами, определенными параметрами.
package main

import "fmt"

func Map[T any](arr []T, f func(T) T) []T {
result := make([]T, len(arr))
for i, v := range arr {
result[i] = f(v)
}
return result
}

func main() {
nums := []int{1, 2, 3, 4}
doubled := Map(nums, func(n int) int { return n * 2 })
fmt.Println(doubled) // [2, 4, 6, 8]
}


Пример обобщенного типа:
package main

import "fmt"

type Pair[T any, U any] struct {
First T
Second U
}

func main() {
p := Pair[int, string]{First: 1, Second: "one"}
fmt.Println(p) // {1 one}
}


🟠Использование интерфейсов
До появления обобщений, интерфейсы были основным способом достижения обобщенности. Интерфейсы позволяют определять функции, которые могут работать с любыми типами, реализующими определенные методы.
package main

import "fmt"

type Stringer interface {
String() string
}

func Print(s Stringer) {
fmt.Println(s.String())
}

type Person struct {
Name string
}

func (p Person) String() string {
return p.Name
}

func main() {
p := Person{Name: "Alice"}
Print(p) // Alice
}


🟠Пустые интерфейсы (`interface{}`)
Пустой интерфейс (interface{}) может содержать значения любого типа. Это позволяет создавать функции, принимающие значения любых типов, но требует явного приведения типов.
package main

import "fmt"

func Print(v interface{}) {
fmt.Println(v)
}

func main() {
Print(42)
Print("Hello")
Print([]int{1, 2, 3})
}


🟠Рефлексия (Reflection)
Рефлексия позволяет программам исследовать и изменять структуру и поведение объектов во время выполнения. Это мощный, но сложный способ написания обобщенного кода.
package main

import (
"fmt"
"reflect"
)

func Print(v interface{}) {
rv := reflect.ValueOf(v)
fmt.Println("Type:", rv.Type(), "Value:", rv)
}

func main() {
Print(42) // Type: int Value: 42
Print("Hello") // Type: string Value: Hello
Print([]int{1, 2, 3}) // Type: []int Value: [1 2 3]
}


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

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

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

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

🚩Виды контекстов

🟠context.Background()
context.Background() возвращает пустой контекст, который обычно используется как корневой контекст в программах. Он не имеет отмены или дедлайна и не содержит значений.
ctx := context.Background()


🟠context.TODO()
context.TODO() также возвращает пустой контекст и используется в случаях, когда еще не ясно, какой контекст следует использовать. Это временный заполнитель, который можно заменить на другой контекст позже.
ctx := context.TODO()


🟠context.WithCancel(parent Context)
context.WithCancel(parent Context) создает дочерний контекст, который может быть отменен явно вызовом функции отмены (cancel). Это полезно для контроля выполнения горутин.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func() {
// Работа горутины
<-ctx.Done() // Ожидание отмены контекста
fmt.Println("Goroutine canceled")
}()

// Отмена контекста после некоторого времени
time.Sleep(2 * time.Second)
cancel()


🟠context.WithDeadline(parent Context, d time.Time)
context.WithDeadline(parent Context, d time.Time) создает дочерний контекст, который будет автоматически отменен по истечении заданного времени (дедлайна).
deadline := time.Now().Add(5 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()

select {
case <-time.After(6 * time.Second):
fmt.Println("Done")
case <-ctx.Done():
fmt.Println("Context canceled:", ctx.Err())
}


🟠context.WithTimeout(parent Context, timeout time.Duration)
context.WithTimeout(parent Context, timeout time.Duration) создает дочерний контекст, который будет автоматически отменен через заданное время (таймаут). Это упрощенный вариант WithDeadline.
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()

select {
case <-time.After(6 * time.Second):
fmt.Println("Done")
case <-ctx.Done():
fmt.Println("Context canceled:", ctx.Err())
}


🟠context.WithValue(parent Context, key, val interface{})
context.WithValue(parent Context, key, val interface{}) создает дочерний контекст, который несет значение, связанное с заданным ключом. Это полезно для передачи метаданных между функциями.
type key string

func main() {
ctx := context.WithValue(context.Background(), key("userID"), 12345)

process(ctx)
}

func process(ctx context.Context) {
if v := ctx.Value(key("userID")); v != nil {
fmt.Println("UserID:", v)
} else {
fmt.Println("UserID not found")
}
}


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

HTTP (HyperText Transfer Protocol) — это протокол передачи данных, который используется для загрузки веб-страниц. HTTPS (HTTP Secure) — это расширенная версия HTTP, которая добавляет слой шифрования (SSL/TLS), обеспечивая защиту передаваемых данных от прослушивания, перехвата и изменения посторонними. HTTPS необходим для защиты конфиденциальности и безопасности пользовательских данных.

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

Контекст (context.Context) используется для управления временем выполнения, обмена метаданными и отмены операций.

🚩Кейсы

🟠Управление временем выполнения операций
Контексты задают таймауты и дедлайны, автоматически отменяя операции по истечении времени.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://example.com", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()

fmt.Println("Response status:", resp.Status)


🟠Отмена долгих операций
Контексты позволяют явно отменять операции, что полезно для управления горутинами.
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Goroutine canceled")
return
default:
fmt.Println("Goroutine working")
time.Sleep(500 * time.Millisecond)
}
}
}(ctx)

time.Sleep(2 * time.Second)
cancel()
time.Sleep(1 * time.Second)


🟠Передача метаданных
Контексты позволяют передавать метаданные между функциями.
type key string

ctx := context.WithValue(context.Background(), key("userID"), 12345)
process(ctx)

func process(ctx context.Context) {
userID := ctx.Value(key("userID")).(int)
fmt.Println("UserID:", userID)
}


🟠Управление жизненным циклом запросов в веб-серверах
Контексты управляют жизненным циклом запросов, обеспечивая таймауты и отмену при завершении запросов.
func handler(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
fmt.Println("Handler started")
defer fmt.Println("Handler ended")

select {
case <-time.After(5 * time.Second):
fmt.Fprintln(w, "Request processed")
case <-ctx.Done():
err := ctx.Err()
fmt.Println("Handler error:", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)


🟠Синхронизация горутин
Контексты синхронизируют горутины и управляют их завершением.
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

var wg sync.WaitGroup
wg.Add(2)

go func() {
defer wg.Done()
worker(ctx, "Worker 1")
}()

go func() {
defer wg.Done()
worker(ctx, "Worker 2")
}()

wg.Wait()

func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name, "stopped")
return
default:
fmt.Println(name, "working")
time.Sleep(1 * time.Second)
}
}
}


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

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

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

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

🚩Монолитная архитектура

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

🚩Плюсы

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

🚩Минусы

Трудность масштабирования: Масштабирование всего приложения для увеличения производительности может быть неэффективным.
Сложность поддержки: Со временем кодовая база может стать очень большой и сложной для понимания и поддержки.
Независимые развертывания: Изменения в одной части приложения требуют повторного развертывания всего приложения.

🚩Микросервисная архитектура

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

🚩Плюсы

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

🚩Минусы

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

🚩Пример различий на практике

🟠Монолит:
Представьте приложение для электронной коммерции, где интерфейс, управление пользователями, обработка заказов и управление продуктами — всё это часть одного большого приложения.

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

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM