Презентации лучше смотреть с десктопа
Слайды рассчитаны на широкий экран, клавиатуру и формат 16:9. Откройте эту страницу на ноутбуке или компьютере.
Вернуться на сайтКак и зачем
мы делаем свой buf
Эдгар Сипки//OZON FinTech
Василий Близнецов//Positive Technologies AppSec
Кто мы
Эдгар Сипки
Senior golang developer, евангелист, проповедник, проктологист.
Тимлид OZON FinTech
Василий Близнецов
Senior golang developer.
Positive Technologies AppSec
О чём поговорим?
- 01Традиционный toolset для proto, и его проблемы
- 02buf, что это и зачем оно
- 03Проблемы buf, настоящие и придуманные
- 04Как нам справиться с бедой
ТРАДИЦИОННЫЙ
TOOLSET
gRPC за 30 секунд
Высокоскоростной RPC
HTTP/2 и асинхронность
Строгая типизация
.proto — общий контракт клиента и сервера
Кодогенерация
Клиенты и сервера для любого языка
Бинарная сериализация
Меньше байт, больше скорость
Традиционный toolset
Golang
Целевой язык наших сервисов
protoc
Канонический компилятор от Google
Plugins
protoc-gen-goprotoc-gen-go-grpc
Проблемы proto tools
- Технологически не обвязаны практически с моментом выхода
- Конфигурация в стиле «старой школы» — портянки shell-команд
- Победа старой школы: конфигурация protoc-плагинов
Неконсистентное описание API
- Отсутствие жёсткой структуры описания
- Вариации именования (
snake,camel) - Легко сломать обратную совместимость
- → увеличивает когнитивную нагрузку
Зоопарк rpc-методов
service ServiceAPI {
rpc example_one(google.protobuf.Empty)
returns (google.protobuf.Empty);
rpc ExampleSecond(google.protobuf.Empty)
returns (google.protobuf.Empty);
}Зоопарк message-ей
message RequestOne {
string example_one = 1;
}
message requestSecond {
string ExampleSecond = 1;
}Зоопарк enum-ов
enum Example {
example_one = 0;
ExampleOne = 1;
}Работа с зависимостями
- Отсутствие пакетного менеджера
- Ручной перенос пакетов в проект
- Использование
git submodulesдля управления зависимостями
Реальный пример
## third_party
This directory contains ("vendors") abbreviated copies
of the following repositories:
- google api types:
https://github.com/googleapis/googleapis
- validate:
https://github.com/bufbuild/protoc-gen-validateBreaking change
Нарушение обратной совместимости
- Синтаксические изменения в коде
- Синтаксический вывод
- А в чём случае невозможен!
Было
syntax = "proto3";
...
service ServiceAPI {
rpc Auth(AuthInfo) returns (google.protobuf.Empty);
}
message AuthInfo {
string username = 1;
string password = 2;
}Стало
message AuthInfo {
oneof login {
string username = 1;
string email = 2;
}
string password = 3;
}→ клиенты сломаются на следующем релизе
Версионирование плагинов
обновили плагин на минорную версию — переписался весь сгенерированный код
О чём поговорим?
- 01Традиционный toolset для proto, и его проблемы
- 02buf, что это и зачем оно
- 03Проблемы buf, настоящие и придуманные
- 04Как нам справиться с бедой
BUF —
ЧТО И ЗАЧЕМ
Buf
lint
находит и помогает исправлять ошибки
breaking checker
проверяет обратную совместимость
generate
генерирует код на основе описания протокола
pkg manager
управляет зависимостями
formatter
форматирует код в соответствии с заданными правилами
Линтер
- Инструмент анализа кода
- Помощь в нахождении и исправлении ошибок
- Выявление несоответствий стилей кодирования
- Обнаружение подозрительных конструкций в коде
- Единообразие!
Линтер: что подаём
syntax = "proto3";
package api;
import 'google/protobuf/empty.proto';
service ServiceAPI {
rpc Auth(AuthInfo) returns (google.protobuf.Empty);
}
message AuthInfo {
string username = 1;
string password = 2;
}Линтер: что говорит
auth.proto:3:1: Package name "api" should be suffixed
with a correctly formed version, such as "api.v1".
auth.proto:7:1: Service "ServiceAPI" should have a
non-empty comment for documentation.
auth.proto:8:3: RPC "Auth" should have a non-empty
comment for documentation.
auth.proto:8:12: RPC request type "AuthInfo" should
be named "AuthRequest" or "ServiceAPIAuthRequest".
auth.proto:8:31: RPC response type "Empty" should
be named "AuthResponse" or "ServiceAPIAuthResponse".Чиним именование
syntax = "proto3";
package api.auth.v1;Чиним документацию
// AuthAPI — отвечает за работу с сессиями пользователей.
// Все запросы автоматически фиксируют IP и UserAgent
// для отправки их в топик ......
service AuthAPI {
// Авторизовывает пользователя и возвращает token сессии в metadata.
// При нескольких неуспешных кейсах с одного IP
// временно блокирует возможность авторизоваться.
// В случае отсутствия такого пользователя
// будет код ошибки bad request, а не not found.
rpc Auth(AuthRequest) returns (AuthResponse);
}Чиним AuthRequest
message AuthRequest {
// Для авторизации может быть использован как username,
// так и email. email регистронезависимый,
// а username — зависимый.
oneof login {
string username = 1;
string email = 2;
}
string password = 3;
}
message AuthResponse {}Конфигурация линтера
lint:
use: [
DIRECTORY_SAME_PACKAGE,
PACKAGE_DEFINED,
PACKAGE_DIRECTORY_MATCH,
PACKAGE_SAME_DIRECTORY,
ENUM_PASCAL_CASE,
ENUM_VALUE_UPPER_SNAKE_CASE,
FIELD_LOWER_SNAKE_CASE,
MESSAGE_PASCAL_CASE,
......
]Breaking Change
buf breaking --against '.git#branch=main'→ или поднимаем версию пакета
syntax = "proto3";
package api.auth.v2;Пакетный менеджер
# buf.yml
version: v1
deps:
- buf.build/googleapis/googleapis
- buf.build/bufbuild/protovalidate→ buf mod update
И в коде ничего лишнего
syntax = "proto3";
package api;
import 'google/type/money.proto';
import "validate/validate.proto";
message MyMessage {
google.type.Money money = 1;
string token = 2 [(validate.rules).string = {
min_len: 1,
max_len: 999
}];
}Конфигурации
buf.work.ymlworkspace
buf.ymlправила и зависимости
buf.gen.ymlгенерация
Buf — наш грааль?
- linter
- проверки breaking change
- registry для пакетов
- generate
А где коммит?
# Generated by buf. DO NOT EDIT.
version: v1
deps:
- remote: buf.build
owner: bufbuild
repository: protoc-gen-validate
commit: 53d835564305d73b17e9b122d58e5001b2e93fc6
- remote: buf.build
owner: googleapis
repository: googleapis
commit: 3dd3023f54b3432dac2ef4f80c8a327d639dcc71Удалённая генерация
version: v1
plugins:
- plugin: buf.build/protocolbuffers/go:v1.31.0
out: .
opt:
- "paths=source_relative"
- plugin: buf.build/grpc/go:v1.3.0
out: .
opt:
- "paths=source_relative"Под капотом — proto до конца
message GenerateCodeRequest {
...
buf.alpha.image.v1.Image image = 1;
...
}О чём поговорим?
- 01Традиционный toolset для proto, и его проблемы
- 02buf, что это и зачем оно
- 03Проблемы buf, настоящие и придуманные
- 04Как нам справиться с бедой
ПРОБЛЕМЫ BUF
НАСТОЯЩИЕ И
ПРИДУМАННЫЕ
Блокировка в РФ и РБ
- Нельзя использовать пакетный менеджер
- Нельзя использовать удалённую генерацию
- Нельзя использовать registry
→ buf.build не отвечает на наши запросы
VPN / Proxy
- Легко
- Быстро
- Заработало сразу
- Нельзя запустить в CI без открытия контура
Альтернатива — Prototool
- Имеет такой же функционал:
- Линтер
- Проверка обратной совместимости
- Компиляция
- ...
- Он заморожен и его развитие остановлено :(
Сами авторы
Велосипед?
- Можно написать свою тулзу с 0!
- Возможность адаптировать под требования компании
- Нужно написать свою тулзу с 0!
- Ресурсы на разработку
- Ресурсы на тестирование
- Ещё ресурсы — на внедрение
Исходники buf
buf — open source, поэтому можно посмотреть, как он устроен.
buf/
├── .github/
├── cmd/
├── data/
├── make/
├── private/
├── proto/ # api описан proto-файлами
└── windows/easyp
- Не придётся переезжать на другое cli
- Обратно-совместимый сервис
- Конечно же нет
Как работает обмен
Client → CLI: Request
CLI → ResolveService: Resolve request
ResolveService → CLI: repository address
CLI → RepositoryService: Get repository information
RepositoryService → CLI: repository information
CLI → DownloadService: Download
DownloadService → CLI: files
CLI → Client: PackagesGit Hosting
- Полный и удобный контроль proto-файлов
- Сложно
- Долго
- Нужно завозить к gitlab ещё один git host
Git Proxy
- Легко
- Мало кода
- Не храним данные
- Скорость работы низкая
Git proxy to disk
- Легко
- Мало кода
- Работает быстро
- Сложности с версионированием
- Неудобства с добавлением новых пакетов
buf + easyp?
deps:
- <your_host>/googleapis/googleapis
- <your_host>/bufbuild/protoc-gen-validate→ подменяем remote на свой и работаем как обычно
Как подтянем зависимости?
buf mod updateО чём поговорим?
- 01Традиционный toolset для proto, и его проблемы
- 02buf, что это и зачем оно
- 03Проблемы buf, настоящие и придуманные
- 04Как нам справиться с бедой
КАК НАМ
СПРАВИТЬСЯ
С БЕДОЙ
Пришло время переезжать без buf?
Боль с зависимостями
- блокировка
buf - buf не умеет работать с корпоративными git хостингами
- нет пакетных менеджеров вообще?
Гуглим...
protop!· github.com/protop-io/protop- Есть демо!!
- Умеет в приватные репозитории!!!
- profit?! profit?!
→ но... последний коммит — 4 года назад, 12 issues, заброшен
Делаем своё
- git sdk для Golang (например,
gogit) - Пилим PoC
- Иии?
Проблемы
privateключиgoogle apis- На этом всё? Но есть же... go mod!
go mod
- Работает с системными конфигами
git'a - Есть доступ к репе == может поставить зависимость
- Переваривает «тяжёлые репы»
- Конечно же нет
Смотрим исходники
- os.Exec("git", ...)
- git init --bare
- git fetch --depth=1
- git ls-tree
- git archive --format=zip
- и всё! (ну почти)
easyp package manager
версионирование
buf.work.yaml
вендоринг
Пакетный менеджер
version: v1
deps:
- github.com/googleapis/googleapis
- github.com/bufbuild/protovalidate@v0.3.1
- github.com/grpc-ecosystem/grpc-gateway@v2.19.1→ любой git host, любая версия, любой тэг
Подтягиваем зависимости
.PHONY: vendor_proto
vendor_proto:
EASYPPATH=$(VENDOR_PROTO) \
$(LOCAL_BIN)/easyp mod -c easyp.yamlГенерируем код
.PHONY: gen-proto
gen-proto:
cd ./pkg/api && \
protoc -I ./../../vendor_proto/mod \
--plugin=protoc-gen-go=$(LOCAL_BIN)/protoc-gen-go \
--go_out=. --go_opt=paths=source_relative \
--plugin=protoc-gen-go-grpc=$(LOCAL_BIN)/protoc-gen-go-grpc \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--plugin=protoc-gen-goclay=$(LOCAL_BIN)/protoc-gen-goclay \
own_service/service.protoЗаключение
- Сохранили cli функционал от
buf - Решили проблему пакетного менеджера
- Создали аналог
buf cliдля работы с proto
ЧТО
ДАЛЬШЕ?
Стартовый забег
Догоняем buf
Финальный рывок
easyp
easyp.tech//github.com/easyp-tech/easyp
Эдгар Сипки//Василий Близнецов