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

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

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

Начать курс

Введение: Почему Go — идеальный язык для микросервисов?

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

Go предлагает быструю компиляцию, минимальное потребление памяти и нативную работу с горутинами — легковесными потоками. Это делает его отличным выбором для высоконагруженных распределенных систем. Мы рассмотрим ключевые концепции, напишем примеры кода и обсудим лучшие практики.

1. Проектирование архитектуры микросервиса на Go

1.1. Принципы микросервисной архитектуры

Прежде чем писать код, необходимо определить границы микросервисов. Основные принципы:

  • Единая ответственность (Single Responsibility) — каждый сервис отвечает за одну бизнес-функцию.
  • Независимость развертывания — каждый микросервис может быть обновлен без остановки всей системы.
  • Слабая связанность (Loose Coupling) — сервисы общаются через четко определенные API.
  • Децентрализация управления данными — каждый сервис имеет свою базу данных.

1.2. Структура проекта Go для микросервиса

Рекомендуемая структура папок для микросервиса на Go:

my-service/
├── cmd/
│   └── server/
│       └── main.go          # Точка входа
├── internal/
│   ├── handler/             # HTTP/gRPC обработчики
│   ├── service/             # Бизнес-логика
│   ├── repository/          # Работа с БД
│   └── model/               # Структуры данных
├── pkg/
│   └── api/                 # Общие прото-файлы (gRPC)
├── config/
│   └── config.go            # Конфигурация
├── migrations/              # Миграции БД
├── Dockerfile
└── go.mod

1.3. Пример простого микросервиса

Создадим базовый HTTP-микросервис для управления пользователями. Начнем с определения модели и репозитория:

// internal/model/user.go
package model

type User struct {
    ID    int64  `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// internal/repository/user_repo.go
package repository

import (
    "context"
    "my-service/internal/model"
)

type UserRepository interface {
    GetByID(ctx context.Context, id int64) (*model.User, error)
    Create(ctx context.Context, user *model.User) (int64, error)
}

type inMemoryRepo struct {
    users map[int64]*model.User
    nextID int64
}

func NewInMemoryRepo() UserRepository {
    return &inMemoryRepo{
        users:  make(map[int64]*model.User),
        nextID: 1,
    }
}

func (r *inMemoryRepo) GetByID(ctx context.Context, id int64) (*model.User, error) {
    user, ok := r.users[id]
    if !ok {
        return nil, fmt.Errorf("user not found")
    }
    return user, nil
}

func (r *inMemoryRepo) Create(ctx context.Context, user *model.User) (int64, error) {
    user.ID = r.nextID
    r.users[user.ID] = user
    r.nextID++
    return user.ID, nil
}

2. Коммуникация между микросервисами: REST, gRPC и очереди

2.1. REST API на Go

Для синхронного взаимодействия чаще всего используют REST или gRPC. Рассмотрим REST-контроллер с использованием стандартной библиотеки net/http и роутера chi:

// internal/handler/user_handler.go
package handler

import (
    "encoding/json"
    "net/http"
    "strconv"
    "my-service/internal/service"
    "github.com/go-chi/chi/v5"
)

type UserHandler struct {
    svc *service.UserService
}

func NewUserHandler(svc *service.UserService) *UserHandler {
    return &UserHandler{svc: svc}
}

func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
    idStr := chi.URLParam(r, "id")
    id, err := strconv.ParseInt(idStr, 10, 64)
    if err != nil {
        http.Error(w, "invalid id", http.StatusBadRequest)
        return
    }

    user, err := h.svc.GetUser(r.Context(), id)
    if err != nil {
        http.Error(w, err.Error(), http.StatusNotFound)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

2.2. gRPC: современный протокол для микросервисов

gRPC обеспечивает более высокую производительность и строгую типизацию. Пример proto-файла и сервера:

// pkg/api/user.proto
syntax = "proto3";

package userapi;

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
}

message GetUserRequest {
    int64 id = 1;
}

message User {
    int64 id = 1;
    string name = 2;
    string email = 3;
}

// cmd/server/main.go (gRPC часть)
import (
    "google.golang.org/grpc"
    pb "my-service/pkg/api"
)

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    grpcServer := grpc.NewServer()
    pb.RegisterUserServiceServer(grpcServer, &userGRPCServer{})
    grpcServer.Serve(lis)
}

2.3. Асинхронная коммуникация через очереди

Для слабой связанности часто используют RabbitMQ или Kafka. Пример отправки сообщения:

// internal/messaging/producer.go
package messaging

import (
    "context"
    "encoding/json"
    "github.com/rabbitmq/amqp091-go"
)

type EventProducer struct {
    channel *amqp091.Channel
}

func (p *EventProducer) PublishUserCreated(ctx context.Context, user model.User) error {
    body, _ := json.Marshal(user)
    return p.channel.PublishWithContext(ctx,
        "user_events",      // exchange
        "user.created",     // routing key
        false, false,
        amqp091.Publishing{
            ContentType: "application/json",
            Body:        body,
        })
}

3. Контейнеризация и развертывание микросервисов на Go

3.1. Docker-контейнер для Go-приложения

Go компили

Блоги

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