От graceful shutdown до observability: 12 промтов для Go-микросервисов, которые сэкономят часы работы

Go уже давно стал языком номер один для микросервисов. Его простота, производительность и встроенная поддержка конкурентности делают его идеальным выбором для высоконагруженных систем. Но даже опытные Go-разработчики часто тратят часы на рутинные задачи: настройку graceful shutdown, конфигурацию метрик, отладку взаимосвязанных сервисов. Что если я скажу, что большую часть этой работы можно делегировать нейросети? В этой статье я собрал 12 проверенных промтов, которые помогут вам ускорить разработку и повысить качество кода. Каждый промт снабжён примером использования и кодом, чтобы вы могли сразу применить его в своих проектах.

Базовые промты: фундамент для каждого микросервиса

1. Скелет микросервиса с graceful shutdown

Задача: Сгенерировать каркас Go-микросервиса с корректным завершением работы.

Промт:

Напиши на Go 1.22 каркас HTTP-сервиса с graceful shutdown. Используй стандартный пакет net/http. Обработай сигналы SIGINT и SIGTERM, дай серверу 10 секунд на завершение текущих запросов. Добавь логирование через log/slog.

Пример результата:

package main

import (
    "context"
    "log/slog"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
    slog.SetDefault(logger)

    mux := http.NewServeMux()
    mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    })

    srv := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }

    go func() {
        slog.Info("server started", "addr", srv.Addr)
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            slog.Error("listen error", "err", err)
            os.Exit(1)
        }
    }()

    stop := make(chan os.Signal, 1)
    signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
    <-stop

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        slog.Error("shutdown error", "err", err)
    }
    slog.Info("server stopped")
}

Этот промт экономит время на написание шаблонного кода и гарантирует, что вы не забудете про обработку сигналов — частую причину «подвисающих» контейнеров в Kubernetes.

2. Dockerfile для Go-приложения

Задача: Создать оптимальный Dockerfile для Go-микросервиса.

Промт:

Создай Dockerfile для Go-приложения с многоступенчатой сборкой. Первый этап — golang:1.22-alpine, второй — alpine:3.19. Скопируй только бинарник, запусти под пользователем nobody. Добавь HEALTHCHECK.

Пример результата:

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /server ./cmd/server

FROM alpine:3.19
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder /server /server
EXPOSE 8080
HEALTHCHECK --interval=5s --timeout=3s CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["/server"]

Такой Dockerfile уменьшает размер образа и повышает безопасность (запуск от непривилегированного пользователя).

3. Middleware для логирования запросов

Задача: Добавить логирование HTTP-запросов с методом, путём, статусом и временем.

Промт:

Напиши middleware для net/http, который логирует каждый запрос: метод, путь, статус, длительность. Используй log/slog, оборачивай ResponseWriter для перехвата статуса.

Пример результата:

func LoggerMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        sw := &statusWriter{ResponseWriter: w, status: http.StatusOK}
        next.ServeHTTP(sw, r)
        slog.Info("request",
            "method", r.Method,
            "path", r.URL.Path,
            "status", sw.status,
            "duration", time.Since(start),
        )
    })
}

type statusWriter struct {
    http.ResponseWriter
    status int
}

func (w *statusWriter) WriteHeader(status int) {
    w.status = status
    w.ResponseWriter.WriteHeader(status)
}

Этот промт часто используется как основа для observability — без него сложно понять, что происходит в микросервисе.

4. Чтение конфигурации из env

Задача: Считать настройки сервиса из переменных окружения с дефолтами.

Промт:

Напиши функцию LoadConfig, которая читает переменные окружения для порта, уровня логирования и строки подключения к БД. Используй os.Getenv с дефолтными значениями. Верни структуру Config.

Пример результата:

type Config struct {
    Port        string
    LogLevel    string
    DatabaseURL string
}

func LoadConfig() Config {
    return Config{
        Port:        getEnv("PORT", "8080"),
        LogLevel:    getEnv("LOG_LEVEL", "info"),
        DatabaseURL: getEnv("DATABASE_URL", "postgres://localhost:5432/mydb"),
    }
}

func getEnv(key, fallback string) string {
    if value, ok := os.LookupEnv(key); ok {
        return value
    }
    return fallback
}

Продвинутые промты: конкурентность и отказоустойчивость

5. Graceful shutdown с ожиданием фоновых задач

Задача: Реализовать корректное завершение с учётом воркеров.

Промт:

Расширь базовый graceful shutdown: добавь поддержку фоновых воркеров, которые обрабатывают задачи из очереди. Используй sync.WaitGroup для ожидания завершения всех воркеров после остановки HTTP-сервера.

Пример результата:

var wg sync.WaitGroup

func worker(ctx context.Context, jobs <-chan Job) {
    defer wg.Done()
    for {
        select {
        case job, ok := <-jobs:
            if !ok {
                return
            }
            process(job)
        case <-ctx.Done():
            return
        }
    }
}

// В main:
ctx, cancel := context.WithCancel(context.Background())
for i := 0; i < 3; i++ {
    wg.Add(1)
    go worker(ctx, jobs)
}

// После Shutdown:
cancel()
wg.Wait()

6. Circuit breaker для внешних вызовов

Задача: Защитить микросервис от каскадных сбоев.

Промт:

Реализуй простой circuit breaker для HTTP-клиента. Если сервис вернул 5 ошибок подряд, открывай цепь на 30 секунд и возвращай fallback. Используй sync.Mutex для потокобезопасности.

Пример результата:

type CircuitBreaker struct {
    mu          sync.Mutex
    failures    int
    open        bool
    openTime    time.Time
}

func (cb *CircuitBreaker) Call(req func() (*http.Response, error)) (*http.Response, error) {
    cb.mu.Lock()
    if cb.open && time.Since(cb.openTime) > 30*time.Second {
        cb.open = false
        cb.failures = 0
    }
    if cb.open {
        cb.mu.Unlock()
        return nil, ErrCircuitOpen
    }
    cb.mu.Unlock()

    resp, err := req()
    if err != nil {
        cb.mu.Lock()
        cb.failures++
        if cb.failures >= 5 {
            cb.open = true
            cb.openTime = time.Now()
        }
        cb.mu.Unlock()
        return nil, err
    }
    cb.mu.Lock()
    cb.failures = 0
    cb.mu.Unlock()
    return resp, nil
}

7. Rate limiting на основе golang.org/x/time/rate

Задача: Ограничить количество запросов в секунду к эндпоинту.

Промт:

Напиши middleware для rate limiting с использованием golang.org/x/time/rate. Установи лимит 10 запросов в секунду с burst 20. Возвращай 429 при превышении.

Пример результата:

import "golang.org/x/time/rate"

func RateLimitMiddleware(next http.Handler) http.Handler {
    limiter := rate.NewLimiter(rate.Limit(10), 20)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !limiter.Allow() {
            http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

8. Retry с экспоненциальной задержкой

Задача: Реализовать повторные попытки с бэкоффом.

Промт:

Напиши функцию Retry, которая выполняет операцию с повторами. Используй экспоненциальную задержку: 100ms, 200ms, 400ms... максимум 5 попыток. Добавь джиттер.

Пример результата:

func Retry(operation func() error) error {
    maxAttempts := 5
    baseDelay := 100 * time.Millisecond
    for attempt := 0; attempt < maxAttempts; attempt++ {
        err := operation()
        if err == nil {
            return nil
        }
        delay := baseDelay * (1 << attempt)
        delay += time.Duration(rand.Intn(50)) * time.Millisecond // джиттер
        time.Sleep(delay)
    }
    return fmt.Errorf("operation failed after %d attempts", maxAttempts)
}

Экспертные промты: observability и оптимизация

9. Метрики Prometheus для Go-сервиса

Задача: Интегрировать метрики Prometheus в микросервис.

Промт:

Добавь в сервис метрики Prometheus: счётчик запросов с метками method и path, гистограмму времени ответа. Используй prometheus/client_golang. Зарегистрируй их в default registry и добавь эндпоинт /metrics.

Пример результата:

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    requestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "path"},
    )
    requestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name: "http_request_duration_seconds",
            Help: "HTTP request duration",
        },
        []string{"path"},
    )
)

func init() {
    prometheus.MustRegister(requestsTotal, requestDuration)
}

// В middleware:
requestsTotal.WithLabelValues(r.Method, r.URL.Path).Inc()
// Измеряем время и наблюдаем гистограмму

10. Трейсинг с OpenTelemetry

Задача: Добавить распределённое трассирование в микросервис.

Промт:

Настрой OpenTelemetry для Go-приложения: создай tracer, добавь middleware, который создаёт span для каждого HTTP-запроса. Экспортируй трейсы в Jaeger по адресу localhost:6831.

Пример результата:

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/jaeger"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)

func setupTracer() {
    exporter, _ := jaeger.New(jaeger.WithAgentEndpoint())
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exporter),
        sdktrace.WithResource(resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceName("my-service"),
        )),
    )
    otel.SetTracerProvider(tp)
}

// В middleware:
tracer := otel.Tracer("http-server")
ctx, span := tracer.Start(r.Context(), "handle "+r.URL.Path)
defer span.End()

11. Профилирование pprof в production

Задача: Включить pprof для диагностики.

Промт:

Добавь import _ "net/http/pprof" и запусти отдельный http.Server на порту 6060 для профилирования. Обработай только эндпоинты pprof, не открывай их наружу.

Пример результата:

import _ "net/http/pprof"

func startPprof() {
    go func() {
        slog.Info("pprof server started on :6060")
        if err := http.ListenAndServe("localhost:6060", nil); err != nil {
            slog.Error("pprof server error", "err", err)
        }
    }()
}

12. Кастомный логгер с уровнем и контекстом

Задача: Создать логгер с поддержкой JSON и включением request ID.

Промт:

Создай логгер на основе log/slog, который автоматически добавляет request ID из контекста в каждую запись. Используй контекст для передачи request ID.

Пример результата:

func WithRequestID(ctx context.Context, logger *slog.Logger) *slog.Logger {
    if reqID, ok := ctx.Value("request_id").(string); ok {
        return logger.With("request_id", reqID)
    }
    return logger
}

// Использование:
logger := slog.Default()
logger = WithRequestID(r.Context(), logger)
logger.Info("processing request")

Заключение

Эти 12 промтов — лишь вершина айсберга. Они покрывают основные боли разработчиков микросервисов: от базовой настройки до продвинутой наблюдаемости. Я рекомендую начать с первых четырёх, чтобы заложить прочный фундамент, а затем постепенно внедрять остальные. Помните: промты — это не замена инженерному мышлению, а инструмент для ускорения рутины. Экспериментируйте, адаптируйте их под свои задачи, и вы увидите, как скорость разработки вырастет в разы. А если у вас есть свои любимые промты — делитесь в комментариях, я всегда рад узнать что-то новое.

← Все статьи

Комментарии

Читайте также

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

23 августа 2026

11 промтов для PostgreSQL: превращаем EXPLAIN ANALYZE в понятный диагноз, а индексы — в лекарство

23 августа 2026

Юрист с AI: 12 промтов для договоров, проверки контрагентов и судебных документов

23 августа 2026

От A1 до C2: 15 промтов, которые превратят ChatGPT в вашего личного репетитора английского

23 августа 2026

От горутин до продакшена: 12 промтов, которые превратят ChatGPT в Go-архитектора

23 августа 2026

Go-промты, которые реально экономят часы: 15 сценариев от микросервисов до highload

22 августа 2026

Маркетинг на автопилоте: 15 промтов, которые заменят рутину и освободят 10+ часов в неделю

22 августа 2026

Легаси-код больше не страшен: 15 промтов для рефакторинга и оптимизации

22 августа 2026

15 проверенных промтов, которые ускоряют мои SQL-запросы в PostgreSQL и MongoDB

22 августа 2026