Вопросы для собеседования Golang разработчика | by Viktor Ugolnikov
Общие вопросы
Что происходит, когда я вбиваю в адресную строку моего браузера [Link]? Как запрос доходит до
сервера, и как идет обратно? Гранулярность — настолько маленькая, насколько возможно.
1. Парсинг строки запроса - получаем протокол, хост, ресурс, параметры запроса
2. Хост необходимо преобразовать в IP-адрес - браузер обращается к своему DNS-кэшу, DNS-кэшу ОС,
файлу hosts… Так или иначе мы резолвим хост в IP
3. Пусть это будет http запрос - сформируется GET запрос, добавятся куки при необходимости,
заголовки, хост, адрес/ресурс, параметры, тело запроса. На уровне приложения дальше будет идти
речь просто о взаимодействии по http
4. Этот запрос упакуется в tcp сегмент, средствами ОС установится tcp-соединение, начнется обмен
данными по http
5. Сервер примет запрос, вернет некий ответ (страницу + данные), браузер отобразит это пользователю
В чем отличие протоколов TCP и UDP? В каком случае UDP предпочтительнее?
TCP устанавливает соединение, гарантирует целостность и порядок сегментов
UDP просто отсылает данные
Первый нужен там, где необходима надежность обмена данными (чат, передача данных)
Второй используется при передаче избыточных данных, например видео и голосовой информации в онлайн
режиме
Можно ли убить поток внутри определенного процесса командой kill?
Команда kill завершает весь поток, нет
Как можно отсортировать файл на 100GB, в котором расположены числа в случайном порядке, по 1 числу
на строку? Ограничения — объем ОЗУ на машине составляет 1GB.
Самый распространенный ответ гугла - разбиение на доступные по памяти куски, сортировка, далее слияние
отсортированных кусков.
Какая твоя любимая технология (это может быть БД, сервер очередей, библиотека — что угодно)? А
почему?
Нет, я просто делаю задачи от ПМ-а и тимлида)))
Какие решения ты бы принял, будучи техническим директором на своем текущем/прошлом месте
работы?
Менял бы технологии/библиотеки на второстепенных/новых небольших проектах, выделял бы больше
времени на закрытие техдолга, напряг бы кого-то, чтобы привести документацию к API в порядок
Какую тему ты бы сам хотел обсудить?
Тут бы я накидывал вопросы о процессах в команде, с которой у меня интервью
Вопросы про микросервисы
(маленькое замечание - в микросервисах я ваще не шарю, поэтому не буду готовить заумные ответы)
Что, по твоему, такое микросервисы?
Микросервисы - также известные как микросервисная архитектура - это архитектурный стиль, который
структурирует приложение как набор сервисов, которые
- Легко обслуживается и тестируется
- Слабо связанный
- Независимое развертывание
- Организован вокруг бизнес-возможностей
- Принадлежит небольшой команде
Какие преимущества ты видишь у микросервисной архитектуры по сравнению с монолитом? А какие
недостатки?
+:
- Более быстрый деплой
- Возможность использовать разные технологии в одном проекте
- Проще рефакторить
- Можно масштабировать только нужный функционал
- Проще ввести нового сотрудника в работу
-:
- Труднее тестировать и запускать приложение в целом
- Необходимость заботиться о надежной коммуникации между микросервисами
- Сложность с транзакциями в БД
С какими трудностями ты сталкивался в построении микросервисной архитектуры?
Не работал с микросервисами
Что ты использовал (или о чем слышал) для трассировки сервисов? Для мониторинга? А для
логирования?
Слышал и немного использовал прометей, графану, логи вроде в кибане были (бла-бла, тут просто у меня
нет реального опыта)
Как быть с консистентностью данных между несколькими микросервисами?
Хз)))
Вопросы по языку Golang
1. Что из себя представляет тип данных string в языке Golang? Можно ли изменить определенный символ
в строке? Что происходит при склеивании строк?
string это некоторая абстракция над массивом byte (uint8), но с особенностями - нельзя менять элементы
этого массива при помощи доступа по индексу. Единичные символы в го принято обрабатывать при помощи
rune (int32, 'c' - для хранения UTF-кода любого символа этого хватит), руны связаны со строками в том
смысле, что если, к примеру, для доступа к символам строки использовать for range, то перебор будет
именно по рунам, а не по байтам, некоторые руны могут занимать более одного байта. При склеивании строк
выделяется новая область памяти под массив байт и туда пишется результат.
[Link]
2. Вытекающий вопрос — как эффективно склеивать множество строк?
Поиском в гугле ответа на этот стандартный вопрос я нашел, что самый эффективный способ этот
[Link], почти не отличается от способа с записью байтов в [Link]
[Link]
3. Что будет происходить при конкурентной записи в map? Как можно решить эту проблему?
Возможна паника/гонки при одновременном доступе к одному полю мапы, поэтому стоит использовать
мьютексы, например, из пакета sync.
4. Нужно ли лочить структуру мьютексом, если идет конкурентная запись в разные поля структуры?
Из соображений безопасности можно этого не делать, а в целях быстродействия - стоит.
[Link]
5. Что выведет код?
func main() {
[Link](1)
done := false
go func() {
done = true
}()
for !done {
}
[Link]("finished")
}
Будет лок
Как можно изменить этот код, чтобы был вывод “finished”?
Убрать ограничение на 1 процессор или увеличить.
6. Как устроены каналы “под капотом”?
Под капотом Golang — как работают каналы. Часть 1. | by Viktor Ugolnikov
Строение каналов в Golang. Часть 2. | by Viktor Ugolnikov
(пишу по памяти)
Канал внутри представлен структурой hchan, в ней есть поля:
buf
bufSize
bufQnt
указатель на позицию в буфере на запись
указатель на позицию в буфере на чтение
closed
список горутин на запись (представлены структурой sudog {elem, g})
список горутин на чтение (представлены структурой sudog {elem, g})
тип данных в канале
размер единицы данных в канале
мьютекс для блокировки вышеописанных полей при чтении/записи
7. Какая есть проблема в коде?
var counter int
for i := 0; i < 1000; i++ {
go func() {
counter++
}()
}
Гонки
Как её можно решить?
Использовать мьютекс
А как её можно бы было решить, если бы в языке не было пакета sync?
Закостылить на канале, т.к. там внутри есть мьютекс [Link]
8. Можно ли реализовать [Link] и [Link] на каналах? Как?
[Link]
9. Что ты использовал из пакета sync(кроме Mutex и WaitGroup)?
Лично я ничего. Работал с кодом с атомарными счетчиками, при выборе использования атомарного счетчика
или мьютекса мне необходимо почитать гугл, под каждую ситуацию может лучше подходить свой вариант с
точки зрения производительности.
10. Что выведет код
func main() {
v := 5
p := &v
println(*p)
changePointer(p)
println(*p)
}
func changePointer(p *int) {
v := 3
p = &v
}
5
5
Почему? Как нужно изменить функцию changePointer, чтобы вывело 5 и 3 (в оригинальной версии
выводится 5 и 5)?
[Link]
11. За сколько примерно выполнится приложение — за 3 секунды или за 6?
func worker() chan int {
ch := make(chan int)
go func() {
[Link](3 * [Link])
ch <- 42
}()
return ch
}
func main() {
timeStart := [Link]()
_, _ = <-worker(), <-worker()
println(int([Link](timeStart).Seconds())) // что выведет - 3 или 6?
}
6
Что нужно изменить, чтобы код работал за 3 секунды?
Как вариант, не читать из канала, возвращаемого функцией worker(), тогда горутина, пишущая в этот канал,
его не заблокирует