логотип PurpleSchool
логотип PurpleSchool

Микросервисы gRPC в Golang

Автор

Александр Гольцман

Микросервисы gRPC в Golang

gRPC — это современный фреймворк для удаленного вызова процедур (RPC), разработанный Google. Он основан на протоколе HTTP/2 и использует формат сериализации Protocol Buffers (protobuf), что делает его эффективным и быстрым. gRPC отлично подходит для построения микросервисной архитектуры, позволяя сервисам взаимодействовать друг с другом через строго типизированные API.

В этой статье я расскажу, как использовать gRPC в Golang, объясню его основные принципы, покажу, как создать сервер и клиента, и разберу важные аспекты, такие как сериализация данных и обработка ошибок.

Что такое gRPC и почему он важен?

gRPC решает задачу эффективного взаимодействия между сервисами. В отличие от REST API, который использует текстовый формат JSON и HTTP/1.1, gRPC работает поверх HTTP/2 и использует бинарный формат Protocol Buffers. Это дает несколько преимуществ:

  • Высокая производительность — бинарная сериализация быстрее и компактнее JSON.
  • Поддержка потоковой передачи данных — gRPC позволяет реализовывать стриминговые вызовы.
  • Языковая независимость — клиенты и серверы могут быть написаны на разных языках.
  • Автоматическая генерация кода — API описываются в файлах .proto, а на их основе создаются серверные и клиентские обертки.

Теперь давайте посмотрим, как использовать gRPC в Go.

Установка gRPC в Go

Перед началом работы необходимо установить пакет gRPC и компилятор Protocol Buffers:

go install google.golang.org/protobuf/cmd/protoc@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

Эти инструменты нужны для генерации Go-кода из .proto файлов. Теперь добавим зависимости в проект:

go get google.golang.org/grpc
go get google.golang.org/protobuf

Определение gRPC-сервиса

В gRPC API описывается с помощью файла .proto. Давайте создадим сервис для управления пользователями:

syntax = "proto3";

package user;

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
  string id = 1;
}

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

Смотрите, здесь определен сервис UserService с одним методом GetUser, который принимает UserRequest (содержит id пользователя) и возвращает UserResponse (данные о пользователе).

Теперь сгенерируем код для Go:

protoc --go_out=. --go-grpc_out=. user.proto

Реализация gRPC-сервера

Создадим сервер на Go, который будет реализовывать UserService:

package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "path/to/generated/userpb" // Импорт сгенерированного кода
)

// Реализация сервиса
type userServiceServer struct {
    pb.UnimplementedUserServiceServer
}

// Метод GetUser
func (s *userServiceServer) GetUser(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
    log.Printf("Получен запрос на пользователя с ID: %s", req.Id)

    return &pb.UserResponse{
        Id:    req.Id,
        Name:  "Иван Иванов",
        Email: "ivan@example.com",
    }, nil
}

func main() {
    // Создаем TCP-сервер
    listener, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("Ошибка запуска сервера: %v", err)
    }

    grpcServer := grpc.NewServer()
    pb.RegisterUserServiceServer(grpcServer, &userServiceServer{})

    log.Println("gRPC сервер запущен на порту 50051")
    if err := grpcServer.Serve(listener); err != nil {
        log.Fatalf("Ошибка запуска сервера: %v", err)
    }
}

Смотрите, что здесь происходит:

  1. Создаем сервер и реализуем UserServiceServer.
  2. Определяем метод GetUser, который возвращает фиктивные данные о пользователе.
  3. Запускаем сервер на порту 50051 и ожидаем подключения клиентов.

Реализация gRPC-клиента

Теперь создадим клиент, который будет отправлять запросы серверу:

package main

import (
    "context"
    "log"
    "time"

    "google.golang.org/grpc"
    pb "path/to/generated/userpb"
)

func main() {
    // Устанавливаем соединение с сервером
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Ошибка подключения: %v", err)
    }
    defer conn.Close()

    client := pb.NewUserServiceClient(conn)

    // Отправляем запрос
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    res, err := client.GetUser(ctx, &pb.UserRequest{Id: "123"})
    if err != nil {
        log.Fatalf("Ошибка при вызове GetUser: %v", err)
    }

    log.Printf("Получены данные о пользователе: ID=%s, Name=%s, Email=%s", res.Id, res.Name, res.Email)
}

В этом коде:

  1. Создается клиент gRPC и устанавливается соединение с сервером.
  2. Отправляется запрос GetUser с id = "123".
  3. Выводятся полученные данные.

Теперь, если запустить сервер и затем клиента, клиент получит информацию о пользователе от сервера.

gRPC и REST: в чем разница?

gRPC часто сравнивают с REST API, так как оба способа позволяют сервисам взаимодействовать друг с другом.

ФункцияgRPCREST
ПротоколHTTP/2HTTP/1.1
Формат данныхProtocol BuffersJSON
ПроизводительностьВысокая (бинарные данные)Ниже (текстовые данные)
Поддержка стримингаДаНет
Автогенерация кодаДаНет

Смотрите, если вам важна производительность, строгая типизация и поддержка потоков — gRPC будет лучшим выбором. Если же нужно простое взаимодействие между сервисами без сложной настройки, REST может быть более удобным.

Заключение

gRPC — это мощный инструмент для создания микросервисной архитектуры, который обеспечивает высокую скорость работы и удобную типизацию API. Он использует HTTP/2, бинарную сериализацию и автогенерацию кода, что делает его удобным для работы в распределенных системах.

В этой статье я показал, как установить и использовать gRPC в Go, создать сервер, написать клиента и объяснил основные концепции. Попробуйте реализовать свой собственный gRPC-сервис и оцените его удобство на практике!

Стрелочка влевоИспользование mock в GolangВеб-фреймворк Gin в GolangСтрелочка вправо

Постройте личный план изучения Golang до уровня Middle — бесплатно!

Golang — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Golang

Работа с YAML в GolangПреобразование типов в GolangКонвертация структур в JSON в GolangStrconv в GolangИспользование пакета SQLx для работы с базами данных в GolangРазбираемся с SQL в GolangРазделение строк с помощью функции split в GolangSort в GoПоиск и замена строк в Go - GolangИспользование пакета reflect в GolangРабота с PostgreSQL в GoPointers в GolangПарсинг в GoРабота со списками (list) в GolangПреобразование int в string в GolangРабота с числами с плавающей точкой в GolangРабота с полями в GolangИспользование enum в GolangОбработка JSON в GoЧтение и запись CSV-файлов в GolangРабота с cookie в GolangРегистры в GoКэширование данных в GolangПреобразование byte в string в GolangByte в GoИспользование bufio для работы с потоками данных в GolangДобавление данных и элементов (add) в Go
Логирование в Golang. Zap, Logrus, Loki, GrafanaРабота с Docker-контейнерами в GoИспользование pprof в GolangМеханизмы синхронизации в GolangРабота с пакетом S3 в GolangМониторинг Golang приложений с помощью PrometheusОптимизация проектов на GoПаттерны проектирования в GolangМиграции базы данных в GolangОркестрация контейнеров Go с Kubernetes + DockerGjGo Playground и компилятор GolangИспользование go mod init для создания модулей GolangРабота с переменными окружения (env) в GolangКоманда go build в GolangАвтоматизация Golang проектов — CI/CD с GitLab CI и JenkinsОтладка кода в GolangЧтение и использование конфигурации в приложениях на GolangКомпиляция в GolangКак развернуть Go-приложение на облаке AWSАутентификация в Golang
Сетевые протоколы в GoПеременные в GolangЗначения в GolangДженерик %T и его применение в GolangТипы данных в GolangИспользование tls в GolangИспользование tag в структурах GolangSwitch в GoСтроки в GolangРабота с потоками (stream) в GolangSelect в GoРуны в GoРабота с пакетом params в GolangКонвертация строк в числа в GolangNull, Nil, None, 0 в GoНаименования переменных, функций и структур в GoInt в GolangУстановка GolangЧтение и установка HTTP заголовков в GolangMethods в GolangGoLand — IDE для разработки на Golang от JetBrainsОбработка «not found» в GolangFloat в GolangФлаги командной строки в Go (Golang)Запуск внешних команд в GolangОбработка ошибок в GoИспользование defer в GolangЗначения default в GolangГенерация кода в GoФорматирование кода в GolangЧистая архитектура в GolangКаналы (channels) в GolangПолучение body из HTTP запроса в Golang
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы Golang

Антон Ларичев
иконка часов19 бесплатных уроков
иконка звёздочки рейтинга4.7
Backend
изображение курса

Nest.js с нуля

Антон Ларичев
иконка часов11 бесплатных уроков
иконка звёздочки рейтинга4.6
Backend
изображение курса

Docker и Ansible

Антон Ларичев
иконка часов19 бесплатных уроков
иконка звёздочки рейтинга4.8
Backend
DevOps