> Как программа определяет URL для выполнения запросов по списку (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Ozon

Стек: Go

> Пример ответа

Короткий ответ

Программа определяет URL для запросов через конфигурацию (env vars, config file), service discovery или DNS. В Go обычно используется net/http с базовым URL из конфига, а пути формируются через fmt.Sprintf или url.URL из стандартной библиотеки. Для списка URL применяется массив строк из конфига или отдельный сервис реестра. Ключевой момент - не хардкодить URL, а выносить их в настройки и обрабатывать ошибки при парсинге.

Подробное объяснение

В Go URL для запросов определяется на нескольких уровнях:

  1. Конфигурация - через os.Getenv, flag, или файлы (JSON, YAML, env). Для списка URL обычно используется JSON-массив или строка с разделителями.

  2. Формирование URL - базовый URL из конфига, путь и query-параметры собираются через net/url:

    • url.Parse для разбора
    • url.URL структура для модификации полей
    • Query() для параметров
  3. Service discovery - для динамических списков (Kubernetes, Consul) URL получают через API или DNS SRV-записи.

  4. Балансировка - если список URL, выбор может быть round-robin, random или по health-check.

Пример структуры конфига:

GO
type Config struct {
Endpoints []string `json:"endpoints"`
Timeout time.Duration
}

На практике

Для списка URL в Go-сервисе:

  • Используйте []string в конфиге, но валидируйте каждый URL через url.ParseRequestURI.
  • Для production - разделяйте базовый URL и пути, чтобы не дублировать.
  • При работе с несколькими сервисами - применяйте интерфейс с методом BaseURL(), а не глобальные переменные.
  • Для динамического списка - обновляйте конфиг через сигналы (SIGHUP) или watch на файл.
  • Не забывайте про контекст и таймауты - URL может быть недоступен.

Пример кода

GO
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"time"
)
type Client struct {
baseURL *url.URL
endpoints []string
http *http.Client
}
func NewClient(endpoints []string) (*Client, error) {
if len(endpoints) == 0 {
return nil, fmt.Errorf("no endpoints provided")
}
base, err := url.Parse(endpoints[0])
if err != nil {
return nil, fmt.Errorf("invalid base URL: %w", err)
}
return &Client{
baseURL: base,
endpoints: endpoints,
http: &http.Client{Timeout: 5 * time.Second},
}, nil
}
func (c *Client) BuildURL(path string, params url.Values) string {
u := *c.baseURL
u.Path = path
u.RawQuery = params.Encode()
return u.String()
}
func (c *Client) Fetch(ctx context.Context, path string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.BuildURL(path, nil), nil)
if err != nil {
return nil, err
}
return c.http.Do(req)
}
func main() {
// из env: ENDPOINTS="http://a.com,http://b.com"
raw := os.Getenv("ENDPOINTS")
endpoints := splitAndTrim(raw)
client, err := NewClient(endpoints)
if err != nil {
panic(err)
}
resp, err := client.Fetch(context.Background(), "/api/v1/items")
if err != nil {
fmt.Println("error:", err)
return
}
defer resp.Body.Close()
fmt.Println("status:", resp.StatusCode)
}
func splitAndTrim(s string) []string {
if s == "" {
return nil
}
parts := strings.Split(s, ",")
result := make([]string, 0, len(parts))
for _, p := range parts {
if trimmed := strings.TrimSpace(p); trimmed != "" {
result = append(result, trimmed)
}
}
return result
}

Как отвечать на собеседовании

  • Начните с конфигурации - это ожидаемый ответ для senior.
  • Упомяните разницу между статическим и динамическим списком URL.
  • Покажите понимание net/url - это важный нюанс Go.
  • Расскажите про обработку ошибок: невалидный URL, пустой список, таймауты.
  • Если спросят про production - скажите про service discovery и health checks.
  • Не углубляйтесь в детали фреймворков, если не спросят.

Что проверяет интервьюер

  • Понимание разделения конфигурации и кода.
  • Умение работать с net/url и net/http в Go.
  • Навыки обработки ошибок и edge cases.
  • Знание паттернов для динамических списков (service discovery).
  • Умение проектировать API клиента (интерфейсы, зависимости).

Типичные ошибки

  • Хардкод URL в коде - сразу красный флаг.
  • Игнорирование ошибок при парсинге URL - url.Parse может вернуть ошибку.
  • Не учитывать trailing slash - http://a.com/api vs http://a.com/api/.
  • Путать base URL и полный URL - при изменении хоста ломается всё.
  • Не использовать context для отмены запросов.
  • Забывать про валидацию пустого списка endpoints.
  • Складывать URL через конкатенацию строк вместо url.URL.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью