---
title: "Установка и кодогенерация protoc"
description: "Установка protoc и плагинов protoc-gen-go: команда генерации с флагами, файлы .pb.go и _grpc.pb.go, паттерн Unimplemented и частые ошибки сборки."
course: "gRPC-Go: высокопроизводительный RPC"
url: "https://golangmaster.ru/courses/grpc-go/lessons/03-generaciya/"
published: "2026-09-19"
updated: "2026-09-26"
minutes: 20
---

# Установка и кодогенерация protoc

*Курс «gRPC-Go: высокопроизводительный RPC», урок 3 из 25. ~20 минут. Обновлено 26.09.2026. Источник: https://golangmaster.ru/courses/grpc-go/lessons/03-generaciya/*

.proto-файл сам по себе ничего не исполняет — превращает его в Go-код компилятор protoc с двумя плагинами: protoc-gen-go и protoc-gen-go-grpc. В этом уроке поставим весь набор, разберём команду кодогенерации по флагам и заглянем в сгенерированные файлы .pb.go, чтобы дальше читать их как обычный код проекта.

Схема из прошлого урока лежит в api/greeter/v1/greeter.proto — и сейчас это просто текст. Чтобы из него получились типы Go, интерфейс сервера и клиентская заглушка, нужен компилятор protoc и два плагина. Настройка делается один раз на машину разработчика и один раз в CI, но именно на этом шаге новичок чаще всего застревает: плагин не найден, файлы легли не в ту папку, версия runtime не совпала с версией компилятора. Поэтому пройдём весь путь по шагам: установка protoc под вашу ОС, установка плагинов через go install, команда генерации с разбором каждого флага, анатомия двух сгенерированных файлов и четыре самые частые ошибки с их причинами. После этого урока фраза «перегенерируй pb» перестанет быть магией — вы будете знать, что именно происходит между .proto и компиляцией.

### Шаг 1: установить protoc и плагины

1. macOS: brew install protobuf
2. Debian/Ubuntu: apt install -y protobuf-compiler — учтите, в старых дистрибутивах в репозитории лежит устаревшая версия; свежее — бинарный zip с GitHub-страницы релизов protobuf
3. Windows: choco install protoc, либо тот же zip protoc-<версия>-win64.zip из релизов — распаковать и добавить в PATH
4. Проверка: protoc --version печатает строку вида libprotoc 25.x (число у вас будет другое)
5. Плагин сериализации: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
6. Плагин gRPC: go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
7. Проверка: оба бинарника должны быть видны в PATH — они уходят в $GOPATH/bin (или $GOBIN); protoc вызывает их как внешние программы по имени

> **@latest удобно в терминале и опасно в CI.** Плагины развиваются вместе с runtime, и свежий protoc при старом runtime иногда рождает код, который старая библиотека не переварит. Для локальной разработки берите @latest, для CI фиксируйте конкретную версию — @v1.31.0 у protoc-gen-go, @v1.3.0 у protoc-gen-go-grpc — и обновляйте осознанно.

### Команда protoc: разбор по флагам

Сама генерация — одна команда, и её стоит не копировать вслепую, а разобрать. protoc ничего не знает про Go: он читает схему, а генерацию делегирует плагинам, вызывая их как дочерние процессы по имени. За то, куда попадут файлы и как устроятся пути, отвечают отдельные флаги — и именно вокруг них возникают почти все вопросы новичков. Команда ниже — рабочий минимум для схемы из двух предыдущих уроков.

### Рабочая команда генерации

```go
protoc \
  --proto_path=api \
  --go_out=gen \
  --go_opt=paths=source_relative \
  --go-grpc_out=gen \
  --go-grpc_opt=paths=source_relative \
  api/greeter/v1/greeter.proto
```

--proto_path — корень, относительно которого protoc ищет импортируемые схемы; аргумент greeter.proto задаётся именно относительно него. --go_out=gen — папка для кода сериализации, --go-grpc_out=gen — для gRPC-клиента и сервера. paths=source_relative сохраняет структуру папок относительно proto_path, поэтому файлы лягут в gen/greeter/v1/. Опции нужно продублировать каждому плагину отдельно — один общий --go_opt на оба плагина не распространяется.

### Что генерируется: два файла, две ответственности

После команды в gen/greeter/v1/ появятся два файла, и их разделение — не случайность. greeter.pb.go создаёт protoc-gen-go: структуры HelloRequest и HelloReply с полями из схемы, геттеры Get-методы (они безопасно возвращают нулевые значения даже для nil-структуры), Marshal/Unmarshal и весь низкоуровневый код поверх runtime-пакета google.golang.org/protobuf. greeter_grpc.pb.go создаёт protoc-gen-go-grpc: интерфейс GreeterServer для вашей реализации, структуру GreeterClient с готовыми методами-вызовами и функцию RegisterGreeterServer. Разделение имеет смысл: сериализация и транспорт развиваются независимо, и protobuf-код нужен даже там, где gRPC нет. Оба файла начинаются с пометки Code generated ... DO NOT EDIT — protoc перезапишет любую правку при следующем запуске, поэтому в код-ревью их диффы просматривают, но не редактируют. Старый импорт github.com/golang/protobuf — устаревший фасад, в новых проектах берите google.golang.org/protobuf, и запомните этот путь: именно его добавит в go.mod команда go mod tidy после первой генерации.

### Заглянем внутрь greeter_grpc.pb.go

```go
// Фрагмент сгенерированного файла (упрощён для чтения).

type GreeterServer interface {
        SayHello(context.Context, *HelloRequest) (*HelloReply, error)
        mustEmbedUnimplementedGreeterServer()
}

type UnimplementedGreeterServer struct{}

func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {}

func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
        s.RegisterService(&Greeter_ServiceDesc, srv)
}
```

Интерфейс сервера — это ваши rpc-методы из .proto, переведённые в Go-сигнатуры, плюс обязательный метод-маркер mustEmbedUnimplementedGreeterServer. RegisterGreeterServer связывает реализацию с дескриптором сервиса — именно её вы вызывали в main() первого урока. Клиентская сторона того же файла даёт GreeterClient с готовым SayHello.

### Unimplemented-паттерн: зачем навязывают встраивание

Строка mustEmbedUnimplementedGreeterServer выглядит как чужеродный хак, но решение осмысленное. Если в схему добавят метод WatchOrders, интерфейс GreeterServer пополнится — и все существующие реализации перестанут компилироваться. Встраивание UnimplementedGreeterServer спасает: у него есть «заглушки» на все методы, возвращающие статус Unimplemented, поэтому ваш server продолжает собираться, а новый метод просто вежливо отвечает «не поддерживаю», пока вы его не реализуете. Требование появилось в генераторе в 2020 году как дефолт; несогласным дают выход — опцию --go-grpc_opt=require_unimplemented_servers=false, после чего маркер из интерфейса исчезает. Моя позиция: не отключайте. Незаметная ошибка компиляции, которая выявляет пропущенный метод на билде, стоит одной строки встраивания — а потомок, который будет расширять схему, скажет вам спасибо.

> **Четыре частые ошибки первой генерации.** «protoc-gen-go: program not found» — плагин не в PATH, проверьте $GOPATH/bin. Файлы легли не туда — вы забыли paths=source_relative, и protoc разложил код по папкам из go_package. «file ... is newer than the code» — компилятор свежее runtime-библиотеки: обновите google.golang.org/protobuf или возьмите protoc постарше. «unknown service» при вызове — сервер запущен, но RegisterGreeterServer забыли вызвать.

### paths=source_relative и структура gen/

Опция paths управляет тем, как protoc разложит файлы по папкам вывода. Без неё он строит дерево из go_package: запросите --go_out=gen при go_package = "github.com/example/greeter/gen/greeterv1", и файлы окажутся в gen/github.com/example/greeter/gen/greeterv1/ — гнездо, которое никто не просил. С paths=source_relative файл кладётся рядом с исходником относительно proto_path: api/greeter/v1/greeter.proto даёт gen/greeter/v1/greeter.pb.go. Два расклада считаются рабочими: держать схемы в api/ или proto/, а генерат — в gen/ (импорт-пути чистые, читателю очевидно, что код генерированный), либо класть .proto рядом с пакетом. Главное — go_package должен указывать на тот путь, по которому пакет реально импортируется, иначе go mod tidy не соберёт картинку. Менять расклад после того, как схема опубликована, — боль, выберите один раз и запишите в Makefile.

### Автоматизация: go:generate

```go
package gen

// Перегенерация после правок схем: go generate ./...
//
//go:generate protoc --proto_path=api --go_out=gen --go_opt=paths=source_relative --go-grpc_out=gen --go-grpc_opt=paths=source_relative api/greeter/v1/greeter.proto
//go:generate go mod tidy
```

Директива //go:generate хранит команду генерации в коде проекта, и go generate ./... запускает её для всех пакетов. Один источник правды: схема в api/, команда — в этой директиве, руки вводят protoc только один раз за всю жизнь проекта.

> ** protoc — не единственный путь.** В индустрии популярен buf: единый инструмент вместо связки protoc+плагины, с линтером схем и проверкой ломающих изменений. Для обучения я всё же начинаю с protoc — он показывает механику процесса, а buf станет естественным апгрейдом, который разбирается в курсе про Connect.

### Проверка: файлы кодогенерации

**Вопрос:** Какие Go-файлы создаёт связка protoc + плагины из greeter.proto?

- Один: greeter.go со всем кодом сразу
- ✅ Два: greeter.pb.go — типы и сериализация, greeter_grpc.pb.go — клиент и сервер gRPC
- Три: pb.go, grpc.pb.go и main.go со скелетом приложения
- Ни одного: protoc лишь проверяет синтаксис схемы

protoc-gen-go отвечает за сообщения и маршалинг (greeter.pb.go), protoc-gen-go-grpc — за транспортный слой: интерфейс сервера, клиентскую заглушку и Register-функцию (greeter_grpc.pb.go).

### Проверка: Unimplemented-паттерн

**Вопрос:** Зачем сгенерированный интерфейс требует встраивать UnimplementedGreeterServer?

- Чтобы автоматически реализовать ваши бизнес-методы
- ✅ Для прямой совместимости: добавление методов в схему не ломает компиляцию существующих серверов
- Таково требование протокола HTTP/2
- Для логирования всех вызовов сервиса

Unimplemented-заглушки закрывают будущие методы схемы со статусом Unimplemented. Без встраивания каждый новый rpc в .proto ломал бы компиляцию всех старых реализаций — именно это требование и защищает от аварии.

### Что получится без --go-grpc_out?

```go
protoc --proto_path=api \
  --go_out=gen \
  --go_opt=paths=source_relative \
  api/greeter/v1/greeter.proto
```

**Что выведет программа?**

- Оба файла — protoc сам догадается запустить gRPC-плагин
- ✅ Только gen/greeter/v1/greeter.pb.go: без greeter_grpc.pb.go код с RegisterGreeterServer не скомпилируется
- Ошибка выполнения — protoc требует оба плагина всегда
- Пустая папка gen: без --go-grpc_out protoc ничего не генерирует

Флаги --go_out и --go-grpc_out независимы: команда отработает без ошибок и создаст только сериализацию. Интерфейс GreeterServer и RegisterGreeterServer появятся лишь после добавления --go-grpc_out, пока же ваш main.go с регистратором не соберётся.

### Соберите команду protoc

Восстановите команду генерации: имя компилятора, папки вывода и ключевое слово опции путей.

```go
___ --proto_path=api \
  --___=gen \
  --go_opt=paths=___ \
  --go-grpc_out=___ \
  api/greeter/v1/greeter.proto
```

Подсказка: Компилятор называется protoc; код сериализации отправляется во флаг --go_out; paths=source_relative сохраняет структуру папок; gRPC-код тоже кладём в gen.

Правильные ответы в пропусках: `protoc`, `go_out`, `source_relative`, `gen`

### Пайплайн: от установки до реализации

Расставьте шаги в правильном порядке — от чистой машины до компилирующегося сервера.

Правильный порядок строк:

1. `Установить protoc: brew install protobuf`
2. `Установить плагины: go install .../protoc-gen-go@latest и protoc-gen-go-grpc@latest`
3. `Убедиться, что $GOPATH/bin с плагинами лежит в PATH`
4. `Описать схему api/greeter/v1/greeter.proto`
5. `Запустить protoc с --go_out и --go-grpc_out`
6. `Выполнить go mod tidy для runtime-зависимостей`
7. `Реализовать интерфейс GreeterServer и зарегистрировать его в main()`

### Что дальше по курсу

Инструменты на месте, код сгенерирован — пора писать руками. [Следующий урок](/courses/grpc-go/lessons/04-server/) собирает первый gRPC-сервер целиком: структура проекта, реализация интерфейса, graceful shutdown. За ним — [клиент с grpc.NewClient](/courses/grpc-go/lessons/05-klient/) и первый сквозной вызов. А когда захочется вылизать процесс сборки, вернитесь к теме: в уроке про [тестирование](/courses/grpc-go/lessons/21-testirovanie/) сгенерированный код тестируют через bufconn, без реального порта и сети.

## Коротко: что запомнить

- Потребуются три инструмента: protoc + плагины protoc-gen-go и protoc-gen-go-grpc, оба ставятся через go install и должны быть в PATH.
- protoc делегирует генерацию плагинам: --go_out — сериализация, --go-grpc_out — транспорт; опции paths дублируются каждому плагину.
- paths=source_relative сохраняет структуру папок; без него protoc строит дерево из go_package.
- Генерятся два файла: greeter.pb.go (типы, геттеры, маршалинг) и greeter_grpc.pb.go (интерфейс сервера, клиент, Register-функция).
- Unimplemented-паттерн — прямая совместимость: новые методы схемы не ломают старые серверы; отключать не советую.
- В CI фиксируйте версии плагинов, а не @latest — расхождение версий protoc и runtime ловится ошибкой «is newer than the code».
- Команду генерации держите в //go:generate или Makefile — один источник правды на проект.

## Домашнее задание

Соберите полный конвейер для схемы заказов из урока 02: положите order.proto в api/shop/v1/, поставьте protoc и плагины, сгенерируйте код в gen/shopv1/ командой с paths=source_relative и выполните go mod tidy. Затем умышленно уберите --go-grpc_out и запустите снова — убедитесь, что файл _grpc.pb.go пропал и компиляция с RegisterXServer падает. Возвратите флаг на место: заодно поймёте, что именно даёт каждый плагин.

## Вопросы и ответы по уроку

### Нужен ли Go для установки protoc?

Сам protoc — отдельный бинарник (brew, apt, choco, zip). А вот плагины protoc-gen-go и protoc-gen-go-grpc — Go-программы, они ставятся через go install, поэтому Go нужен.

### Файлы .pb.go можно править руками?

Нет: protoc перезапишет изменения при следующей генерации. В первой строке каждого файла об этом так и написано. Править надо схему .proto и код, который использует сгенерированные типы.

### Чем protoc отличается от protoc-gen-go?

protoc — компилятор схем, он парсит .proto и раздаёт работу плагинам. protoc-gen-go — плагин, который переводит сообщения в Go-код сериализации; protoc-gen-go-grpc — второй плагин для транспортного слоя gRPC.

### Как поставить protoc и protoc-gen-go на Windows?

protoc скачивается бинарным zip с релизов protobuf на GitHub (выберите win64, распакуйте и добавьте bin в PATH) либо ставится пакетным менеджером: choco install protoc или winget. Плагины ставятся одинаково на любой ОС: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest и go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest — главное, чтобы %GOPATH%/bin (обычно %USERPROFILE%\go\bin) тоже был в PATH, иначе protoc не найдёт плагин.
