> Как реализовать HTTP сервер на Go, который выполняет параллельные запросы к нескольким системам и агрегирует ответы в JSON (Go)

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

Компании: X5Tech

Стек: Go

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

Для реализации HTTP сервера на Go, который выполняет параллельные запросы к нескольким системам и агрегирует ответы в JSON, можно использовать горутины и каналы или sync.WaitGroup. Ниже приведен пример с использованием sync.WaitGroup и context для управления таймаутами.

GO
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
type AggregatedResponse struct {
Results map[string]interface{} `json:"results"`
Errors map[string]string `json:"errors,omitempty"`
}
func fetchURL(ctx context.Context, url string, wg *sync.WaitGroup, results chan<- map[string]interface{}, errors chan<- map[string]string) {
defer wg.Done()
client := http.Client{Timeout: 5 * time.Second}
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
errors <- map[string]string{url: err.Error()}
return
}
resp, err := client.Do(req)
if err != nil {
errors <- map[string]string{url: err.Error()}
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
errors <- map[string]string{url: err.Error()}
return
}
var data interface{}
if err := json.Unmarshal(body, &data); err != nil {
errors <- map[string]string{url: err.Error()}
return
}
results <- map[string]interface{}{url: data}
}
func aggregateHandler(w http.ResponseWriter, r *http.Request) {
// Список URL для параллельных запросов (можно получать из запроса)
urls := []string{
"https://api.example.com/system1",
"https://api.example.com/system2",
"https://api.example.com/system3",
}
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
var wg sync.WaitGroup
results := make(chan map[string]interface{}, len(urls))
errors := make(chan map[string]string, len(urls))
for _, url := range urls {
wg.Add(1)
go fetchURL(ctx, url, &wg, results, errors)
}
wg.Wait()
close(results)
close(errors)
aggregated := AggregatedResponse{
Results: make(map[string]interface{}),
Errors: make(map[string]string),
}
for res := range results {
for k, v := range res {
aggregated.Results[k] = v
}
}
for err := range errors {
for k, v := range err {
aggregated.Errors[k] = v
}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(aggregated)
}
func main() {
http.HandleFunc("/aggregate", aggregateHandler)
fmt.Println("Server started on :8080")
http.ListenAndServe(":8080", nil)
}

Ключевые моменты:

  • Использование sync.WaitGroup для ожидания завершения всех горутин.
  • Каналы results и errors для сбора данных от каждой системы.
  • Контекст с таймаутом для предотвращения зависания.
  • Агрегация результатов в структуру AggregatedResponse и сериализация в JSON.
  • Обработка ошибок на каждом этапе (сетевые, парсинг JSON).

Этот код масштабируется: можно легко добавить новые URL или изменить логику агрегации.

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

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