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