Микросервисы на Go: архитектура и практика от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в микросервисы на Go

Микросервисная архитектура стала стандартом де-факто для построения современных распределенных систем. Язык Go (Golang) с его легковесными горутинами, высокой производительностью и простотой развертывания идеально подходит для реализации микросервисов. В этой статье мы разберем архитектуру микросервисов на Go от концепции до практической реализации с примерами кода.

Go предлагает встроенную поддержку конкурентности, быстрый запуск и малый размер бинарных файлов — все это критически важно для микросервисов. Мы рассмотрим, как спроектировать сервис, реализовать REST API, добавить middleware и подготовить приложение к контейнеризации.

Архитектура микросервиса на Go

1. Структура проекта

Правильная организация кода — основа поддерживаемого микросервиса. Рекомендуемая структура:

myservice/
├── cmd/
│   └── server/
│       └── main.go          # Точка входа
├── internal/
│   ├── handler/             # HTTP-обработчики
│   ├── service/             # Бизнес-логика
│   ├── repository/          # Работа с данными
│   └── middleware/          # Промежуточные обработчики
├── pkg/
│   └── models/              # Общие модели данных
├── config/
│   └── config.go            # Конфигурация
├── go.mod
└── Dockerfile

Такая структура соответствует принципам Clean Architecture: handler → service → repository. Это позволяет легко тестировать каждый слой независимо.

2. Конфигурация и запуск

Начнем с простого HTTP-сервера с использованием стандартной библиотеки net/http:

// cmd/server/main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, `{"status": "ok"}`)
    })

    log.Printf("Server starting on :%s", port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

Это минимальный микросервис. Для production используйте роутер с поддержкой middleware, например gorilla/mux или chi.

Практическая реализация микросервиса

3. REST API с gorilla/mux

Установим gorilla/mux и реализуем CRUD для сущности "Пользователь":

// go.mod
module github.com/youruser/userservice

go 1.21

require github.com/gorilla/mux v1.8.1
// internal/handler/user.go
package handler

import (
    "encoding/json"
    "net/http"
    "github.com/gorilla/mux"
)

type User struct {
    ID   string `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

var users = []User{
    {ID: "1", Name: "Alice", Age: 30},
}

func GetUsers(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

func CreateUser(w http.ResponseWriter, r *http.Request) {
    var newUser User
    if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    users = append(users, newUser)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newUser)
}
// cmd/server/main.go (продолжение)
package main

import (
    "log"
    "net/http"
    "github.com/gorilla/mux"
    "yourproject/internal/handler"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", handler.GetUsers).Methods("GET")
    r.HandleFunc("/users", handler.CreateUser).Methods("POST")

    log.Println("Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", r))
}

4. Middleware: логирование и аутентификация

Middleware — неотъемлемая часть микросервисов. Реализуем логирование запросов:

// internal/middleware/logging.go
package middleware

import (
    "log"
    "net/http"
    "time"
)

func Logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
        log.Printf("Completed in %v", time.Since(start))
    })
}

Подключаем middleware к роутеру:

// cmd/server/main.go
func main() {
    r := mux.NewRouter()
    r.Use(middleware.Logging) // Глобальное middleware

    r.HandleFunc("/users", handler.GetUsers).Methods("GET")
    // ...
}

5. Graceful shutdown

Корректное завершение работы — важная практика. Добавим graceful shutdown:

package main

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

func main() {
    r := mux.NewRouter()
    // ... настройка роутов

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

    // Запуск в горутине
    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("listen: %s\n", err)
        }
    }()

    // Ожидание сигнала
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("Shutting down server...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    if err := srv.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }
    log.Println("Server exited")
}

Docker и развертывание

Микросервисы на Go идеально подходят для контейнеризации. Пример Dockerfile с multi-stage сборкой:

# Dockerfile
FROM golang:1.21-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:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/server .
EXPOSE                

Блоги

Рекомендаций по книгам